Spaces:
Sleeping
Sleeping
Venkatesh Rajagopal
REFRAME: live CBT studio — fine-tuned Gemma 12B on Modal + Cohere voice (ZeroGPU)
4ae4ae8 | """Card engine — state machine that builds a thought card from conversation.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass, field | |
| from enum import Enum | |
| from distortion_parser import detect_distortions | |
| from phase_detector import detect_phase | |
| from session import ThoughtCard | |
| class CardState(Enum): | |
| IDLE = "idle" | |
| TRIGGERED = "triggered" | |
| DISTORTION_TAGGED = "distortion_tagged" | |
| EVIDENCE_GATHERING = "evidence_gathering" | |
| REFRAMING = "reframing" | |
| COMPLETE = "complete" | |
| class ActiveCard: | |
| """Represents a card being built during conversation.""" | |
| state: CardState = CardState.IDLE | |
| card: ThoughtCard = field(default_factory=ThoughtCard) | |
| turn_count: int = 0 # turns since card was triggered | |
| def update_card(active: ActiveCard, model_response: str, user_message: str) -> ActiveCard: | |
| """Update the active card based on model response and user message. | |
| This is called after each model response. It advances the card state machine. | |
| """ | |
| active.turn_count += 1 | |
| # Detect distortions in model output | |
| distortions = detect_distortions(model_response) | |
| if distortions: | |
| for d in distortions: | |
| if d not in active.card.distortions: | |
| active.card.distortions.append(d) | |
| # Detect phase | |
| phase = detect_phase(model_response) | |
| # State transitions | |
| if active.state == CardState.IDLE: | |
| # Card triggers when model reflects back a thought or names a distortion | |
| if distortions or phase in ("automatic_thought", "situation"): | |
| active.state = CardState.TRIGGERED | |
| if user_message and not active.card.automatic_thought: | |
| active.card.automatic_thought = _extract_thought(user_message) | |
| elif active.state == CardState.TRIGGERED: | |
| if distortions: | |
| active.state = CardState.DISTORTION_TAGGED | |
| elif phase in ("evidence_for", "evidence_against"): | |
| active.state = CardState.EVIDENCE_GATHERING | |
| elif active.state == CardState.DISTORTION_TAGGED: | |
| if phase in ("evidence_for", "evidence_against"): | |
| active.state = CardState.EVIDENCE_GATHERING | |
| elif active.state == CardState.EVIDENCE_GATHERING: | |
| # Accumulate evidence from user messages | |
| if phase == "evidence_for" and user_message: | |
| active.card.evidence_for.append(user_message[:120]) | |
| elif phase == "evidence_against" and user_message: | |
| active.card.evidence_against.append(user_message[:120]) | |
| elif phase == "reframe": | |
| active.state = CardState.REFRAMING | |
| elif active.state == CardState.REFRAMING: | |
| if user_message and not active.card.balanced_thought: | |
| active.card.balanced_thought = user_message[:200] | |
| active.state = CardState.COMPLETE | |
| return active | |
| def should_show_card(active: ActiveCard) -> bool: | |
| """Whether the card should be visible in the UI.""" | |
| return active.state != CardState.IDLE | |
| def reset_card() -> ActiveCard: | |
| """Reset to a fresh card.""" | |
| return ActiveCard() | |
| def _extract_thought(message: str) -> str: | |
| """Extract the core thought from a user message (first 150 chars).""" | |
| return message[:150].strip() | |