mud_server.translation.service
OOC→IC translation service.
OOCToICTranslationService is the single public entry-point for the
translation layer. It orchestrates CharacterProfileBuilder,
OllamaRenderer, and OutputValidator to produce in-character
dialogue from a raw player message.
Caller contract
translate() always returns either:
A non-empty IC string on success.
Noneon any failure (missing profile, Ollama error, validation failure).
The caller (GameEngine.chat/yell/whisper) treats None as a signal
to use the original OOC message. This is the graceful-degradation
guarantee: the layer never breaks the game.
Profile summary injection
Each world’s active canonical prompt template uses a single {{profile_summary}}
placeholder to embed the character’s current axis state as a formatted block.
Before _render_system_prompt substitutes placeholders, translate()
calls _build_profile_summary() to produce this block and injects it
into the profile dict under the key profile_summary. Without this step,
{{profile_summary}} would reach the LLM as a literal unresolved string
and the model would have no character context.
Ledger integration
Every translate() call — success or failure — emits a
chat.translation event to the world’s JSONL ledger via
append_event().
Events record:
status:"success"|"fallback.api_error""fallback.validation_failed"
character_name: the character whose voice was translatedchannel:"say"|"yell"|"whisper"ooc_input: the raw OOC message from the playeric_output: the final IC text, ornullon fallbackaxis_snapshot:{axis_name: {score, label}}for every axispresent in the character’s profile at translation time
A ledger write failure is never fatal — the game interaction
completes and only the audit record is lost. See
TODO(ledger-hardening) comments in _emit_translation_event().
The event is not emitted when the character profile cannot be
resolved (profile is None) — there is no character data to record.
IPC hash and deterministic mode
translate() accepts an optional ipc_hash: str | None parameter.
The axis engine (core/engine.py) computes this hash via
AxisEngine.resolve_chat_interaction() and passes it here.
When ipc_hash is provided and config.deterministic is True:
ipc_hash[:16]is converted to an integer seed.self._renderer.set_deterministic(seed_int)is called, clamping temperature to 0.0 and forwarding the seed to Ollama.Identical game state + identical OOC input → identical IC output (subject to Ollama model determinism at seed=constant, temp=0.0).
When ipc_hash is None (solo-room interactions, axis resolution
disabled, or axis engine failure), deterministic mode is silently skipped
and the renderer uses the configured temperature.
Pre-axis-engine era
Events emitted before the axis engine was integrated carry
meta: {"phase": "pre_axis_engine"} to distinguish them from
post-integration events during ledger replay or analysis.
Post-integration events with a real ipc_hash carry meta: {}.
Attributes
Classes
Result returned by |
|
Orchestrates profile building, rendering, and validation. |
Module Contents
- mud_server.translation.service.logger
- class mud_server.translation.service.LabTranslateResult[source]
Result returned by
OOCToICTranslationService.translate_with_axes.Carries the full research context the Axis Descriptor Lab needs to display results: the IC text, outcome status, the profile_summary block as the server formatted it, and the fully-rendered system prompt that was actually sent to Ollama.
- ic_text
Validated IC dialogue on success,
Noneon any fallback path.
- status
Outcome string —
"success","fallback.api_error", or"fallback.validation_failed".
- profile_summary
The
{{profile_summary}}block as formatted by the server (canonical format).
- rendered_prompt
The fully-rendered system prompt sent to Ollama, with all placeholders resolved.
- prompt_template
Raw template text before per-character variable substitution. Consumers that need a stable hash identifying which prompt file was used should hash this field, not
rendered_prompt.
- class mud_server.translation.service.OOCToICTranslationService(*, world_id, config, world_root)[source]
Orchestrates profile building, rendering, and validation.
One instance is created per
Worldwhen the world’stranslation_layer.enabledisTrue. It is cached on theWorldobject and reused for every chat call in that world.- _world_id
World this service is scoped to.
- _config
Frozen translation config from
world.json.
- _profile_builder
Builds the character context dict.
- _renderer
Calls the Ollama API.
- _validator
Validates/cleans the raw LLM output.
- _prompt_template
System prompt template text, loaded once at init.
Initialise the service.
- Parameters:
world_id (str) – World this service is scoped to. Required.
config (mud_server.translation.config.TranslationLayerConfig) – Frozen config from
world.json.world_root (pathlib.Path) – Path to the world package directory. Retained for compatibility with world construction flow.
- Raises:
ValueError – If
world_idis empty (same guard asCharacterProfileBuilder).
- translate(character_name, ooc_message, *, channel='say', ipc_hash=None)[source]
Translate an OOC message to in-character dialogue.
Full pipeline:
Build character axis profile (DB lookup).
Inject
channelandprofile_summaryinto the profile dict so that{{channel}}and{{profile_summary}}placeholders in the active canonical prompt template resolve correctly.Arm deterministic mode if
ipc_hashis provided andconfig.deterministicisTrue.Render the system prompt from the profile + template.
Call Ollama via the renderer.
Validate the raw output.
Emit a
chat.translationledger event (success or fallback).
On any failure at steps 1–6 the method returns
Noneand the caller falls back to the original OOC message. Step 7 always executes on the success/fallback paths (steps 5–6 only) — it is skipped when the profile cannot be resolved (step 1 failure) because there is no character data to record.- Parameters:
character_name (str) – Name of the character speaking (must exist in
self._world_id; world-scoped lookup is used).ooc_message (str) – The raw, unsanitised message from the player.
channel (str) – Chat channel context (
"say","yell","whisper"). Injected into the profile dict aschannelso that prompt templates can tailor tone by delivery mode.ipc_hash (str | None) – Optional IPC hash produced by the axis engine for this interaction. When provided and
config.deterministic=True, deterministic mode is armed: temperature is clamped to 0.0 and a seed derived from the hash is forwarded to Ollama, ensuring identical game state + OOC input always produces identical IC output. WhenNone(solo-room interaction, axis engine disabled, or engine failure), deterministic mode is skipped silently.
- Returns:
IC dialogue string on success,
Noneon any failure.- Return type:
str | None
- property config: mud_server.translation.config.TranslationLayerConfig
Return the world’s frozen translation layer configuration.
- translate_with_axes(axes, ooc_message, *, character_name='Lab Subject', channel='say', seed=None, temperature=0.7, prompt_template_override=None)[source]
Translate an OOC message using raw axis values — no DB lookup.
This is the entry point for the Axis Descriptor Lab. It accepts a caller-supplied axis dict instead of looking up a character in the database, then runs steps 2–6 of the standard
translate()pipeline (profile injection, prompt rendering, Ollama call, validation). No ledger event is emitted — lab calls are research runs, not production game interactions.The server filters
axesto its configuredactive_axesbefore building the profile, so the caller may supply all 11 known axes and the server will silently use only the ones its world is configured for. The response includesworld_configso the lab can see exactly which axes were applied.A fresh
OllamaRendereris created for each call to avoid polluting the persistent game renderer’s deterministic-mode state.- Parameters:
axes (dict[str, dict]) – Dict of
{axis_name: {"label": str, "score": float}}. Keys not inactive_axesare silently ignored.ooc_message (str) – The raw OOC message to translate.
character_name (str) – Display name used in the
profile_summaryfirst line. Defaults to"Lab Subject".channel (str) – Chat channel context (
"say","yell","whisper").seed (int | None) – Integer seed for deterministic Ollama output.
Nonemeans non-deterministic (random).temperature (float) – Sampling temperature forwarded to Ollama. Ignored when
seedis provided (clamped to 0.0 for determinism).prompt_template_override (str | None) – Optional full prompt template text. When provided, used instead of
self._prompt_templatefor this single call. The server’s canonical file is never modified.
- Returns:
LabTranslateResultwith the IC text, status, canonical profile_summary, and fully-rendered system prompt.- Return type: