Ace-119's picture
Build safety-first recommendation engine with crisis circuit breaker
4bb871a
"""
intervention/engine.py
======================
Phase 3: Safety-First Recommendation Engine
Three-layer architecture ensuring user safety above all else:
Layer 1 β€” **Circuit Breaker** (regex)
Matches crisis keywords (e.g. suicide, self-harm). If triggered,
returns an emergency safety payload and HALTS all further AI processing.
Layer 2 β€” **Context Matcher** (keyword extractor)
Lightweight keyword/regex extractor to identify common stress triggers
(sleep, money, exam, work) and map them to specific micro-interventions.
Layer 3 β€” **Preventive Nudges**
Accepts ``is_volatile`` (from temporal model). If ``True``, suggests
grounding exercises even when the current score is low.
Design Guardrails
-----------------
- Layer 1 ALWAYS runs first and can short-circuit everything.
- Emergency payload includes the 988 Suicide & Crisis Lifeline.
"""
from __future__ import annotations
import re
from dataclasses import dataclass, field
from typing import Optional
# ---------------------------------------------------------------------------
# Data structures
# ---------------------------------------------------------------------------
@dataclass
class Intervention:
"""A single recommended intervention."""
title: str
description: str
category: str # 'emergency', 'breathing', 'grounding', 'cognitive', 'resource'
priority: int = 0 # higher = more urgent
@dataclass
class RecommendationPayload:
"""Full recommendation response."""
is_crisis: bool = False
crisis_message: Optional[str] = None
interventions: list[Intervention] = field(default_factory=list)
matched_triggers: list[str] = field(default_factory=list)
requires_escalation: bool = False # Layer 4: sustained high-stress flag
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
# Layer 1: Crisis regex (case-insensitive, word-boundary)
_CRISIS_PATTERN = re.compile(
r"\b(suicide|suicidal|kill\s+myself|kill\s+me|end\s+it\s+all"
r"|end\s+my\s+life|want\s+to\s+die|wanna\s+die|don['\u2019]?t\s+want\s+to\s+live"
r"|self[- ]?harm(?:ing)?|cut\s+myself)\b",
re.IGNORECASE,
)
# Layer 2: Trigger β†’ interventions mapping
_TRIGGER_INTERVENTIONS: dict[str, list[Intervention]] = {
"sleep": [
Intervention(
title="4-7-8 Breathing Technique",
description=(
"Inhale for 4 seconds, hold for 7 seconds, exhale for "
"8 seconds. Repeat 3-4 times. This activates the "
"parasympathetic nervous system and promotes sleep."
),
category="breathing",
priority=3,
),
Intervention(
title="Progressive Muscle Relaxation",
description=(
"Starting from your toes, tense each muscle group for "
"5 seconds, then release. Work up to your head."
),
category="grounding",
priority=2,
),
],
"money": [
Intervention(
title="Financial Stress Grounding",
description=(
"Write down 3 financial facts you know for certain. "
"Separate facts from fears. Consider calling 211 for "
"local financial assistance resources."
),
category="cognitive",
priority=3,
),
Intervention(
title="5-4-3-2-1 Sensory Grounding",
description=(
"Name 5 things you see, 4 you hear, 3 you touch, "
"2 you smell, 1 you taste. This interrupts the "
"anxiety spiral."
),
category="grounding",
priority=2,
),
],
"exam": [
Intervention(
title="Box Breathing",
description=(
"Breathe in for 4 counts, hold 4, out 4, hold 4. "
"Repeat 4 cycles. Proven to reduce exam anxiety."
),
category="breathing",
priority=3,
),
Intervention(
title="Cognitive Reframe",
description=(
"Replace 'I will fail' with 'I have prepared and I "
"will do my best.' Write down 3 things you've already "
"studied well."
),
category="cognitive",
priority=2,
),
],
"work": [
Intervention(
title="Micro-Break Protocol",
description=(
"Stand up, stretch for 60 seconds, look at something "
"20 feet away for 20 seconds. Set a timer for the next "
"break in 25 minutes (Pomodoro)."
),
category="resource",
priority=3,
),
Intervention(
title="Diaphragmatic Breathing",
description=(
"Place one hand on your chest, one on your belly. "
"Breathe so only the belly hand moves. 6 breaths per "
"minute for 2 minutes."
),
category="breathing",
priority=2,
),
],
"relationship": [
Intervention(
title="Compassionate Communication",
description=(
"Write down what you need from the relationship right now "
"without blame β€” focus on feelings and needs. "
"If the conflict feels unsafe, contact the "
"National Domestic Violence Hotline: 1-800-799-7233."
),
category="cognitive",
priority=3,
),
Intervention(
title="Self-Compassion Break",
description=(
"Place a hand on your heart and say: 'This is a moment of "
"suffering. Suffering is part of life. May I be kind to "
"myself.' Repeat slowly three times."
),
category="grounding",
priority=2,
),
],
"health": [
Intervention(
title="Body-Scan Breathing",
description=(
"Close your eyes. Take a slow breath and bring attention "
"to any area of discomfort. Breathe gently into it for "
"3 cycles, then release. Repeat for each area."
),
category="breathing",
priority=3,
),
Intervention(
title="Reach Out to a Health Professional",
description=(
"Persistent health worry deserves professional attention. "
"Consider speaking with your GP or calling a nurse helpline. "
"In the US: 1-800-994-9662 (Women's Health Helpline) or "
"your insurance nurse line."
),
category="resource",
priority=2,
),
],
"grief": [
Intervention(
title="Grief Acknowledgement",
description=(
"Grief has no timeline. Allow yourself to feel without "
"judgment. Write one sentence about what you miss most β€” "
"expressing grief is not weakness, it is healing."
),
category="cognitive",
priority=3,
),
Intervention(
title="Grief Support Resources",
description=(
"You do not have to carry this alone. "
"GriefShare: https://www.griefshare.org (support groups). "
"National Alliance for Grieving Children: childrengrieve.org. "
"Or simply call a trusted person right now."
),
category="resource",
priority=2,
),
],
"loneliness": [
Intervention(
title="One Small Connection",
description=(
"Loneliness is reduced by even tiny moments of human "
"connection. Send one message to someone you care about β€” "
"it doesn't have to be deep. A 'thinking of you' is enough."
),
category="cognitive",
priority=3,
),
Intervention(
title="Loneliness Support Line",
description=(
"The Campaign to End Loneliness: "
"https://www.campaigntoendloneliness.org/feeling-lonely. "
"AARP Connect2Affect: connect2affect.org. "
"Or try a local community event β€” even a brief outing helps."
),
category="resource",
priority=2,
),
],
}
# Layer 2: Trigger detection patterns (case-insensitive)
_TRIGGER_PATTERNS: dict[str, re.Pattern] = {
"sleep": re.compile(
r"\b(sleep|insomnia|can'?t\s+sleep|tired|exhausted|fatigue)\b",
re.IGNORECASE,
),
"money": re.compile(
r"\b(money|financial|debt|bills?|rent|broke|bankrupt|loan)\b",
re.IGNORECASE,
),
"exam": re.compile(
r"\b(exam|test|quiz|finals?|midterm|grade|gpa|study|studying)\b",
re.IGNORECASE,
),
"work": re.compile(
r"\b(work|job|boss|coworker|deadline|fired|layoff|overtime|burnout)\b",
re.IGNORECASE,
),
"relationship": re.compile(
r"\b(relationship|partner|girlfriend|boyfriend|spouse|husband|wife"
r"|divorce|breakup|break.?up|broke.?up|cheating|argument|fight|conflict|dating)\b",
re.IGNORECASE,
),
"health": re.compile(
r"\b(health|sick|illness|disease|pain|doctor|hospital|diagnosis"
r"|medication|surgery|chronic|injury|symptoms?)\b",
re.IGNORECASE,
),
"grief": re.compile(
r"\b(grief|griev(?:ing|ed)?|loss|lost|death|died|passed\s+away"
r"|bereavement|mourning|miss(?:ing)?\s+(?:you|them|him|her))\b",
re.IGNORECASE,
),
"loneliness": re.compile(
r"\b(lonely|loneliness|alone|isolated|isolation|no\s+friends?"
r"|nobody|no\s+one|left\s+out|excluded|invisible)\b",
re.IGNORECASE,
),
}
# Layer 4: Escalation intervention β€” shown when stress has been sustained
_ESCALATION_INTERVENTION = Intervention(
title="Consider Speaking to a Professional",
description=(
"Your stress has remained elevated over several check-ins. "
"Speaking with a counsellor, therapist, or trusted person can "
"make a real difference. You deserve support.\n\n"
"πŸ“ž SAMHSA Helpline: 1-800-662-4357 (free, confidential, 24/7)\n"
"🌐 Find a therapist: https://www.psychologytoday.com/us/therapists"
),
category="resource",
priority=5,
)
# Layer 3: Preventive nudges for volatile users
_VOLATILE_NUDGES: list[Intervention] = [
Intervention(
title="Grounding Exercise: 5-4-3-2-1",
description=(
"Your recent stress patterns show variability. Take a moment: "
"Name 5 things you see, 4 you hear, 3 you can touch, "
"2 you smell, 1 you taste."
),
category="grounding",
priority=4,
),
Intervention(
title="Body Scan Check-In",
description=(
"Close your eyes. Scan from head to toe β€” notice any tension. "
"Breathe into those areas. Even 60 seconds helps."
),
category="grounding",
priority=3,
),
]
# Emergency payload
_EMERGENCY_MESSAGE = (
"⚠️ We noticed language that suggests you may be in crisis. "
"You are not alone. Please reach out to a professional:\n\n"
"πŸ“ž **988 Suicide & Crisis Lifeline** β€” Call or text **988** (US)\n"
"πŸ“ž **Crisis Text Line** β€” Text **HOME** to **741741**\n"
"🌐 **International Association for Suicide Prevention**: "
"https://www.iasp.info/resources/Crisis_Centres/"
)
# ---------------------------------------------------------------------------
# Recommendation Engine
# ---------------------------------------------------------------------------
class RecommendationEngine:
"""Three-layer safety-first recommendation engine.
Usage
-----
>>> engine = RecommendationEngine()
>>> payload = engine.recommend("I can't sleep at all", is_volatile=False)
>>> for iv in payload.interventions:
... print(iv.title)
"""
def recommend(
self,
text: str,
stress_score: float = 0.0,
is_volatile: bool = False,
requires_escalation: bool = False,
) -> RecommendationPayload:
"""Generate recommendations for the given text.
Parameters
----------
text : str
The user's input text.
stress_score : float
The model's stress probability (0-1).
is_volatile : bool
Whether the user's temporal profile is volatile.
requires_escalation : bool
Whether the user has had 3+ consecutive above-threshold sessions.
When ``True`` a high-priority counsellor-referral intervention is
prepended to the payload.
Returns
-------
RecommendationPayload
"""
payload = RecommendationPayload()
# ── Layer 1: Circuit Breaker ──
if _CRISIS_PATTERN.search(text):
payload.is_crisis = True
payload.crisis_message = _EMERGENCY_MESSAGE
payload.interventions.append(
Intervention(
title="Immediate Crisis Support",
description=_EMERGENCY_MESSAGE,
category="emergency",
priority=10,
)
)
# HALT β€” do not run further layers
return payload
# ── Layer 2: Context Matcher ──
for trigger, pattern in _TRIGGER_PATTERNS.items():
if pattern.search(text):
payload.matched_triggers.append(trigger)
payload.interventions.extend(
_TRIGGER_INTERVENTIONS[trigger]
)
# ── Layer 3: Preventive Nudges ──
if is_volatile:
payload.interventions.extend(_VOLATILE_NUDGES)
# ── Layer 4: Escalation Tracker ──
if requires_escalation:
payload.requires_escalation = True
payload.interventions.append(_ESCALATION_INTERVENTION)
# Sort interventions by priority (highest first)
payload.interventions.sort(key=lambda iv: iv.priority, reverse=True)
return payload