Repository navigation
Resolver and Prompt
Spec reference: MASTER_SPEC §17–19.
The Resolver is the deterministic back half of the pipeline. No Large Language Model is involved in it.
Renamed. This page was "Resolver, Categories & Prompt Builder". V1.1 removed the separate Category Splitter and Prompt Builder stages — the Resolver now does that work itself — so the page is named for what actually ships. Categories still exist, but only as traceability metadata (see Categories).
The Resolver converts semantic concepts into Illustrious-compatible tags. It is the first component aware of the target model and is completely deterministic. It ships a single implementation: the Illustrious Resolver.
- Input: Validated Scene JSON. Output: a flat prompt string (plus the traceable Resolved Tags as JSON for inspection).
- Must: normalize concepts; resolve aliases; look up canonical concepts; expand; generate Illustrious tags; remove duplicate tags; join the tags into one prompt string; emit warnings.
- Must not: call an LLM; guess concepts; invent tags; modify Scene JSON.
Concept → Normalize → Alias Resolution → Canonical Concept
→ Knowledge Base Lookup → Expansion → Tag Generation → Resolved Tags
→ join in resolution order → Prompt
- Normalization — lowercase, trim whitespace, collapse repeated whitespace, Unicode-normalize.
-
Alias resolution & lookup — aliases resolve directly to canonical concepts (
girl → female); alias chains are forbidden. Known concepts continue. -
Head-noun fallback (with modifier preservation) — when a compound concept has no entry, the Resolver drops leading modifiers and looks up the longest matching suffix (
white summer dress → dress), emitting anSC0019(Concept Reduced) warning. As of V2 each dropped modifier is then retried as a<modifier> <head-noun>compound through the normal resolution path and, when it resolves, emitted as its own Resolved Tag in discovery order (open white shirt → white shirt, open shirt). Only the high-precision compound form is tried (never bare single words), so this stays exact and deterministic — no tag is invented — and goldens are unchanged. A concept with no entry even after reduction produces theSC0001unknown-concept warning. -
Semantic fallback (V2, opt-in) — see below. When
semantic.enabledis on, a concept that misses both deterministic lookup and head-noun reduction gets one last nearest-neighbour attempt against the existing Knowledge Base before being reported unknown. -
NSFW gating — entries rated
explicitare excluded from lookup and expansion unlessresolver.include_nsfwis enabled; while disabled, a gated concept resolves as unknown. -
Expansion — occurs after canonical lookup; recursive; circular expansion is forbidden (detected by the KB loader). Expansion order (normative): a concept emits its own
tagsfirst, then, depth-first and in list order, the tags of eachexpandtarget, inserted immediately after the concept that generated them. Expansion order is stable. A configurable max expansion depth may be enforced. - Unknown concepts — never disappear; the Resolver warns and compilation continues.
-
Duplicate tags — removed after expansion; the first occurrence is kept and ordering preserved. As of V2 this deduplication is gated by
prompt_builder.remove_duplicate_tags(defaulttrue); when disabled, duplicates are kept and noSC0007is emitted. - Parallel resolution (V2) — for large scenes, independent per-concept resolution can run on a thread pool and merges back in exact discovery order, so output is identical regardless of scheduling. It is threshold-gated and sequential by default.
- Ordering & category preservation — output preserves discovery order; every tag keeps the category of its Knowledge Base Entry. Categories are never inferred later.
- Warnings (continue): unknown concept, deprecated concept, invalid expansion, missing Knowledge Base entry.
- Errors (stop): Knowledge Base unavailable/corrupted, circular expansion, invalid category.
The Resolver should log every decision, e.g.:
gold hair → alias → blonde hair → Knowledge Base `blonde_hair` → tag "blonde hair"
Future Resolvers (Illustrious, Pony, Flux…) consume identical Scene JSON; only the Knowledge Base changes, and the compiler core stays unchanged.
Added in V2 and off by default (semantic.enabled). When enabled, a concept
that misses both deterministic lookup and head-noun reduction falls back to its
nearest Knowledge Base entry in a committed embedding index
(data/kb_embedding_index.json), accepted only above semantic.min_similarity
and emitting an SC0020 (Semantic Fallback) warning. The default backend
(semantic.backend = char_ngram) is a deterministic, offline, dependency-free
character-n-gram model.
Guarantees: deterministic lookup always wins; the fallback can only return an
entry that already exists in the Knowledge Base, so it can never invent a tag; and
identical input yields identical output. Build/refresh the index with
scripts/build_embedding_index.py.
The Resolver is the final stage. After producing the ordered, de-duplicated
Resolved Tags, it joins their tag values into a single flat prompt string,
comma-separated, in resolution order (character concepts first, then the scene
sections). That is the whole "translator" — Scene JSON in, prompt out:
1girl,blonde hair,blue eyes,dress,smile,classroom,sunset
Nothing is reordered, escaped, or optimized at this step, and deduplication already happened during resolution. The Resolver node also emits the Resolved Tags as JSON so a translation problem is inspectable.
Removed in V1.1. Earlier versions split tags into 19 categories (Category Splitter) and emitted one prompt output per category, plus reserved Negative and Scene outputs (Prompt Builder). Those stages and nodes were removed: there is one flat prompt, not per-category outputs. The
categoryfield still exists on Knowledge Base entries and Resolved Tags for traceability, but it no longer shapes the output.
Every Knowledge Base entry still carries exactly one of these 19 categories,
surfaced on each Resolved Tag in the json output:
| Category | Purpose | Examples |
|---|---|---|
| Character | Identity |
1girl, 1boy, 2girls
|
| Appearance | General appearance |
young, adult, muscular
|
| Hair | Hair-related |
blonde hair, long hair, ponytail
|
| Face | Facial characteristics |
freckles, beauty mark, lipstick
|
| Eyes | Eye-related |
blue eyes, closed eyes
|
| Expression | Emotion |
smile, crying, blush
|
| Body | Body-related |
abs, tail, wings
|
| Clothing | Wearable items |
dress, shirt, armor
|
| Accessories | Portable items |
glasses, necklace, hat
|
| Pose | Static positions |
standing, kneeling, sitting
|
| Action | Independent actions |
walking, reading, running
|
| Interaction | Character relationships |
hug, holding hands, kiss
|
| Objects | Scene objects |
chair, book, tree
|
| Environment | Scene location |
forest, classroom, beach
|
| Camera | Camera descriptors |
close-up, wide shot, low angle
|
| Lighting | Lighting descriptors |
sunset, moonlight, studio lighting
|
| Style | Reserved for workflow integration | — (not generated) |
| Quality | Reserved for workflow integration | — (not generated) |
| Miscellaneous | Fallback | should remain nearly empty |
- Style is reserved for workflow integration; the curated Knowledge Base does not target it, though the generated Danbooru vocabulary maps a few style-ish concepts (e.g. colour scheme) here, so it may occasionally carry a tag. Quality is reserved and never generated in V1.
- Miscellaneous is a fallback that should stay nearly empty.
-
CompositionandBackgroundwere considered and intentionally excluded from V1; they may return in a future version.
The category of a concept is decided by the category field of its Knowledge
Base Entry, not by the Scene JSON section it appeared in. Style is reserved
for workflow integration (the generated vocabulary maps a few style-ish concepts
here); Quality is reserved and never generated; Miscellaneous is a rarely
used fallback. Composition and Background were intentionally excluded and may
return later.
EasyIllustrious compatibility is achieved by connecting the Resolver's prompt
output to the appropriate node; the compiler implements no EasyIllustrious-specific
logic.
This project — its code, tests, and documentation — is built with heavy help from AI coding assistants and maintained by a solo author. AI makes mistakes: despite testing and review, parts of this may be inaccurate, out of date, or wrong. Treat it as a good-faith best effort, not authoritative truth — if the docs and the code disagree, the code wins. Please open an issue when something looks off.