2026-08-03-comfyui-anime-generation-design.md 5.2 KB

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<FileUploadInfo> uploadFile(bytes, FileMeta) Client::uploadFile
Result<std::vector<uint8_t>> downloadFile(id) Client::downloadFile
Result<FileInfo> getFileInfo(id) Client::getFileInfo
Result<void> 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.