Skip to content

02: Native Assembly

Fixed-order contributor pipeline. Each numbered file in this folder documents one contributor that appends to the shared contextItems list. Order is structural — it determines where each item appears in the LLM’s prompt, and the LLM cares about that order.

File: src/utils/text/context/nativeBuilder.ts:27-320

Walk the numbered files in sequence — they describe the prompt top-to-bottom as the LLM will see it. The chat pipeline’s buildChatTurnContext appends tail directives after this output, so the actual prompt the LLM sees is:

[01] prompt items ← system role, top of prompt
[02] server info
[03] server memories ← skipped on impersonation
[04] server emojis ← optional
[05] server stickers ← optional
[06] participants
[07] short-term memory ← may emit lower-priority tail directive
[08] RAG documents ← optional
[09] conditioning ← optional
[10] sample dialogues ← few-shot examples
[11] dialogue history ← actual messages, bottom of prompt
+ tail directives ← appended by chat pipeline
+ uncensor directive ← appended by chat pipeline
#ContributorFileOutput tagSkipped when
01Prompt items01-prompt-items.mdSYSTEM_HUMANIZER_RULES, SYSTEM_CHANNEL_PROMPT, SYSTEM_PERSONA_PROMPT, SYSTEM_PERSONALITY(always emits at least one in non-impersonation)
02Server info02-server-info.mdKNOWLEDGE_SERVER_INFO(always emits)
03Server memories03-server-memories.mdKNOWLEDGE_SERVER_MEMORIESimpersonation; no memories
04Server emojis04-server-emojis.mdKNOWLEDGE_SERVER_EMOJISDM; emoji_usage_enabled=false; no guild emojis
05Server stickers05-server-stickers.mdKNOWLEDGE_SERVER_STICKERSDM; impersonation; sticker_usage_enabled=false
06Participants06-participants.mdKNOWLEDGE_USERS_IN_CONVERSATIONempty userList
07Short-term memory07-short-term-memory.mdKNOWLEDGE_SHORT_TERM_MEMORYno triggering user ID
08RAG documents08-rag-documents.mdKNOWLEDGE_SERVER_DOCUMENTSRAG unavailable; no docs; memory pressure critical
09Conditioning09-conditioning.mdKNOWLEDGE_SERVER_CONDITIONINGimpersonation; conditioning disabled; no persona lineage
10Sample dialogues10-sample-dialogues.mdDIALOGUE_SAMPLEimpersonation; mismatched/empty arrays
11Dialogue history11-dialogue-history.mdDIALOGUE_HISTORY, CONTEXT_NOTE_INJECTIONempty history (no-op)
type NativeBuildContextResult = {
contextItems: StructuredContextItem[];
tailDirectives: string[]; // populated by contributors
lowerPriorityTailDirectives: string[]; // populated by short-term memory
uncensorDirective?: string; // built from tomoriConfig.uncensor_*
};

Three contributors emit tail directives during assembly:

SourceDirectiveBucket
Native builder (impersonation closing block)"Imitate ${impersonatedIdentityName}, start your message with ${impersonatedIdentityName}:"tailDirectives
Short-term memory (stage 07)“Create a short-term memory for this conversation…” hintlowerPriorityTailDirectives (inserted before latest dialogue pair by the chat pipeline)
Native builder (uncensor closing block)Stripped [System: ...] text from buildUncensorInjectionTextuncensorDirective (separate tail item)

The wrapper (01-preset-routing.md) then runs random-choice macro resolution across all three buckets before returning.

These helpers are imported by multiple contributors and documented here rather than per-stage:

Converts <@id> / <@!id> / <#id> / <@&id> Discord mentions, channel permalinks, and {bot} / {user} placeholders into LLM-readable labels. Used by every contributor that emits text into context items. May fetch guild members, channels, and roles from Discord; respects user privacy levels (FULL privacy hides nicknames), blacklist status, and the server’s personal_memories_enabled config.

Rolls {{random:a,b}} / {{random::a::b}} / {random:a,b} / {random::a::b} macros independently per occurrence. Used by SillyTavern preset imports. Runs in the routing wrapper after all contributors complete (see stage 01).

toolPromptMacroResolver (src/utils/tools/toolPromptMacros.ts)

Section titled “toolPromptMacroResolver (src/utils/tools/toolPromptMacros.ts)”

Expands tool-name macros ({short_term_memory_tool}, {sticker_tool}, {memory_tool}, etc.) into the actual function-call tool names for the active provider. Each provider may name tools differently (provider name prefix, suffix conventions); the resolver hides this from prompt text. Built once at the top of the native builder via createToolPromptMacroResolver and passed to contributors that emit tool-reference text.

A grab-bag utility module split between two consumers:

  • Used by dialogueHistory.ts (stage 11): buildMediaAttributionText, buildMediaDescription, formatMessageTimestamp, getLastImageOccurrenceIndices, getRenderedImageMessageIdsWithinWindow, isCountedRenderedImageAttachment, MEDIA_IMAGE_MESSAGE_LIMIT, pushDialogueHistoryContextItem.
  • Used by participants.ts (stage 06): getUserPresenceDetails — formats Discord presence (online/idle/dnd, activities like Playing / Listening on Spotify / Watching / Competing) into a human-readable status string.
  • Re-exported from the public barrel: formatTimestampInline is exposed via src/utils/text/contextBuilder.ts for external callers.

The native builder itself is a fixed-order contributor sequence — there is no current registration mechanism for adding new contributors. The order matters (prompt at top, dialogue history at bottom) and changing it without the LLM noticing is unlikely.

A future plugin extension for “add a new contributor” would likely take the form of either:

  • (a) Named insertion points (afterServerInfo, beforeDialogueHistory) where a plugin’s ContextContributor registers. → plugin plan candidate.
  • (b) Pre/post hooks on the whole native build, less granular. The plugin emits its own context items and the runner appends them at the configured position.

The individual contributors do have plugin-relevant seams — see each per-stage doc’s “Extension points” section. Today’s most plugin-relevant seams are the memory-type contributors (stages 03, 07, 09) and the asset-type contributors (stages 04, 05).