storytellurx/scripts/image-generation
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-04 18:19:03 +02:00
..
generate.test.ts chore: update dependencies and resolve fleet checks 2026-09-27 19:39:04 +02:00
generate.ts chore: update dependencies and resolve fleet checks 2026-09-27 19:39:04 +02:00
list-world-assets.ts ok 2026-09-09 16:15:17 +02:00
nanogpt-quick.py feat: refine StoryTellurX experience and persona loading states 2026-09-25 04:39:52 +02:00
nanogpt-quick.ts chore: add demo tooling and graphics dependencies 2026-10-04 18:19:03 +02:00
presets.ts chore: update dependencies and resolve fleet checks 2026-09-27 19:39:04 +02:00
README.md fix: design 2026-09-09 13:18:14 +02:00
regenerate-world.ts chore: update dependencies and resolve fleet checks 2026-09-27 19:39:04 +02:00
tsconfig.json fix: axd 2026-09-08 21:05:57 +02:00

Generate local images

From the project root:

vp run generate:image --prompt "Fluorescent punk comic collage, acid yellow and hot pink, thick black ink" --name "cybelle"

Images are written directly to public/assets/generations/:

# Default
public/assets/generations/<name>.<preset>.webp
# With --with-hash
public/assets/generations/<name>.<preset>.<prompt-hash>.webp
vp run generate:image --prompt "A ceramic teapot" --name "teapot"
vp run generate:image --prompt "A ceramic teapot" --name "teapot" --with-hash

The preset is its canonical lowercase ID (default: dreamshaper-8-lcm). The optional prompt hash is the first eight hexadecimal characters of the prompt's SHA-256 hash. Images are served at /assets/generations/<filename>.

Other generated files live in .cache/generations/<hash>/, where <hash> is the full SHA-256 hash of the output filename. The <filename>.json sidecar records the settings, settings hash, prompt, prompt hash, reference path, date, and generation time. Engine logs are saved as <filename>.server.log. Temporary files and the exclusive reservation are removed when the command finishes or is interrupted.

Existing images are rejected unless --force is provided. With --force, successful generation replaces the image and updates its cached metadata and log; a generation failure preserves the existing image. Concurrent writes remain blocked by the output reservation. Alternatively, choose another --name, or use --with-hash with a different prompt. Changing only the seed or settings does not change the output filename. Earlier images under assets/generations/ are retained in place.

Prompt prefix

Set defaultPromptPrefix at the top of presets.ts to share a prefix across every preset. It starts empty. The prefix is prepended to --prompt with a space. Override it per run with --prompt-prefix, or pass --prompt-prefix "" to disable it:

vp run generate:image --name teapot --prompt "A ceramic teapot" --prompt-prefix "Watercolor illustration,"

--with-hash hashes the complete prompt including the prefix. Cached metadata records the original input, effective prompt, and prefix.

Preset registry

vp run generate:image --preset cyberrealistic --prompt "A ceramic teapot, soft window light" --name "teapot"
vp run generate:image --preset dreamshaper-8-lcm --prompt "Fluorescent punk comic collage" --name "draft"
vp run generate:image --list-presets

Omitting --preset selects dreamshaper-8-lcm. Names are case-insensitive (CyberRealistic works).

Preset Checkpoint Steps CFG Sampler / scheduler Cache
cyberrealistic CyberRealistic v5 FP16, SD 1.5 20 7 DPM++ 2M / Karras UCache
cyberrealistic-no-cache Same checkpoint 20 7 DPM++ 2M / Karras Off
realistic-vision-hyper Realistic Vision v5.1 Hyper FP16 4 1.8 DPM++ 2M / Discrete Off
dreamshaper-8-lcm (default) DreamShaper 8 LCM, SD 1.5 8 2 LCM / LCM Off
juggernaut-xi-lightning Juggernaut XI Lightning, SDXL 6 1.5 DPM++ 2M SDE / Karras Off

Every preset owns a negativePrompt setting. Realistic Vision Hyper defaults to ugly, deformed, bad anatomy, blurry, cartoon; the other current presets default to an empty negative prompt.

vp run generate:image --preset realistic-vision-hyper --prompt "A ceramic teapot, soft window light" --name "teapot"
vp run generate:image --preset realistic-vision-hyper --prompt "A ceramic teapot" --name "teapot" --negative-prompt "blurry, low resolution"
vp run generate:image --preset realistic-vision-hyper --prompt "A ceramic teapot" --name "teapot" --negative-prompt ""

The effective negative prompt (preset default or CLI override) is included in the cached JSON sidecar and its settings hash. Overriding it changes the recorded settings hash; choose a different name to retain both images. The filename suffix remains the positive-prompt hash. Realistic Vision uses the CLI-equivalent default discrete scheduler and no UCache, matching the supplied four-step recipe. DreamShaper 8 LCM is the default preset (8 steps, 512×512).

SD 1.5 presets use the existing TAESD decoder. Juggernaut uses its embedded SDXL VAE and defaults to 1024×1024. CyberRealistic's UCache explicitly records threshold 1.0, start 0.15, end 0.95, decay 1.0, relative 1 and reset 1.

Add model/config combinations in presets.ts; change defaultPreset there to change the default. CLI --steps overrides the selected preset's step count. Checkpoints and decoder paths are relative to --tools. This registry deliberately pairs architectures and settings rather than guessing settings from arbitrary filenames.

The installed checkpoints were checked on 2026-09-08. SD-Turbo has been removed from the registry because its checkpoint is no longer installed. Old generation history is preserved. klein-4bit/ is an MFLUX model bundle, and loras/lcm-sd15.safetensors is an adapter, so neither is exposed as a standalone checkpoint in this sd-server runner.

DreamShaper uses an eight-step LCM starting configuration with CFG 2; its author's example uses 15 steps, which you can select with --steps 15. Juggernaut follows its model card for resolution, low CFG and step count. The installed engine does not expose DPM++ SDE, so its preset uses the supported DPM++ 2M SDE variant with Karras. These new presets have not been performance-benchmarked on this Mac; SDXL is substantially larger than the SD 1.5 checkpoints.

Reference images

vp run generate:image --preset dreamshaper-8-lcm \
  --prompt "Fluorescent punk comic collage, thick black ink" \
  --name "cybelle-remix" \
  --reference assets/backgrounds/cybelle-punk-v1/reference-sized.png \
  --seed 73 --strength 0.5 --width 512 --height 896

This uses DreamShaper and does not reproduce the historical SD-Turbo remix. Defaults are 512×512 for SD 1.5 or 1024×1024 for Juggernaut, a random seed on each run and reference strength 0.5. --width and --height override preset dimensions. Use --seed 73 to fix the seed for reproducible generation. The chosen seed is recorded in the cached metadata.

Server lifecycle

No server command is necessary. The runner starts a private server on an available loopback port, generates one image, converts it to WebP, and shuts down the server on success, failure, Ctrl+C, SIGTERM or SIGHUP. It escalates to SIGKILL if its child does not stop within three seconds. As with any process, uncatchable termination of the runner itself (SIGKILL or power loss) cannot execute cleanup.

To reuse a server you already keep running:

vp run generate:image --prompt "..." --name "cybelle" --server http://127.0.0.1:18792

The runner verifies the exposed model name and leaves a borrowed server running. That API cannot verify the server's decoder, weights or backend; external mode is identified separately in the saved settings. Use the default owned-server mode for the recorded local configuration and predictable cleanup.

Local prerequisites

The script is TypeScript, executed directly by the project's Node 26. It uses built-in Node APIs and the already installed cwebp executable; no Python server or Python environment is required.

The existing generator installation is expected at ~/code/image-generation-benchmark, containing:

stable-diffusion.cpp/build/bin/sd-server
models/cyberrealistic_v5_fp16.safetensors
models/realisticVision_v51_hyper.safetensors
models/dreamshaper_8_lcm.safetensors
models/juggernaut_xi_lightning.safetensors
models/taesd.safetensors

Override with --tools /path/to/installation. Models stay outside the application. File fingerprints are cached under .cache/image-generation using file path, size and modification/change times; first use hashes the model files. Images are generated locally. --help lists arguments.

Verification

node --test scripts/image-generation/generate.test.ts
node_modules/.bin/tsc --ignoreConfig --noEmit --allowImportingTsExtensions --target esnext --module nodenext --moduleResolution nodenext --strict --skipLibCheck --types node scripts/image-generation/generate.ts scripts/image-generation/generate.test.ts

Tests cover WebP conversion, preset selection, model-specific request/cache settings, filename/settings hashing, overwrite protection, owned-server cleanup on errors and Ctrl+C, traversal rejection, and borrowed-server preservation. Tests use a tiny fake image server and the real local cwebp converter; a real Metal generation is verified separately through vp run generate:image.

Earlier real validation: the CyberRealistic preset produced a still-life WebP and stopped its server. Engine logs confirmed UCache skipped 9/20 steps; generation was 22.87 s with 1.02 s decode (23.32 s runner timing). This is a single run, not a controlled cached-versus-uncached speed comparison. Successful engine logs are retained in .cache/generations/<hash>/.