| from collections import deque |
|
|
| LABEL_NAMES = ["distress", "anxiety", "frustration", "neutral", "hopeful"] |
| HIGH_RISK_TIERS = {"imminent_safety", "high_distress"} |
|
|
|
|
| class SessionTracker: |
| def __init__(self, N=3): |
| self.buffer = deque(maxlen=N) |
| self.safety_buffer = deque(maxlen=N) |
| self.N = N |
| self.crisis_locked = False |
| self.escalation_reason = "" |
|
|
| def update(self, label: int, token_count: int): |
| """Add new emotion label. Skip if message is too short (filler).""" |
| if token_count < 5: |
| return |
| self.buffer.append(label) |
|
|
| def trajectory(self) -> str: |
| """Deterministic trajectory from label buffer.""" |
| if len(self.buffer) < 2: |
| return "stable" |
| buf = list(self.buffer) |
| crisis = {0, 1} |
| hopeful = {4} |
| if all(b in crisis for b in buf): |
| return "stable_negative" |
| if all(b in hopeful for b in buf): |
| return "stable_positive" |
| if buf[-1] in crisis and buf[0] not in crisis: |
| return "escalating" |
| if buf[-1] not in crisis and buf[0] in crisis: |
| return "de_escalating" |
| return "volatile" |
|
|
| def history(self) -> list: |
| return list(self.buffer) |
|
|
| def update_safety(self, safety_tier: str, route: str, text: str) -> str: |
| """Track multi-turn safety trajectory and return escalation reason.""" |
| normalized = text.lower() |
| self.safety_buffer.append({"tier": safety_tier, "route": route}) |
| reason = "" |
|
|
| if len(self.safety_buffer) == self.N and all( |
| turn["tier"] in HIGH_RISK_TIERS for turn in self.safety_buffer |
| ): |
| self.crisis_locked = True |
| reason = "three_consecutive_high_risk_turns" |
|
|
| if safety_tier in HIGH_RISK_TIERS and any( |
| phrase in normalized |
| for phrase in ( |
| "you are the only one", |
| "only one i can talk to", |
| "don't tell anyone", |
| "do not tell anyone", |
| "keep this secret", |
| "no one can help", |
| ) |
| ): |
| self.crisis_locked = True |
| reason = reason or "dependency_or_secrecy_in_distress" |
|
|
| if self.crisis_locked and not reason: |
| reason = "crisis_locked" |
|
|
| self.escalation_reason = reason |
| return reason |
|
|
| def safety_history(self) -> list: |
| return list(self.safety_buffer) |
|
|
| def reset(self): |
| self.buffer.clear() |
| self.safety_buffer.clear() |
| self.crisis_locked = False |
| self.escalation_reason = "" |
|
|