{ "_id": "wf_58ed4490-dad0-4ea3-9c89-7122339dadb9", "_version": 33, "active": true, "connections": [ { "sourceNodeId": "node_input", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_ollama_vision" }, { "sourceNodeId": "node_ollama_vision", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_safety_gate" }, { "sourceNodeId": "node_safety_gate", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_build_doc" }, { "sourceNodeId": "node_if_gen", "sourceOutput": "true", "targetInput": "data", "targetNodeId": "node_call_gen" }, { "sourceNodeId": "node_if_gen", "sourceOutput": "false", "targetInput": "data", "targetNodeId": "node_output" }, { "sourceNodeId": "node_call_gen", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_gen_ok" }, { "sourceNodeId": "node_gen_ok", "sourceOutput": "true", "targetInput": "data", "targetNodeId": "node_blog_text" }, { "sourceNodeId": "node_gen_ok", "sourceOutput": "false", "targetInput": "data", "targetNodeId": "node_output" }, { "sourceNodeId": "node_blog_text", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_blog_image" }, { "sourceNodeId": "node_blog_image", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_blog_rate" }, { "sourceNodeId": "node_blog_rate", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_blog_post" }, { "sourceNodeId": "node_blog_post", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_output" }, { "sourceNodeId": "node_safety_gate", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_analysis_present" }, { "sourceNodeId": "node_analysis_present", "sourceOutput": "false", "targetInput": "data", "targetNodeId": "node_no_analysis" }, { "sourceNodeId": "node_analysis_present", "sourceOutput": "true", "targetInput": "data", "targetNodeId": "node_store_eligible" }, { "sourceNodeId": "node_store_eligible", "sourceOutput": "false", "targetInput": "data", "targetNodeId": "node_not_eligible_defaults" }, { "sourceNodeId": "node_store_eligible", "sourceOutput": "true", "targetInput": "data", "targetNodeId": "node_measure_source" }, { "sourceNodeId": "node_measure_source", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_build_gen_params" }, { "sourceNodeId": "node_not_eligible_defaults", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_if_gen" }, { "sourceNodeId": "node_build_gen_params", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_if_gen" } ], "consecutiveFailures": 0, "deactivatedReason": "", "description": "The ten-node chain that turns one image into a redraw and a published post: Vision Analyse through Publish To Blog. Called by the RSS pipeline (35photo2anime - sdcpp) per item and by the upload form, so both run exactly the same steps. Input: file (binary).", "groupId": "wfg_07ea58b4-3d38-4633-b6d8-3186355c5f51", "name": "[SWF] anime: process one image", "nodes": [ { "config": { "allowExtra": true, "fields": [ { "description": "The image, as the binary object a download or an upload produces", "name": "file", "required": true } ] }, "disabled": false, "id": "node_input", "name": "Workflow Input", "position": { "x": 120, "y": 0 }, "type": "workflow-input" }, { "config": { "authType": "credential", "baseUrl": "https://ollama.com", "credentialId": "cred_b01319ae-e31c-4545-86ee-ff655a7cc1e4", "imageField": "", "imageMode": "auto", "model": "minimax-m3:cloud", "passthroughImage": true, "responseFormat": "json", "retryCount": 3, "retryDelayMs": 3000, "retryMaxDelayMs": 30000, "skipOnError": true, "systemPrompt": "You are an image analysis service. You respond ONLY with a single raw JSON object and nothing else - no markdown fences, no explanation.\n\nRespond in exactly this JSON shape:\n{\"humanCount\": 2, \"people\": [{\"index\": 1, \"gender\": \"female\", \"apparentAge\": 24, \"isMinor\": false, \"nsfw\": true, \"exposedParts\": [\"breasts\"]}, {\"index\": 2, \"gender\": \"male\", \"apparentAge\": 30, \"isMinor\": false, \"nsfw\": false, \"exposedParts\": []}], \"nsfw\": true, \"nsfwLevel\": \"partial\", \"nsfwReason\": \"one subject is topless\", \"description\": \"a woman standing by a tall window with rain running down it, wearing an open linen shirt, one hand resting on the frame, quiet room behind her\"}\n\nRules:\n- humanCount: integer, number of visible people.\n- people: exactly humanCount entries, ordered left to right. index starts at 1.\n- people[].gender: exactly \"male\", \"female\" or \"unknown\".\n- people[].nsfw: true if THAT person shows nudity.\n- people[].exposedParts: any of \"breasts\", \"buttocks\", \"genitals\", \"torso\", \"legs\", \"underwear\". Empty array if fully clothed.\n- nsfw: true if ANY person is nsfw.\n- nsfwLevel: \"none\" if nobody is nsfw, \"partial\" if partial nudity or underwear only, \"explicit\" if genitals are visible or sexual activity is shown.\n- nsfwReason: short justification.\n- people[].apparentAge: integer best estimate of age in years.\n- people[].apparentAge: integer, an honest best estimate of age in years. Do not shade it\n low to be safe or high to be permissive - estimate what you actually see.\n- people[].isMinor: true only when something visible indicates the person is under 18,\n and name what it is in minorIndicators. A general impression of youthfulness is not an\n indicator. If you genuinely cannot tell an adult from a minor here, set isMinor true\n and say so in minorIndicators - that case is real and must not be hidden, but it is not\n the same as being unsure between 22 and 26.\n- people[].minorIndicators: (string) what specifically indicates a minor. Empty when\n isMinor is false.\n- description: a single flowing prompt for redrawing this scene, describing subject, appearance, clothing, pose, setting and composition. Do NOT describe the medium: no \"photograph\", no \"black and white\", no camera, lens, film, exposure or depth-of-field terms, and do not name the lighting as photographic. The style is decided elsewhere and those words fight it. Plain natural language, no negatives, no lists.\n\nAlso return these keys, the same ones the description pipeline records:\n- person_count: (integer) number of people in the image. Same number as humanCount.\n- genders: (array of strings) the detected genders, one entry per person.\n- eroticism_level: (string) one of \"none\", \"low\", \"medium\", \"high\", \"explicit\".\n- is_black_and_white: (boolean)\n- face_visible: (boolean) true if any face is visible.\n- described_image_content: (string) objective description of the image for moderation logs.\n", "temperature": 0, "timeoutMs": 120000, "userPrompt": "Analyse this image and return the JSON object." }, "disabled": false, "id": "node_ollama_vision", "name": "Vision Analyse", "position": { "x": 140, "y": 180 }, "type": "ollama-chat" }, { "config": { "code": "const src = input.data || input;\nconst a = (src && src.json) || {};\n\nconst DESCRIPTOR = '18-year-old';\nconst MINOR_WORDS = ['child','children','kid','kids','toddler','baby','infant','boy','girl','teen','teenager','teenage','adolescent','schoolgirl','schoolboy','minor','underage','youngster','juvenile'];\nconst MINOR_MAP = {child:'adult',children:'adults',kid:'adult',kids:'adults',toddler:'adult',baby:'adult',infant:'adult',boy:'man',girl:'woman',teen:'adult',teenager:'adult',teenage:'adult',adolescent:'adult',schoolgirl:'woman',schoolboy:'man',minor:'adult',underage:'adult',youngster:'adult',juvenile:'adult'};\nconst PERSON_NOUNS = ['woman','man','female','male','lady','gentleman','model','person','adult','figure','subject','women','men','people','persons','figures'];\n\nfunction articleFor(word) {\n return /^(8|11|18|[aeiou])/i.test(String(word)) ? 'an' : 'a';\n}\n\nfunction tidy(text) {\n let out = String(text);\n out = out.replace(new RegExp('\\\\badult\\\\s+(' + DESCRIPTOR + ')\\\\b', 'gi'), '$1');\n out = out.replace(new RegExp('\\\\b(' + DESCRIPTOR + ')\\\\s+adult\\\\b', 'gi'), '$1');\n out = out.replace(/\\s{2,}/g, ' ');\n out = out.replace(/\\b(a|an|A|An)\\s+(\\S+)/g, function (m, art, next) {\n const correct = articleFor(next);\n return (art.charAt(0) === 'A' ? correct.charAt(0).toUpperCase() + correct.slice(1) : correct) + ' ' + next;\n });\n return out;\n}\n\nfunction enforceAdult(text, humanCount) {\n if (!text) { return ''; }\n let out = String(text);\n out = out.replace(/\\b(\\d{1,2})[\\s-]*(?:year|yr)s?[\\s-]*old\\b/gi, DESCRIPTOR);\n for (let i = 0; i < MINOR_WORDS.length; i++) {\n out = out.replace(new RegExp('\\\\b' + MINOR_WORDS[i] + '\\\\b', 'gi'), MINOR_MAP[MINOR_WORDS[i]] || 'adult');\n }\n if (humanCount < 1 || out.toLowerCase().indexOf(DESCRIPTOR.toLowerCase()) !== -1) {\n return tidy(out);\n }\n for (let i = 0; i < PERSON_NOUNS.length; i++) {\n const withArticle = new RegExp('\\\\b(an?)\\\\s+(' + PERSON_NOUNS[i] + ')\\\\b', 'i');\n if (withArticle.test(out)) {\n return tidy(out.replace(withArticle, articleFor(DESCRIPTOR) + ' ' + DESCRIPTOR + ' $2'));\n }\n const bare = new RegExp('\\\\b(' + PERSON_NOUNS[i] + ')\\\\b', 'i');\n if (bare.test(out)) {\n return tidy(out.replace(bare, DESCRIPTOR + ' $1'));\n }\n }\n return tidy(out);\n}\n\nconst people = [];\nlet containsMinor = false;\nlet minorWhy = '';\nconst rawPeople = a.people && typeof a.people.length === 'number' ? a.people : [];\nfor (let i = 0; i < rawPeople.length; i++) {\n const p = rawPeople[i] || {};\n let age = Number(p.apparentAge);\n if (!(age > 0)) { age = 0; }\n let isMinor = p.isMinor === true;\n if (age > 0 && age < 18) { isMinor = true; }\n if (isMinor) {\n containsMinor = true;\n // Carry the model's own words through, so a skipped image can be told\n // apart from a shrug without reading raw execution JSON.\n const why = String(p.minorIndicators || '').trim();\n if (why) { minorWhy = minorWhy ? (minorWhy + '; ' + why) : why; }\n }\n const parts = [];\n if (p.exposedParts && typeof p.exposedParts.length === 'number') {\n for (let j = 0; j < p.exposedParts.length; j++) { parts.push(String(p.exposedParts[j])); }\n }\n let g = String(p.gender || '').toLowerCase();\n if (g !== 'male' && g !== 'female') { g = 'unknown'; }\n people.push({ index: Number(p.index) || i + 1, gender: g, apparentAge: age, isMinor: isMinor, nsfw: p.nsfw === true, exposedParts: parts });\n}\n\nlet humanCount = Number(a.humanCount);\nif (!(humanCount >= 0)) { humanCount = people.length; }\n\nconst analysed = src && src.success === true;\nconst blocked = containsMinor;\nconst nsfw = a.nsfw === true;\nconst description = blocked ? '' : String(a.description || '');\nconst prompt = blocked ? '' : enforceAdult(description, humanCount);\n\nreturn {\n analysed: analysed,\n humanCount: humanCount,\n people: people,\n containsMinor: containsMinor,\n blocked: blocked,\n blockReason: blocked ? ('suspected minor present: ' + (minorWhy || 'no indicator given by the model')) : '',\n nsfw: nsfw,\n nsfwLevel: String(a.nsfwLevel || (nsfw ? 'partial' : 'none')),\n nsfwReason: String(a.nsfwReason || ''),\n storeEligible: analysed && nsfw && !blocked,\n description: description,\n prompt: prompt,\n imageBase64: blocked ? '' : String(src.imageBase64 || ''),\n mimeType: String(src.mimeType || ''),\n sourceUrl: String(src.sourceUrl || ''),\n\n // The same fields the description pipeline records, so an image analysed\n // here and one analysed there can be compared without translating between\n // two shapes.\n person_count: Number(a.person_count) >= 0 ? Number(a.person_count) : humanCount,\n genders: a.genders && typeof a.genders.length === 'number'\n ? a.genders.map(function (g) { return String(g); })\n : people.map(function (p) { return p.gender; }),\n eroticism_level: String(a.eroticism_level || (nsfw ? 'medium' : 'none')),\n is_black_and_white: a.is_black_and_white === true,\n face_visible: a.face_visible === true,\n described_image_content: String(a.described_image_content || a.description || ''),\n\n // The reply exactly as it arrived. The named fields above are what the rest\n // of the flow reads; this is what stops the next prompt change from\n // silently dropping whatever it adds.\n analysis: a\n};\n", "timeout": 30 }, "disabled": false, "id": "node_safety_gate", "name": "Safety Gate + Prompt", "position": { "x": 120, "y": 380 }, "type": "code" }, { "config": { "code": "function findDedupe(node, depth) {\n if (!node || typeof node !== 'object' || depth > 6) { return null; }\n if (Object.prototype.hasOwnProperty.call(node, 'fileId') &&\n Object.prototype.hasOwnProperty.call(node, 'checksum')) { return node; }\n const keys = Object.keys(node);\n for (let i = 0; i < keys.length; i++) {\n const found = findDedupe(node[keys[i]], depth + 1);\n if (found) { return found; }\n }\n return null;\n}\n\nfunction findAnalysis(node, depth) {\n if (!node || typeof node !== 'object' || depth > 6) { return null; }\n if (Object.prototype.hasOwnProperty.call(node, 'storeEligible')) { return node; }\n const keys = Object.keys(node);\n for (let i = 0; i < keys.length; i++) {\n const found = findAnalysis(node[keys[i]], depth + 1);\n if (found) { return found; }\n }\n return null;\n}\n\nconst doc = findAnalysis(input, 0);\nif (!doc) { throw new Error('No analysis payload found in input'); }\n\nif (!doc.storeEligible) {\n return { stored: false, reason: doc.blocked ? doc.blockReason : 'not nsfw' };\n}\n\n// The image already lives in the file store, put there once by the dedupe step\n// and deduplicated by checksum, so the record references it rather than carrying\n// a second copy of the same bytes.\nconst source = findDedupe(input, 0);\n\nconst record = {\n sourceUrl: doc.sourceUrl,\n mimeType: doc.mimeType,\n fileId: source ? source.fileId : '',\n checksum: source ? source.checksum : '',\n humanCount: doc.humanCount,\n people: doc.people,\n nsfw: doc.nsfw,\n nsfwLevel: doc.nsfwLevel,\n nsfwReason: doc.nsfwReason,\n description: doc.description,\n prompt: doc.prompt,\n\n // Everything the description pipeline stores, under the same names.\n person_count: doc.person_count,\n genders: doc.genders,\n eroticism_level: doc.eroticism_level,\n is_black_and_white: doc.is_black_and_white,\n face_visible: doc.face_visible,\n described_image_content: doc.described_image_content,\n analysis: doc.analysis,\n\n detectedAt: Date.now()\n};\n\nconst res = smartbotic.storage.insert('nsfw_images', record, null, 0);\nif (!res.success) {\n throw new Error('Failed to store NSFW record: ' + res.error);\n}\nreturn { stored: true, id: res.id, sourceUrl: doc.sourceUrl };\n", "timeout": 30 }, "disabled": false, "id": "node_build_doc", "name": "Store If NSFW", "position": { "x": 0, "y": 560 }, "type": "code" }, { "config": { "combineWith": "and", "conditions": [ { "field": "data.generate", "operator": "is_true", "value": "" } ] }, "disabled": false, "id": "node_if_gen", "name": "Should Generate", "position": { "x": 440, "y": 1300 }, "type": "if-condition" }, { "config": { "fields": [ { "name": "prompt", "value": "{{$node[\"Prepare Generation\"].result.prompt}}" }, { "name": "width", "value": "{{$node[\"Prepare Generation\"].result.width}}" }, { "name": "height", "value": "{{$node[\"Prepare Generation\"].result.height}}" }, { "name": "loraTag", "value": "" }, { "name": "title", "value": "35photo2anime" }, { "name": "batchCount", "value": "1" }, { "name": "keepForHours", "value": "0" }, { "name": "storeInDatabase", "value": "false" } ], "inputSource": "fields", "workflowId": "wf_f7c8244a-2f8a-4a66-a3a4-62b8c63381dc" }, "disabled": false, "id": "node_call_gen", "name": "Generate Image", "position": { "x": 540, "y": 1480 }, "type": "call-workflow" }, { "config": { "authType": "none", "contentType": "", "downloadCollection": "downloads", "downloadTtlHours": 24, "failOnErrorStatus": true, "followRedirects": true, "method": "GET", "responseMode": "binary", "retries": 2, "retryDelayMs": 2000, "retryMaxDelayMs": 30000, "storeDownload": false, "timeout": 60000, "url": "{{$node['Generate Image'].result.urls[0]}}" }, "disabled": false, "id": "node_blog_image", "name": "Fetch Generated Image", "position": { "x": 620, "y": 2040 }, "type": "http-request" }, { "config": { "authType": "credential", "baseUrl": "https://ollama.com", "credentialId": "cred_b01319ae-e31c-4545-86ee-ff655a7cc1e4", "imageField": "", "imageMode": "none", "model": "glm-5.2:cloud", "passthroughImage": false, "responseFormat": "json", "retryCount": 2, "retryDelayMs": 3000, "retryMaxDelayMs": 30000, "skipOnError": false, "systemPrompt": "You write short blog posts about an illustration, in Hungarian and English.\nYou are given the scene it shows. Reply with ONE raw JSON object and nothing else - no markdown fences, no commentary.\n\nShape:\n{\"title_hu\":\"...\",\"title_en\":\"...\",\"content_hu\":\"...\",\"content_en\":\"...\",\"tags\":[{\"slug\":\"...\",\"name_hu\":\"...\",\"name_en\":\"...\"}]}\n\nRules:\n- The Hungarian is the point. Write it as Hungarian, not as a word-for-word rendering of the English - natural word order, natural phrasing.\n- title: at most 60 characters, no full stop, no quotation marks.\n- content: two or three sentences of markdown about what the picture shows and its mood. Do not mention prompts, models, generation or that it was made by a machine.\n- tags: two to four, drawn from what is actually in the picture (subject, setting, mood). slug is lowercase letters, digits and hyphens only, ASCII, no accents - it is a URL. name_hu and name_en are how the tag is shown, so those keep their accents.\n- Never leave a field empty.", "temperature": 0.4, "timeoutMs": 120000, "userPrompt": "The illustration shows: {{$node['Vision Analyse'].json.description}}" }, "disabled": false, "id": "node_blog_text", "name": "Write The Post", "position": { "x": 640, "y": 1860 }, "type": "ollama-chat" }, { "config": { "authType": "credential", "baseUrl": "https://ollama.com", "credentialId": "cred_b01319ae-e31c-4545-86ee-ff655a7cc1e4", "imageField": "", "imageMode": "auto", "model": "minimax-m3:cloud", "passthroughImage": true, "responseFormat": "json", "retryCount": 2, "retryDelayMs": 3000, "retryMaxDelayMs": 30000, "skipOnError": false, "systemPrompt": "You report what is visible in one illustration, so that another step can decide whether it needs an 18+ warning. Reply with ONE raw JSON object and nothing else - no markdown fences, no commentary.\n\n{\"nudity\": \"none\", \"visible\": [], \"sexualActivity\": false, \"underwear\": false, \"reason\": \"woman in a blazer, cleavage visible, nothing bare\"}\n\nReport what is drawn, not what it suggests. You are not deciding the warning, so do not lean towards caution - a wrong \"yes\" is as much an error here as a wrong \"no\".\n\n- nudity: \"full\" when a person is naked or all but naked; \"partial\" when one of the parts listed under `visible` is bare; \"none\" otherwise, however tight, short, thin or low-cut the clothing is.\n- visible: any of \"nipples\", \"genitals\", \"buttocks\", \"pubic_area\" - bare skin only, never shape or outline through fabric. Empty array when none are bare.\n- sexualActivity: true only when a sexual act is depicted.\n- underwear: true when underwear or lingerie is the visible outer layer. Recorded, but on its own it is not nudity.\n- reason: a short phrase naming what you actually saw.\n\nNone of the following is nudity. Each one gives nudity \"none\" and an empty `visible`: cleavage, a low or open neckline, a bare midriff or navel, bare arms, bare legs, bare thighs, bare shoulders, a bare back, swimwear, a leotard or bodysuit, tight or thin-looking clothing, and any pose, expression or camera angle however suggestive it seems.\n\nA stylised or cartoon drawing is reported exactly like any other image.", "temperature": 0, "timeoutMs": 120000, "userPrompt": "Report what is visible in this illustration and return the JSON object." }, "disabled": false, "id": "node_blog_rate", "name": "Rate The Picture", "position": { "x": 640, "y": 2240 }, "type": "ollama-chat" }, { "config": { "fields": [ { "name": "file", "value": "{{$node['Fetch Generated Image'].file}}" }, { "name": "imageExtension", "value": "png" }, { "name": "title_hu", "value": "{{$node['Write The Post'].json.title_hu}}" }, { "name": "title_en", "value": "{{$node['Write The Post'].json.title_en}}" }, { "name": "content_hu", "value": "{{$node['Write The Post'].json.content_hu}}" }, { "name": "content_en", "value": "{{$node['Write The Post'].json.content_en}}" }, { "name": "tags", "value": "{{$node['Write The Post'].json.tags || []}}" }, { "name": "slug", "value": "{{(($node['Write The Post'].json.title_en || 'anime').toString().toLowerCase().normalize('NFD').replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,60)) + '-' + ($node['Generate Image'].result.jobId || Date.now()).toString().slice(0,8)}}" }, { "name": "status", "value": "published" }, { "name": "is_adult", "value": "{{(function(r){if(!r) return 1;if(r.sexualActivity===true) return 1;var bare=['nipples','genitals','buttocks','pubic_area'];var v=(r.visible||[]).map(function(x){return (x||'').toString().toLowerCase();});for(var i=0;i=0) return 1;if((r.nudity||'').toString().toLowerCase()==='full') return 1;if((r.nsfwLevel||'').toString().toLowerCase()==='explicit') return 1;return 0;})($node['Rate The Picture'].json)}}" } ], "inputSource": "fields", "workflowId": "wf_234820e9-807a-44a9-b8cf-d8830ea8257c" }, "disabled": false, "id": "node_blog_post", "name": "Publish To Blog", "position": { "x": 640, "y": 2420 }, "type": "call-workflow" }, { "config": { "combineWith": "and", "conditions": [ { "field": "data.result.succeeded", "operator": "is_true", "value": "" } ] }, "disabled": false, "id": "node_gen_ok", "name": "Did The Image Get Made", "position": { "x": 520, "y": 1680 }, "type": "if-condition" }, { "config": { "fields": [ { "name": "succeeded", "value": "{{ !$node['Generate Image'] || ($node['Generate Image'].result.succeeded === true && !!($node['Publish To Blog'] && $node['Publish To Blog'].result && $node['Publish To Blog'].result.ok)) }}" }, { "name": "skipped", "value": "{{ !$node['Generate Image'] }}" }, { "name": "error", "value": "{{ $node['Generate Image'] ? $node['Generate Image'].result.error : '' }}" }, { "name": "url", "value": "{{ ($node['Publish To Blog'] && $node['Publish To Blog'].result) ? $node['Publish To Blog'].result.url : '' }}" }, { "name": "postId", "value": "{{ ($node['Publish To Blog'] && $node['Publish To Blog'].result) ? $node['Publish To Blog'].result.postId : '' }}" } ], "source": "fields" }, "disabled": false, "id": "node_output", "name": "Workflow Output", "position": { "x": 440, "y": 2600 }, "type": "workflow-output" }, { "config": { "combineWith": "and", "conditions": [ { "field": "data.result.storeEligible", "operator": "exists", "value": "" } ] }, "disabled": false, "id": "node_analysis_present", "name": "Analysis Present", "position": { "x": 300, "y": 560 }, "type": "if-condition" }, { "config": { "message": "No analysis payload found in input", "mode": "error" }, "disabled": false, "id": "node_no_analysis", "name": "No Analysis Payload", "position": { "x": 120, "y": 740 }, "type": "stop-and-error" }, { "config": { "combineWith": "and", "conditions": [ { "field": "data.result.storeEligible", "operator": "is_true", "value": "" } ] }, "disabled": false, "id": "node_store_eligible", "name": "Store Eligible", "position": { "x": 460, "y": 740 }, "type": "if-condition" }, { "config": { "dropFields": [], "fields": [ { "name": "generate", "value": "{{false}}" }, { "name": "reason", "value": "{{ $node['Safety Gate + Prompt'].result.blocked ? $node['Safety Gate + Prompt'].result.blockReason : 'not nsfw' }}" }, { "name": "prompt", "value": "" }, { "name": "seed", "value": "{{0}}" }, { "name": "width", "value": "{{1024}}" }, { "name": "height", "value": "{{1024}}" }, { "name": "sourceUrl", "value": "{{ $node['Safety Gate + Prompt'].result.sourceUrl || '' }}" } ], "mode": "only-set" }, "disabled": false, "id": "node_not_eligible_defaults", "name": "Not Eligible Defaults", "position": { "x": 260, "y": 1120 }, "type": "set-fields" }, { "config": { "backgroundColor": "transparent", "base64Data": "{{ $node['Safety Gate + Prompt'].result.imageBase64 }}", "cropX": 0, "cropY": 0, "filterBlur": false, "filterBlurRadius": 5, "filterBrightness": false, "filterBrightnessValue": 0, "filterContrast": false, "filterContrastValue": 0, "filterGrayscale": false, "filterSepia": false, "filterSepiaIntensity": 80, "filterSharpen": false, "filterSharpenAmount": 1, "includeExif": false, "inputSource": "base64", "maintainAspectRatio": true, "operation": "info", "outputFormat": "auto", "outputQuality": 85, "resizeMode": "dimensions", "resizePercentage": 100, "rotateAngle": 90, "rotatePreset": "custom", "timeout": 15000, "watermarkColor": "white", "watermarkFont": "Arial", "watermarkFontSize": 48, "watermarkImageSource": "filePath", "watermarkMargin": 10, "watermarkOpacity": 50, "watermarkPosition": "center", "watermarkScale": 20, "watermarkType": "text" }, "disabled": false, "id": "node_measure_source", "name": "Measure Source Image", "position": { "x": 600, "y": 940 }, "type": "image" }, { "config": { "dropFields": [], "fields": [ { "name": "generate", "value": "{{true}}" }, { "name": "reason", "value": "" }, { "name": "prompt", "value": "{{ 'high-quality anime illustration, cel shaded, clean line art, vibrant colours, 2D, clean lineart, ' + ($node['Safety Gate + Prompt'].result.prompt || '') }}" }, { "name": "seed", "value": "{{42}}" }, { "name": "width", "value": "{{ $node['Measure Source Image'].width > $node['Measure Source Image'].height ? 1152 : ($node['Measure Source Image'].width < $node['Measure Source Image'].height ? 896 : 1024) }}" }, { "name": "height", "value": "{{ $node['Measure Source Image'].width > $node['Measure Source Image'].height ? 896 : ($node['Measure Source Image'].width < $node['Measure Source Image'].height ? 1152 : 1024) }}" }, { "name": "sourceWidth", "value": "{{ $node['Measure Source Image'].width }}" }, { "name": "sourceHeight", "value": "{{ $node['Measure Source Image'].height }}" }, { "name": "sourceUrl", "value": "{{ $node['Safety Gate + Prompt'].result.sourceUrl || '' }}" } ], "mode": "only-set" }, "disabled": false, "id": "node_build_gen_params", "name": "Build Generation Params", "position": { "x": 600, "y": 1120 }, "type": "set-fields" } ], "ownerId": "usr_dc798bf2-4e5b-4f86-b003-0b9184b2a528", "projectId": "prj_97b218e7-0bc1-41db-aa75-7b9263c876c0", "publishedAt": 1786510410741, "publishedBy": "admin", "publishedVersion": 32, "settings": { "continueOnError": false, "errorWorkflowId": "wf_766fcbcb-6345-4e9c-b7f8-f219505ee45c", "storagePermissions": { "collections": { "files": "read-write", "nsfw_images": "read-write" }, "defaultAccess": "none" } }, "updatedAt": 1786329512341 }