The per-turn prompt was ~5.5K tokens (persona alone ~40%), sent up to 3x/turn.
Tightened by RELEVANCE (the control plane decides what each turn needs), not by
deletion — fidelity preserved, focus improved (buried instructions were getting
ignored), tokens roughly halved.
- persona split: core (identity + voice — always) vs situational sections pulled
in only when relevant. mind._persona_block: self-model/origin only on meta turns
(generous _META_HINTS), poker guardrails only in poker context (mode/strategic/
_POKER_HINTS). persona.core_prompt()/section(); system_prompt() kept as fallback.
- lean deliberation: the private 'what do I think' pass now uses a focused context
(her interiority + recent turns + the message), not the full persona/profile/
narrative/recall dump. It shapes the take, not the voice.
Measured: casual Talk turn 21,949 -> 15,974 chars (-27%); deliberation 21,949 ->
6,026 (-72%); meta turns still include the self-model. Suite 98 green, ruff clean.
Real retirement of the long prompt is still the fine-tune (mouth); this is the
cheap, high-leverage cut that also improves adherence.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"She suggests, you confirm" — instead of brittle keyword→mode mapping, she's given
awareness of her modes + the ability to switch, and her judgment decides when to
offer (the model reads "should I drive to Cleveland?" vs "should I fold the river"
far better than a lexicon could).
- tools: set_mode(mode) — switches the session's mode; in _BASE (all modes).
- mind: a per-turn mode-menu note listing her modes + "offer a switch when the work
clearly shifts; on his yes, call set_mode; don't nag."
- Sticky mode stays manual otherwise; Poker still auto-engages on session start.
- test: set_mode switches + rejects unknown. Suite 97 green, ruff clean.
Note: server-side switch takes effect next turn; the UI badge syncs on next mode
load (cosmetic lag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mind (chat backend/model) decides, reasons, and runs tools → a draft; the mouth
re-voices that draft in her character. Default: no mouth configured → the mind's
draft IS the reply, bit-for-bit the old behavior (and old streaming path untouched).
- config: MOUTH_BACKEND / MOUTH_MODEL. The slot for an eventual fine-tuned voice.
- chat: _mind_loop (tool/generation loop, non-stream, returns draft + tools_run),
_voice_pass / mind.voice_messages (re-voice the draft, keep every fact/number),
_mouth_target (active only when configured AND != mind). respond + respond_stream
branch: mouth off = stream the mind directly (unchanged); mouth on = mind decides
+ runs tools, then the mouth streams the re-voiced reply. Falls back to the draft
on any mouth failure (chat never breaks).
- Key payoff: the mouth needs no tool support (the mind handles tools), so it can be
a non-tool character model (Dolphin / Claude / fine-tune). Makes the fine-tune
easy: teach a small model to *sound* like Lyra, not to be smart.
- tests: mouth target on/off, voice_messages shape, voice_pass revoice+fallback.
Suite 96 green, ruff clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The control plane gains senses — cheap, deterministic, no LLM:
- lyra/perceive.py: lexicon+signal heuristic → {sentiment, intensity, tilt, kind:
emotional|strategic|meta|build|casual}. Good at the action-relevant signal,
especially tilt (the mental-game core). Word-boundary matching so 'line' doesn't
fire inside 'pipeline'.
- mind: _perceive fills ctx.moment; _route keeps the manual mode as the dominant
frame but, on a genuinely charged moment, adds a per-turn register nudge — tilt →
"meet him there, warm and steady, don't clip into logging"; up/energized → "match
his energy." Neutral turns get nothing (don't over-narrate). Injected via
build_messages(moment=...). Logged to /logs for observability.
- tests: perceive read (tilt/strategy/up/build/casual) + route nudge on/off.
Suite 92 green, ruff clean.
Complements modes (manual frame) — perceive refines register within it, doesn't
override. Model routing (mind/mouth) is P3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First step of the cognition control plane (docs/COGNITION.md). The chat turn is now
an explicit society of parts over a shared TurnContext blackboard:
perceive (stub) -> route (session mode) -> compose (tiered prompt) -> deliberate.
- lyra/mind.py (new): TurnContext + the pipeline + assemble(); moved build_messages
and the deliberation helpers here (the assembly belongs in the control plane).
- lyra/chat.py: slimmed to "speak + persist" — calls mind.assemble(), runs the
tool/generation loop, persists. No behavior change (same prompt, same output).
- tests: point test_time/test_chat at mind; add an assemble() structure test;
make test_chat/test_tools hermetic (CHAT_DELIBERATE off so respond() doesn't make
a real LLM call). Suite 86 green in ~5s, ruff clean, no import cycle.
This is the frame; perceive/route/learn get filled in next phases — each opt-in.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>