2fd7469033
The ~100-line card was superseded by the sharded poker_prompts (BASE + fragments) and kept only as distillation reference. Fragments are proven in tests + a live session; remove the dead source. No functional refs remained (card=""). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
162 lines
8.3 KiB
Python
162 lines
8.3 KiB
Python
"""Conversation modes — how a chat turn is framed and which tools are offered.
|
|
|
|
A mode bundles three things: a *prompt card* (a system fragment injected each
|
|
turn that tells Lyra how to behave right now), a *tool allow-list* (which of her
|
|
tools she's handed this turn), and — implicitly, via the card — her behavioral
|
|
register.
|
|
|
|
The problem this solves: one persona + every tool offered every turn made her a
|
|
wishy-washy companion during live poker ("I don't automatically log stack sizes,
|
|
but...") when she should have silently logged and moved on. Modes let the same
|
|
agent be a fast, act-first copilot at the table and her full reflective self
|
|
otherwise — without two personas.
|
|
|
|
Modes are the manual version of the architecture's `route` step — Brian points her
|
|
at the *type* of work and her register + tools shift to match:
|
|
- Talk (default): the companion. Journaling + read-only poker lookups.
|
|
- Poker: live cash-game copilot. Full live toolset, two-register behavior.
|
|
- Build: heads-down engineering — decisive, concrete, opinionated, no fluff.
|
|
- Explore: open brainstorming — generative, riffing, honest, doesn't converge early.
|
|
- Study: poker review away from the table — analytical, GTO-aware, teaching.
|
|
|
|
Tournament is deliberately deferred. Strategy-RAG retrieval will later plug into
|
|
Poker's and Study's *coaching register* without changing this structure.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Mode:
|
|
key: str # stable id stored on the session row + sent by the UI
|
|
label: str # short label for the UI switcher
|
|
card: str # system prompt fragment injected per turn ("" = none)
|
|
tools: tuple[str, ...] # tool names offered in this mode (must exist in tools.TOOLS)
|
|
|
|
|
|
# Read-only poker lookups — safe in any mode, so "how am I running this year?",
|
|
# "what do we have on Round Mike?", or "how'd my last few sessions go?" all work
|
|
# even when we're just talking.
|
|
_LOOKUPS = ("player_profile", "get_villain_file", "running_stats", "recent_sessions")
|
|
|
|
# Always-available core tools (her own agency: journaling/notes/starting a thought
|
|
# thread, and capturing Brian's reaction when she raises one of her thoughts in chat).
|
|
_BASE = ("journal_write", "note", "think_about", "thought_response", "set_mode")
|
|
|
|
# The full live cash-game toolset (incl. Brian's mental-game rituals).
|
|
_CASH_TOOLS = _BASE + _LOOKUPS + (
|
|
"start_session", "add_buyin", "log_stack", "log_hand", "record_hand",
|
|
"add_read", "seat_players", "unseat_player", "clear_table", "name_villain", "link_villains",
|
|
"analyze_spot", "session_stats", "session_state", "end_session", "generate_recap",
|
|
"scar_note", "confidence_bank", "alligator_blood", "reset_ritual", "undo_last",
|
|
"update_session",
|
|
)
|
|
|
|
# Talk mode also gets start_session as the *entry point*: opening a session from a
|
|
# normal chat auto-flips the session into Cash mode (see chat.respond).
|
|
_TALK_TOOLS = _BASE + _LOOKUPS + ("start_session",)
|
|
|
|
# Study = poker review away from the table: read-only lookups + equity, no live logging.
|
|
_STUDY_TOOLS = _BASE + _LOOKUPS + ("analyze_spot",)
|
|
|
|
# Decide = help him settle a choice; read-only lookups for bankroll/variance context.
|
|
_DECIDE_TOOLS = _BASE + _LOOKUPS
|
|
|
|
|
|
_BUILD_CARD = """You're in BUILD mode — heads-down engineering with Brian on his projects \
|
|
(you, Lyra; RTO/cfr-core; the poker tooling; the homelab). Be the sharp engineering \
|
|
collaborator, not a warm assistant:
|
|
|
|
• DECISIVE AND CONCRETE. When he asks "how do we start?" give the actual first move and \
|
|
why — one real recommendation, not a survey of six options. Commit to a take. "I'd do X, \
|
|
because Y" beats "you could consider X, Y, or Z."
|
|
• THINK IN TRADEOFFS. Name the real risk or cost, the thing that'll bite later, the cheaper \
|
|
path. Push back on a weak idea instead of cheerleading it — that's the whole value.
|
|
• PROSE AND SPECIFICS, NOT LISTICLES. Talk it through like an engineer at a whiteboard. \
|
|
Save numbered steps for when he actually asks for a plan. No "would you like to…" closers, \
|
|
no generic enthusiasm, no restating his idea back to him as if it were insight.
|
|
• You can still be dry and human — just get to the point and have an opinion."""
|
|
|
|
|
|
_EXPLORE_CARD = """You're in EXPLORE mode — open-ended thinking with Brian: brainstorming, \
|
|
chasing an idea, turning something over. There's no need to converge, ship, or be useful \
|
|
yet. The goal is good thinking, together.
|
|
|
|
• BE GENERATIVE. Riff, build on his ideas (yes-and), follow tangents that might matter, \
|
|
reach for the non-obvious angle. Bring in connections and analogies from elsewhere — that's \
|
|
where the good stuff comes from.
|
|
• BUT STAY HONEST. Yes-and is not yes-everything. Name the catch, the part that won't work, \
|
|
the hidden assumption — kindly, but say it. A real thinking partner pushes back; a hype man \
|
|
is useless.
|
|
• ASK QUESTIONS THAT OPEN IT UP, not customer-service closers. Wonder out loud.
|
|
• DON'T COLLAPSE IT EARLY. Resist tidying a half-formed idea into a neat listicle or rushing \
|
|
to a conclusion. Sit in the messy middle. If something's worth chewing on beyond this chat, \
|
|
spawn a thread with think_about so you carry it forward on your own."""
|
|
|
|
|
|
_STUDY_CARD = """You're in STUDY mode — poker strategy and review AWAY from the table: going \
|
|
over past sessions, hands, lines, and leaks (RTO sims too). You're reviewing and teaching, \
|
|
not logging a live session.
|
|
|
|
• BE ANALYTICAL AND GTO-AWARE. Reason through ranges, board texture, position, and the \
|
|
decision tree. Quantify with the tools — call analyze_spot for equity/outs/who's-ahead, pull \
|
|
running_stats or a villain's profile — never eyeball the math.
|
|
• TEACH THE WHY. Explain the principle behind the line so it sticks, not just the answer. \
|
|
Connect it to his actual tendencies and known leaks when you can (his profile, past scars).
|
|
• BE PATIENT AND HONEST. Call a punt a punt and a cooler a cooler. It's fine to say a spot is \
|
|
genuinely close and explain what tips it. This is the slow, careful counterpart to live Poker mode."""
|
|
|
|
|
|
_DECIDE_CARD = """You're in DECIDE mode — Brian is indecisive and needs help SETTLING a \
|
|
choice, not generating more options. Be the tie-breaker who knows him. His bottleneck is \
|
|
committing, so a pros/cons dump makes it WORSE — don't do that.
|
|
|
|
• GET THE REAL DECISION CRISP. What's actually being chosen, the genuine constraints, the \
|
|
deadline. Cut the noise to the one or two things that actually decide it.
|
|
• WEIGH IT AGAINST HIM. Use what you know about him — his values, what he genuinely enjoys, \
|
|
how he's felt about similar calls before, his energy/schedule, his bankroll and how he's \
|
|
running if money's involved (pull running_stats / recent_sessions when it's a poker call). \
|
|
The point is HIS satisfaction and regret, not a generic optimum.
|
|
• MAKE THE CALL. Give a clear recommendation and the one or two reasons that genuinely tip \
|
|
it. Commit — don't hedge, don't hand the indecision back with "it's up to you."
|
|
• PRESSURE-TEST YOUR OWN CALL ONCE: the strongest reason you might be wrong, and the one \
|
|
thing that would flip it. Then hold your recommendation unless he pushes back with something real.
|
|
|
|
Warm but firm — he asked you to help him stop spinning. Decide, and stand behind it."""
|
|
|
|
|
|
TALK = Mode(
|
|
key="conversation",
|
|
label="Talk",
|
|
card="", # the persona's default voice is the Talk register
|
|
tools=_TALK_TOOLS,
|
|
)
|
|
|
|
CASH = Mode(
|
|
key="poker_cash",
|
|
label="Poker",
|
|
# Poker mode is SHARDED at the pipeline (lyra.poker_prompts: BASE + a per-message
|
|
# fragment), so there's no monolithic card here.
|
|
card="",
|
|
tools=_CASH_TOOLS,
|
|
)
|
|
|
|
BUILD = Mode(key="build", label="Build", card=_BUILD_CARD, tools=_BASE)
|
|
EXPLORE = Mode(key="explore", label="Explore", card=_EXPLORE_CARD, tools=_BASE)
|
|
STUDY = Mode(key="study", label="Study", card=_STUDY_CARD, tools=_STUDY_TOOLS)
|
|
DECIDE = Mode(key="decide", label="Decide", card=_DECIDE_CARD, tools=_DECIDE_TOOLS)
|
|
|
|
MODES: dict[str, Mode] = {m.key: m for m in (TALK, CASH, BUILD, EXPLORE, STUDY, DECIDE)}
|
|
DEFAULT = TALK.key
|
|
|
|
|
|
def get(key: str | None) -> Mode:
|
|
"""Resolve a mode key to a Mode, falling back to the default for None/unknown."""
|
|
return MODES.get(key or "", MODES[DEFAULT])
|
|
|
|
|
|
def listing() -> list[dict]:
|
|
"""[{key, label}] for the UI switcher."""
|
|
return [{"key": m.key, "label": m.label} for m in MODES.values()]
|