# ComfyUI anime generation from analysed images **Date:** 2026-08-03 **Status:** Design - approved **Owner:** Ferenc Szontagh ## 1. Goal Extend the 35photo workflow so that every image flagged NSFW is additionally rendered as an anime-style image by the local ComfyUI instance at `http://127.0.0.1:8188`, using the Z-Image Turbo model, and stored with its metadata. ## 2. Constraints established during exploration - ComfyUI 0.28.0, RTX 3060 (12.5 GB total). ComfyUI already holds ~7.2 GB of cached models; ~3.3 GB free. `z_image_turbo_bf16.safetensors` is ~11.5 GB, so ComfyUI must evict and will still partially offload. Generations will be slow. Use `z_image_turbo_bf16` regardless - no Q8/GGUF substitution. - Z-Image Turbo is text-to-image only. `TextEncodeZImageOmni` exists in this install but targets the Omni checkpoint, which is not present, so image-conditioned generation is not available. The prompt produced by the vision step is therefore the source of the generated image. - No base64 image-loader node exists. Not needed here, since generation is text-to-image, but it rules out feeding source pixels without the `/upload/image` endpoint. - Model components present: `z_image_turbo_bf16.safetensors` (UNETLoader), `qwen_3_4b.safetensors` (CLIPLoader), `ae.safetensors` (VAELoader). - The upstream database exposes a file API (`uploadFile`, `downloadFile`, `getFileInfo`, `deleteFile`, `listFiles`) with checksums, dedupe and ref-counting. `lib/storage/storage_client.hpp` exposes none of it. ## 3. Part A - expose database file storage The document API already flows adapter -> script engine -> node. Files follow the same path. `lib/storage/storage_client.{hpp,cpp}`: | Method | Upstream call | | --- | --- | | `Result uploadFile(bytes, FileMeta)` | `Client::uploadFile` | | `Result> downloadFile(id)` | `Client::downloadFile` | | `Result getFileInfo(id)` | `Client::getFileInfo` | | `Result deleteFile(id)` | `Client::deleteFile` | `src/runner/engine/script_engine.cpp` exposes: - `smartbotic.storage.uploadFile(base64, meta)` -> `{success, id, size, checksum, deduplicated}` - `smartbotic.storage.downloadFile(id)` -> `{success, data (base64), mimeType}` - `smartbotic.storage.getFileInfo(id)` -> `{success, ...FileRecord fields}` Base64 is the transport at the JS boundary, matching how `http-request` already carries binary through `file.data`. The C++ side decodes once; documents never carry image bytes. Rationale: storing a ~1.5 MB PNG as base64 inside a document costs ~133% of the image size per record and defeats dedupe. The file store is purpose-built for this and unblocks every future binary node. ## 4. Part B - generic `comfyui-prompt` node `nodes/ai/comfyui-prompt.js`. Task-agnostic, following the `ollama-chat` precedent: nothing anime- or Z-Image-specific lives in the code. **Config:** `baseUrl`, `workflowJson` (ComfyUI API-format graph, with `{{placeholder}}` substitution), `outputNodeId`, `pollIntervalMs`, `timeoutMs`, `retryCount`, `retryDelayMs`, `retryMaxDelayMs`, `skipOnError`. **Behaviour:** substitute placeholders -> `POST /prompt` -> poll `/history/{prompt_id}` until the entry appears -> read the output image descriptor from `outputNodeId` -> fetch bytes via `/view` -> return base64. **Output:** `{success, error, imageBase64, filename, mimeType, width, height, promptId, attempts}`. **Error handling:** mirrors `ollama-chat`. Exponential backoff between attempts; `skipOnError` returns `success: false` rather than aborting the loop. ## 5. Part C - workflow wiring Only NSFW-flagged images proceed, so generation is rare and GPU pressure stays bounded. ``` Store If NSFW -> ComfyUI Generate -> Store Anime File ``` - **ComfyUI Generate:** `comfyui-prompt` configured with a Z-Image Turbo text-to-image graph. Positive prompt is `anime style, ` prefixed to the `prompt` field the safety gate already produces (which has the adult descriptor enforced). Sampler settings follow the model's guidance: `cfg 1.0`, low step count for Turbo. Both are exposed in the graph config for tuning. - **Store Anime File:** a `code` node that calls `smartbotic.storage.uploadFile` with `file_type: "generated"`, then inserts a document into the `anime_images` collection holding `fileId`, `sourceUrl`, `sourceRecordId`, `prompt`, `model` and sampler parameters. `anime_images` must be granted `read-write` in the workflow's `settings.storagePermissions`, since storage access is deny-by-default. ## 6. Testing The NSFW branch has never fired on this feed, so the path cannot be validated by a normal run. Testing proceeds in this order: 1. Part A verified directly: upload a known file, download it, compare checksums, confirm `deduplicated` on a second identical upload. 2. Part B verified standalone: a scratch workflow with a fixed prompt, confirming an image comes back and lands in `anime_images`. 3. Part C verified by temporarily relaxing the store condition, exactly as the NSFW store path was verified, then restoring it. ## 7. Out of scope - Image-conditioned (img2img) generation - not supported by the available checkpoint. - Substituting a smaller Z-Image variant to fit VRAM. - Backfilling anime images for previously stored records.