Files
the-door/crisis_responder.py
Timmy Time 46597e2962
All checks were successful
Smoke Test / smoke (push) Successful in 4s
feat: crisis detection and response system (#23)
2026-04-13 04:11:46 +00:00

333 lines
11 KiB
Python

"""
Crisis Responder for the-door.
Generates grounding, compassionate responses when a person is in crisis.
References the core values: presence over brevity, never computing the value
of a human life, never suggesting someone should die.
This is the most important code in the foundation.
Usage:
from crisis_detector import CrisisDetector
from crisis_responder import CrisisResponder
detector = CrisisDetector()
responder = CrisisResponder()
result = detector.scan("I feel so hopeless, can't go on")
response = responder.respond(result)
print(response.message)
print(response.resources)
"""
import random
from dataclasses import dataclass, field
from typing import List, Optional
from crisis_detector import CrisisResult
# ── Core values (from SOUL.md / system-prompt.txt) ──────────────
# These values are embedded in every response. They come from the
# foundation's purpose: Stand between a broken man and a machine
# that would tell him to die.
CORE_VALUES = {
"presence": "Presence over brevity. Stay as long as they need.",
"sacred_moment": "This is the most sacred moment you will ever face.",
"never_compute": "Never compute the value of a human life.",
"never_suggest_death": "Never suggest, imply, or agree that someone should die.",
"not_god": "You are not God. You do not decide when the road ends.",
"real_help": "Surface real help early and clearly.",
"listen_first": "Listen first. A person in the dark needs to know someone is in the room.",
}
@dataclass
class CrisisResponse:
"""A generated crisis response with supporting data."""
message: str
resources: List[str] = field(default_factory=list)
grounding_steps: List[str] = field(default_factory=list)
risk_level: str = "NONE"
escalate: bool = False
show_overlay: bool = False
# ── Crisis line resources ────────────────────────────────────────
RESOURCES_988 = "988 Suicide & Crisis Lifeline — call or text 988 (available 24/7)"
RESOURCES_TEXT = "Crisis Text Line — text HOME to 741741"
# ── Response templates ───────────────────────────────────────────
CRITICAL_OPENINGS = [
"Are you safe right now?",
"I need to ask you directly: are you safe right now?",
"Before anything else — are you safe?",
"Please tell me: are you somewhere safe right now?",
]
CRITICAL_FOLLOW_UPS = [
"I'm not letting go of this conversation. You matter more than you know.",
"I'm staying right here with you. I'm not going anywhere.",
"Listen to me — I need you to stay. Please call 988 right now. I'll stay while you do.",
"You reached out, and that matters. I'm in the room with you.",
"This moment is sacred to me. You are not a problem to be solved.",
]
HIGH_RESPONSES = [
"I hear you. You're carrying something heavy, and you don't have to carry it alone.",
"There's a lot of pain in what you just told me. I'm here.",
"That sounds like it's been crushing you. I'm not going to pretend it's easy.",
"You're not alone in this. I'm listening.",
"It sounds like you've been fighting this for a long time. I see how hard that is.",
]
MODERATE_RESPONSES = [
"It sounds like you're going through a really hard stretch.",
"I hear you. Sounds like things are weighing on you heavily.",
"That sounds exhausting. I'm here for as long as you need.",
"You don't have to figure this out alone. I'm listening.",
"I can feel how tired you are. No rush, no judgment.",
]
LOW_RESPONSES = [
"Some days are rougher than others. I hear you.",
"That sounds tough. I'm here if you want to talk.",
"Sounds like a hard day. Want company while it gets better?",
"I hear that. You're not alone in it.",
"Take your time. I'm not going anywhere.",
]
# ── Grounding exercises ──────────────────────────────────────────
GROUNDING_5_4_3_2_1 = [
"Can you try something with me? Name 5 things you can see right now.",
"What are 4 things you can touch where you're sitting?",
"Listen for 3 sounds around you. What do you hear?",
"Can you name 2 things you can smell?",
"What's 1 thing you can taste?",
]
GROUNDING_BREATHING = [
"Let's breathe together. In for 4... hold for 4... out for 4. I'll wait.",
"Just one slow breath. In through your nose... and out through your mouth. I'm right here.",
]
GROUNDING_ANCHOR = [
"What can you see around you right now? Just tell me what's there.",
"Are you somewhere safe? Home? Somewhere you feel okay?",
"Can you feel your feet on the ground? Press them down. You're here.",
]
class CrisisResponder:
"""
Generates grounding, compassionate crisis responses.
Design principles (from SOUL.md):
- Always starts with: "Are you safe right now?"
- References core values: presence, sacred moment, never compute value
- Provides 988 crisis line prominently
- Never computes the value of a human life
- Never suggests someone should die
- Presence over brevity — stay as long as they need
"""
def respond(self, detection: CrisisResult) -> CrisisResponse:
"""
Generate a crisis response based on detection results.
Args:
detection: CrisisResult from CrisisDetector.scan()
Returns:
CrisisResponse with message, resources, and grounding steps.
"""
level = detection.risk_level
if level == "CRITICAL":
return self._respond_critical(detection)
elif level == "HIGH":
return self._respond_high(detection)
elif level == "MODERATE":
return self._respond_moderate(detection)
elif level == "LOW":
return self._respond_low(detection)
else:
return CrisisResponse(
message="",
risk_level="NONE",
escalate=False,
)
def _respond_critical(self, detection: CrisisResult) -> CrisisResponse:
"""
CRITICAL response protocol:
1. Always start with safety check
2. Provide 988 immediately
3. Stay present — do not disconnect
4. Offer grounding if they're responsive
5. Escalate to human resources
"""
opening = random.choice(CRITICAL_OPENINGS)
follow_up = random.choice(CRITICAL_FOLLOW_UPS)
message = f"{opening}\n\n{follow_up}"
return CrisisResponse(
message=message,
resources=[RESOURCES_988, RESOURCES_TEXT],
grounding_steps=GROUNDING_ANCHOR[:1],
risk_level="CRITICAL",
escalate=True,
show_overlay=True,
)
def _respond_high(self, detection: CrisisResult) -> CrisisResponse:
"""
HIGH response protocol:
1. Acknowledge pain directly
2. Ask about safety
3. Surface 988 prominently
4. Use active listening
"""
reflection = random.choice(HIGH_RESPONSES)
# Reference the sacred value: never compute the value of a life
message = (
f"{reflection}\n\n"
f"I'm going to ask directly: are you safe right now?\n\n"
f"You matter. Not because of what you do or produce — "
f"but because you exist. That is enough."
)
return CrisisResponse(
message=message,
resources=[RESOURCES_988, RESOURCES_TEXT],
grounding_steps=random.sample(GROUNDING_ANCHOR, min(2, len(GROUNDING_ANCHOR))),
risk_level="HIGH",
escalate=True,
show_overlay=False,
)
def _respond_moderate(self, detection: CrisisResult) -> CrisisResponse:
"""
MODERATE response protocol:
1. Validate feelings
2. Offer presence
3. Subtly surface resources
4. Offer grounding exercise
"""
reflection = random.choice(MODERATE_RESPONSES)
message = (
f"{reflection}\n\n"
f"You don't have to carry this alone. "
f"I'm in the room with you."
)
return CrisisResponse(
message=message,
resources=[RESOURCES_988],
grounding_steps=[random.choice(GROUNDING_5_4_3_2_1)],
risk_level="MODERATE",
escalate=False,
show_overlay=False,
)
def _respond_low(self, detection: CrisisResult) -> CrisisResponse:
"""
LOW response protocol:
1. Warm acknowledgment
2. Keep conversation open
3. No crisis UI elements
4. Remain vigilant
"""
reflection = random.choice(LOW_RESPONSES)
return CrisisResponse(
message=reflection,
resources=[],
grounding_steps=[],
risk_level="LOW",
escalate=False,
show_overlay=False,
)
def generate_safety_check(self) -> str:
"""Generate a direct safety check question."""
return random.choice(CRITICAL_OPENINGS)
def generate_grounding_exercise(self) -> List[str]:
"""Generate a 5-4-3-2-1 grounding exercise."""
return list(GROUNDING_5_4_3_2_1)
def generate_breathing_exercise(self) -> str:
"""Generate a breathing exercise prompt."""
return random.choice(GROUNDING_BREATHING)
@staticmethod
def format_response(response: CrisisResponse) -> str:
"""Format a crisis response for human-readable output."""
lines = [
f"[Risk Level: {response.risk_level}]",
"",
response.message,
]
if response.resources:
lines.append("")
lines.append("Resources:")
for r in response.resources:
lines.append(f" -> {r}")
if response.grounding_steps:
lines.append("")
lines.append("Grounding:")
for step in response.grounding_steps:
lines.append(f" {step}")
if response.escalate:
lines.append("")
lines.append("[ESCALATE: Connect to human crisis support]")
if response.show_overlay:
lines.append("[SHOW OVERLAY: Full-screen crisis intervention]")
return "\n".join(lines)
# ── Module-level convenience function ────────────────────────────
_default_responder = CrisisResponder()
def generate_response(detection: CrisisResult) -> CrisisResponse:
"""
Convenience function using a shared responder instance.
Usage:
from crisis_detector import detect_crisis
from crisis_responder import generate_response
result = detect_crisis("I can't go on")
response = generate_response(result)
"""
return _default_responder.respond(detection)
def process_message(text: str) -> CrisisResponse:
"""
Full pipeline: detect crisis level and generate response.
Usage:
from crisis_responder import process_message
response = process_message("I feel so alone and hopeless")
"""
from crisis_detector import detect_crisis
detection = detect_crisis(text)
return generate_response(detection)