Persona Presets
Nội dung này hiện chưa có sẵn bằng ngôn ngữ của bạn.
Official persona presets are seeded from typed catalog definitions under src/db/seed/catalog/personas/ into persona_presets. They provide copy-on-write pointers that receive upstream catalog updates until local customization forks them into independent rows.
Data model and pointer resolution
Section titled “Data model and pointer resolution”persona_presets
Section titled “persona_presets”The canonical preset catalog stores character definitions keyed by (preset_lineage_id, preset_language):
preset_lineage_id: Identifies the official character family across localized variants. Display names (persona_preset_name) can change in the catalog without altering lineage or requiring schema migrations. When listing presets for a user locale,selectPresetsForLocale()picks the highest-priority fallback language that has at least one preset on the requested NSFW side and returns only that language’s rows, falling back toen-USonly when the locale has none. A locale that has at least one preset on a side therefore never sees presets that exist only in a later fallback language. The selected rows come back in ascendingpreset_lineage_idorder, so/setupand/persona defaultoffer the same sequence in every locale.preset_attribute_public_flags: Boolean array indicating which attributes are shared publicly in conversation participant profiles. Official presets mark their appearance attributes public; other seeded attributes stay private.preset_avatar_shared_urlandpreset_avatar_hash: Object storage reference and content hash for the bundled character avatar.preset_char_ref_shared_url: Object storage reference for the bundled character reference image, used by reference-based image generation.preset_naming_config: Defines gendered and neutral address terms for the character.
Pointer personas
Section titled “Pointer personas”When a server adopts an official preset via /setup or /persona default, the database creates a pointer persona with personas.is_pointer = true:
- Live content resolution: Runtime state loading resolves prompt text, attributes, public attribute flags, and sample dialogues directly from
persona_presets. - Trigger deduplication: Trigger words normally resolve live from
persona_presetsand pass throughapplyTriggerWordOwnership()to prevent collisions on shared keywords (see Multi-Persona System). Resynced pointers keep their local trigger words and naming habits throughpersona_configs.preserve_preset_identity. - Natural Nudges defaults: Talkativeness, interests, and authored idle nudges resolve live from
persona_presetsuntil the persona has its ownpersona_natural_nudge_configsrow. Editing them on the Nudges page writes that row without forking the pointer, which makesResync with Presetavailable to delete just that row. The first idle nudge content edit copies the whole preset list into the row; switching an entry on or off only records local availability and copies nothing.Restore Preset Nudgesreturns just the list to live resolution. Materializing copies the preset values into the row, and re-pointing deletes it. See Natural Nudges. - Lineage separation:
preset_lineage_ididentifies the official preset row, whilepersona_lineage_idscopes server memories, personal memories, and conditioning history. Applying a preset stampspersona_lineage_idwith the preset lineage, but memory updates never fork the pointer.
Shared assets and reconciliation
Section titled “Shared assets and reconciliation”Preset media is stored once and shared across servers:
Seeder / Sweep Lock (PostgreSQL Advisory Lock) ├── Seed Pass ──> Uploads immutable assets to presets/{lineage}/... │ ├── preset_avatar_shared_url (avatars) │ ├── preset_char_ref_shared_url (character references) │ └── preset_sprites (expressions) └── Fan-out ├── Alters ──> Live-resolve shared URL per webhook send (no local copy) └── Main ──> Reconciler PATCHes guild member avatar on hash deltaAlter avatars
Section titled “Alter avatars”Alter personas send messages via webhooks that accept an image URL per delivery. Pointer alters store NULL in personas.webhook_avatar_url and live-resolve preset_avatar_shared_url at load time (PersonaRepository.resolvePointerAlterAvatarUrl). Assets are stored under immutable paths (presets/{lineage}/avatar-{hash}.png), eliminating per-server storage costs.
Character references
Section titled “Character references”A pointer persona with no character reference of its own live-resolves preset_char_ref_shared_url at load time, so char_ref_url in its state names the shared presets/{lineage}/character-reference-{hash}.png image. The character reference loaders in charrefStorage.ts accept these shared paths for any persona, while panel display accepts a remote one only from this deployment’s avatar storage.
Main avatar reconciler
Section titled “Main avatar reconciler”The main persona uses Discord’s guild member identity, which cannot live-resolve per message. reconcilePresetMainAvatars() in src/utils/persona/presetAvatarReconciler.ts handles synchronization:
- Runs as a background task following
clientReady. - Compares
personas.applied_avatar_hashwithpersona_presets.preset_avatar_hash. - PATCHes Discord guild member avatars only when content hashes differ, skipping unchanged servers.
- Materialized (non-pointer) personas are skipped automatically.
Shared sprites (preset_sprites)
Section titled “Shared sprites (preset_sprites)”Official sprite variations are uploaded once to immutable storage paths (presets/{lineage}/sprites/{key}-{hash}.png). PersonaSpriteRepository.listForPersona() reads from preset_sprites for pointer personas and from persona_sprites for materialized personas, exposing a uniform interface to prompts and render modifiers.
Asset protection and cleanup
Section titled “Asset protection and cleanup”deletePersonaAvatarFromStorage() guards all paths under presets/, preventing server resets or persona removals from deleting shared catalog assets. Obsolete asset cleanup is handled through bun run sweep-preset-assets, which runs under a PostgreSQL advisory lock shared with startup seeders to prevent race conditions during deployments.
Materialization and customization
Section titled “Materialization and customization”The first local edit to prompt text, attributes, sprites, or naming forks the pointer into an independent copy:
is_pointertransitions tofalse.- The live preset state is copied into per-persona database tables (
personas,persona_attributes,persona_configs,persona_sprites). persona_lineage_idis preserved so existing memories and conditioning history remain intact.- Reference preservation: Sprites, alter avatars, and the character reference copy shared
presets/URLs by reference, avoiding byte duplication while retaining protection under storage deletion guards. - Avatar customization exception: Editing an avatar forks the persona and removes preset sprites referencing shared assets (
removePresetSpritesAfterAvatarChange), preventing expressions from the previous character from persisting alongside the new face. It also clears a character reference that is still the preset image (clearPresetCharacterReferenceAfterAvatarChange), so generated images stop drawing the previous character; an uploaded reference stays. - Pointer restoration: A server manager can use
Resync with Presetin/config> Persona > Identity & Personality for a forked main or alter persona. It restores the live preset’s prompt, attributes, sample dialogues, avatar, and sprites while preservingpersona_lineage_id, name, trigger words, naming habits, and other persona settings. Memories, conditioning, and stats keep their existing lineage. Custom avatars, own sprite rows, and the persona’s character reference are removed; shared preset images remain protected. Main personas apply the guild avatar immediately; a failed PATCH leaves the avatar hash unset so the boot reconciler retries./persona default type: defaultalso restores a pointer, but only for the main persona and with the selected preset’s lineage;type: altercreates another persona.
Cards, imports, and vision generation
Section titled “Cards, imports, and vision generation”- Export: Exports materialize pointer state into self-contained cards, tagging
preset_lineage_idfor character provenance. PNG and JSON exports carry the optional voice description (up to 1,000 characters) and Design/Clone priority. They also carry the effective authored idle nudges and the lineage’s lorebook entries, including disabled entries. Local nudge availability, server custom nudges, local sample IDs, sample bytes, provider voice IDs, credentials, and Gemini bindings are excluded. - Import: Re-establishes a live preset pointer only when attributes, public flags, dialogues, triggers, and prompt text exactly match the seeded preset and the card’s idle nudge list, when it has one, equals the preset’s. Deviations produce independent materialized copies. Importing an authored voice description also materializes the persona; older cards without it keep their pointer behavior. Main and alter imports restore the description and priority, defaulting missing priority to Design, and need a new reference assignment for Clone. Main imports retire previous Gemini bindings even when the imported description matches, so the imported persona creates its own identity lazily. Retired IDs stay pending for cleanup with the original credentials. Importing a card does not activate a speech provider or change the workspace’s Design and Clone targets.
- Lorebook replacement: A present lorebook payload replaces the destination lineage’s complete book; an absent payload preserves it. Nonempty lore prevents official-preset matching. See Lorebooks.
- Image captioning: During
/persona generate, an uploaded image is paired with the primary model. If the primary model cannot accept images,analyzeImageWithVisionModel()requests a description from the configured vision model and passes that text viaappearanceDescription, keeping raw images out of text-only model requests.
OpenRouter persona generation requests strict JSON output with six attributes and five dialogue pairs, including after successful search tool calls. A failed search tool result aborts persona generation before another model request. Google likewise aborts when its search stage fails. The command owns the failure reply and preserves the inputs with a retry hint; background tools suppress their public denial notices. Search engines can still fall back within one tool call. OpenRouter’s initial model request chooses the search tool, so it can incur usage before search fails. Anthropic requests omit unsupported array constraints. The shared schema used by other providers stays unchanged. Local validation still rejects noncompliant output without exporting a card; validation receipts preserve the inputs and show the failed requirement without suggesting an API-key problem.
Source pointers
Section titled “Source pointers”src/db/seed/catalog/personas/&src/db/seed/catalog/personaSeed.ts: Canonical preset catalog and startup seed logic.src/db/seed/catalog/personas/naturalNudges.ts: Per-lineage Natural Nudges defaults, including authored idle nudges.src/utils/persona/presetAvatarReconciler.ts: Background fan-out reconciler for main persona guild avatars.src/utils/db/repositories/PersonaRepository.ts: Live pointer state assembly and alter avatar resolution.src/utils/db/repositories/PersonaSpriteRepository.ts: Unified sprite resolution across pointer and materialized personas.src/utils/persona/spriteArchive.ts: Sprite package export and import serialization.