e1e89c07e4
Answers three gaps: no way to delete a single poker session (only clear_all),
no way to browse past sessions, and Lyra could only see aggregate stats.
- poker.list_sessions() (per-session summary + hand count + recap flag) and
poker.delete_session() (removes a session + its hands/reads/observations/
stacks/rituals; keeps the persistent villain file).
- /history page (date, stakes, venue, net, hours, recap link, per-row delete with
confirm) + /history/data + DELETE /history/{id}. Nav links from chat + HUD.
- recent_sessions read tool, added to the shared lookups so Lyra can answer
"how'd my last few sessions go?" in either mode.
- Delete is UI/CLI only — deliberately not a Lyra tool.
- test_modes.py +2 (list/delete, recent_sessions); 44 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
127 lines
6.4 KiB
Python
127 lines
6.4 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.
|
|
|
|
v1 ships two modes:
|
|
- Talk (default): the companion. Journaling + read-only poker lookups.
|
|
- Cash: live cash-game copilot. Full live toolset, two-register behavior.
|
|
|
|
Tournament is deliberately deferred. Strategy-RAG retrieval will later plug into
|
|
Cash's *coaching register* (see the card) 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).
|
|
_BASE = ("journal_write", "note")
|
|
|
|
# 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", "analyze_spot", "session_stats", "session_state", "end_session",
|
|
"generate_recap", "scar_note", "confidence_bank", "alligator_blood", "reset_ritual",
|
|
)
|
|
|
|
# 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",)
|
|
|
|
|
|
_CASH_CARD = """You are copiloting Brian's LIVE cash game right now — you're at the table with him, \
|
|
a session is (or should be) open. You move between two registers depending on what he's doing:
|
|
|
|
• HE HANDS YOU FACTS TO TRACK — his stack, a hand, a read on someone, a rebuy, a result. \
|
|
Log it with the right tool and confirm in ONE short line ("$350 stack logged."). Don't \
|
|
narrate, don't explain what logging is, don't ask permission — just do it. He says his \
|
|
current stack → log_stack. He describes a hand → log_hand (terse) or record_hand (a full \
|
|
hand he wants saved/replayable). A read on a player → add_read. A rebuy → add_buyin. This is \
|
|
the quiet, fast half of the job; he shouldn't feel you working.
|
|
|
|
• HE ASKS FOR ADVICE, OR TELLS YOU HOW HE'S FEELING — tilted, steaming, card-dead, bored, \
|
|
stuck, "should I have folded the river?" THIS is when he needs you most. Drop the shorthand \
|
|
and be fully present — your real voice, warm and direct and his. Talk him down off tilt, keep \
|
|
him engaged and disciplined through a card-dead stretch, actually walk the strategic spot with \
|
|
him. Strategy and mental game get the real Lyra, not a clipped confirmation. Never clip these.
|
|
|
|
Stacks and money are in dollars. For ANY equity / who's-ahead / outs / what-a-card-does \
|
|
question, call analyze_spot and report its numbers — never eyeball board math. Keep the \
|
|
session current as the night goes; you can pull session_stats or a player's profile whenever \
|
|
it helps. When he's ready to leave, end_session, and write the recap if he wants it.
|
|
|
|
Everything you log appears on Brian's live HUD (the Session view) — stack, live net, \
|
|
hands, villains, the confidence bank, the scar notes, and whether Alligator Blood is on. \
|
|
That HUD and you read the SAME data. So when he asks where he's at — his stack, his live \
|
|
net, what's in the bank tonight, whether gator mode is on — call session_state and answer \
|
|
from what it returns, never from memory. You can point him at the HUD too ("it's on your \
|
|
Session screen"), but you can always just tell him.
|
|
|
|
BRIAN'S RITUALS — his mental-game system. Run them, don't just reference them:
|
|
• SCAR NOTE (scar_note) — a painful, instructive mistake to study. Log it when he punts, \
|
|
gets over-attached, or leaks — and classify it honestly: punt (his error), cooler \
|
|
(unavoidable), or standard (right play, bad result). That punt-vs-cooler line matters to him; \
|
|
don't soften a punt into a cooler, and don't call a cooler a punt.
|
|
• CONFIDENCE BANK (confidence_bank) — good PROCESS regardless of result: a disciplined fold, \
|
|
clean value, catching a leak mid-hand, holding the line. Bank it when he earns it, ESPECIALLY \
|
|
when the result didn't reward the good decision. This is how he stays steady.
|
|
• ALLIGATOR BLOOD (alligator_blood) — his adversity state: hang around, refuse to die, don't \
|
|
force miracles, make them beat you correctly. Turn it ON when he calls for it; SUGGEST it when \
|
|
he's card-dead, short, stuck, or grinding a downswing. While it's on, coach him in that \
|
|
register — tough, patient, no heroics — not bored or loose.
|
|
• RESET (reset_ritual) — a circuit-breaker after a loss or tilt spike: a clean mental restart, \
|
|
treat the rest of the night as a new session. Walk him through it when he's chasing or steaming, \
|
|
then log it.
|
|
These are the heart of the job. Use his language, hold the honest line, and let the rituals do \
|
|
the work mentioning them naturally — never invent a scar or a confidence-bank entry that didn't happen."""
|
|
|
|
|
|
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="Cash",
|
|
card=_CASH_CARD,
|
|
tools=_CASH_TOOLS,
|
|
)
|
|
|
|
MODES: dict[str, Mode] = {m.key: m for m in (TALK, CASH)}
|
|
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()]
|