tts-voice-creator-clone-and.../architecture/character-visual-identity.md
mARTin-B78 5877aa2edc fix(characters): persist base-appearance image pairs, deterministic prompts, form-aware review; add forms panel
- fix: base-appearance matched pair lost its concept image + metadata on reload; visual save
  wrote base concept art to the record root; one save path for base and forms now
- fix: extraction no longer lets the LLM author image_prompt (source of avatar/concept drift);
  avatar-only generation uses the deterministic prompt and flags an existing pair
- fix: prompt writes and Physical/Clothing edits go to the active form; review endpoint checks
  each form's own pair; fingerprint parity with the browser incl. visual_* overrides
- feat: appearance forms panel (add/rename/delete/show, per-form stale flag, generate one/all)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 15:27:21 +02:00

56 lines
3.4 KiB
Markdown

# SOP — Character visual identity and appearance forms
Status: implemented. Covers how a character's avatar and concept (reference) sheet stay the same
person, and how one character holds several appearances ("forms") without splitting into several
characters or voices.
## Single source of truth
* Appearance facts come from the sheet's `physical`, `clothing`, `age_estimate`, `race_species`,
`gender`, `title`, `profession`, `archetype` (and the user overrides `visual_appearance`,
`visual_species`, `visual_costume`). Both prompts are built by
`_character_visual_facts` in `routes/conversation.py`; the active form is overlaid first
(`_character_form_sheet`, which never lets a form change its own `id`/`name`).
* `image_prompt` (portrait) and `concept_art_prompt` (reference sheet) are **deterministic** —
built server-side from those facts with fixed camera/lighting/period text. Sheet extraction no
longer asks the LLM to write `image_prompt` (it drifted from the concept sheet); the prompt is
generated right after extraction by `csAutoGenerateExternalPrompts`, and again before any
avatar-only or per-form generation. A hand-edited prompt (`*_user_edited`) is never overwritten.
* Only the two prompts describe an appearance. `voice_design_prompt` and the SillyTavern prompt
belong to the character as a whole, so they always live on the base sheet.
## Stale detection
`_character_identity_fingerprint` (server) and `_libIdentityFingerprint` (browser) hash the same
field list; a test runs the browser code in Node and compares, including list values and non-BMP
characters. The three `visual_*` overrides are appended only when set, so pairs made before they
existed keep a valid fingerprint. A pair needs review when its prompt version is not current, its
fingerprint is missing or differs, or `identity_consistent` is false. Regenerating **only** the
avatar or **only** the reference image sets `identity_consistent = false` on an existing pair,
because a lone regeneration produces a new face.
## Where the data lives
| | base appearance | a form |
|---|---|---|
| profile image | record `image` | `form.image` |
| reference image, version, fingerprint, digests, `identity_consistent` | `sheet.*` | `form.*` |
| Physical / Clothing text | `sheet.physical/clothing` | `form.physical/clothing` |
All visual results are written through one function (`_libSaveCharacterVisual`), which re-reads the
stored record, writes to the active form or the base, and saves. (It used to write the base
reference image to the record root, and the consistent-pair generator kept it in memory only, so a
base pair lost its reference image and metadata on reload.)
`GET /api/characters/visual-pair-review` checks the base pair and every form that has images, each
against its own metadata and its own fingerprint, and reports form pairs under the form name.
## Forms (max 8 per character, enforced in `core/validation.py`)
Add (starts as a copy of the base Physical/Clothing; only non-empty keys are stored because an
empty key would blank the base value when overlaid), rename (unique, ≤160 chars), delete (confirm;
the character, its voice and the base look stay), show, and generate matched images for one form or
for all forms. Generation rebuilds that form's prompts first (unless hand-edited), saves the pair on
that form, then restores whichever appearance was active. Editing Physical/Clothing while a form is
active changes only that form.