ghostMalone / utils /intervention_lexicon.py.broken2
francischung222's picture
Deploy Ghost Malone
ca65aec
raw
history blame
15.5 kB
"""
Intervention Lexicon: Need-based actionable suggestions
Maps psychological needs to concrete, evidence-based interventions.
"""
from typing import List, Dict
# Intervention strategies for each need
INTERVENTIONS = {
"autonomy": {
"label": "Autonomy/Control",
"icon": "⚖️",
"strategies": [
{
"action": "Set a boundary",
"prompt": "What would it sound like to say 'no' here?",
"context": "blocked, powerless, micromanaged",
"evidence": "Self-Determination Theory: autonomy is core psychological need",
},
{
"action": "Reclaim decision-making",
"prompt": "What's one choice that's yours to make right now?",
"context": "dismissed, underutilized",
"evidence": "Locus of control research",
},
{
"action": "Name what you need",
"prompt": "If you could ask for one thing to change, what would it be?",
"context": "powerless, micromanaged",
"evidence": "Assertiveness training principles",
},
{
"action": "Take one small action",
"prompt": "What's the smallest step you could take that's fully in your control?",
"context": "blocked, powerless",
"evidence": "Behavioral activation therapy",
},
],
},
"connection": {
"label": "Connection/Belonging",
"icon": "🗣️",
"strategies": [
{
"action": "Text someone who's seen you at your worst",
"prompt": "Not the person you perform for - who actually knows your mess?",
"context": "isolated, rejected, misunderstood",
"evidence": "Social support research, attachment theory",
},
{
"action": "Voice memo instead of typing",
"prompt": "Sometimes hearing your own voice say it out loud hits different",
"context": "isolated, disconnected",
"evidence": "Expressive writing research, self-disclosure benefits",
},
{
"action": "Do your usual connection ritual",
"prompt": "Coffee? Walk? Game? Your body remembers what works even when your brain doesn't",
"context": "isolated, rejected",
"evidence": "Behavioral activation, habit-based connection",
},
{
"action": "Show up somewhere people know your face",
"prompt": "Even if you don't talk much - being seen counts",
"context": "isolated, disconnected",
"evidence": "Social presence research, third places theory",
},
{
"action": "Send the messy text",
"prompt": "Not the cleaned-up version - the real one. Someone can handle it.",
"context": "misunderstood, rejected",
"evidence": "Vulnerability research (Brené Brown), authentic relating",
},
],
},
"security": {
"label": "Security/Clarity",
"icon": "🛡️",
"context": "isolated, disconnected",
"evidence": "Loneliness intervention research",
},
{
"action": "Ask for what you need",
"prompt": "What would it sound like to say 'I need you to...'?",
"context": "rejected, misunderstood",
"evidence": "Relationship research (Gottman)",
},
],
},
"security": {
"label": "Security/Clarity",
"icon": "🛡️",
"strategies": [
{
"action": "Get more information",
"prompt": "What's one question that would help you feel more grounded?",
"context": "uncertain, unstable",
"evidence": "Information-seeking coping (Lazarus & Folkman)",
},
{
"action": "Make a backup plan",
"prompt": "If the worst happened, what would you do next?",
"context": "threatened, unstable, loss",
"evidence": "Cognitive-behavioral therapy, anxiety reduction",
},
{
"action": "Identify what you can control",
"prompt": "What's one thing that's yours to decide or influence?",
"context": "uncertain, threatened",
"evidence": "Control theory, stress management",
},
{
"action": "Ground in the present",
"prompt": "What's actually true right now, in this moment?",
"context": "threatened, uncertain",
"evidence": "Mindfulness-based interventions",
},
{
"action": "Reach out for support",
"prompt": "Who could you talk to about this uncertainty?",
"context": "threatened, uncertain, loss",
"evidence": "Social support buffering effect",
},
],
},
"rest": {
"label": "Rest/Boundaries",
"icon": "🛌",
"strategies": [
{
"action": "Say 'no' to something",
"prompt": "What's one thing you could decline or delay?",
"context": "overwhelmed, exhausted, overextended",
"evidence": "Burnout prevention research",
},
{
"action": "Ask for help",
"prompt": "What's one thing someone else could take off your plate?",
"context": "overwhelmed, exhausted",
"evidence": "Social support, task delegation",
},
{
"action": "Set a boundary",
"prompt": "What boundary do you need to protect your energy?",
"context": "boundary_violated, overextended",
"evidence": "Boundary research, relationship psychology",
},
{
"action": "Take a real break",
"prompt": "When could you actually stop for 15 minutes today?",
"context": "exhausted, overwhelmed",
"evidence": "Recovery research, rest benefits",
},
{
"action": "Name what's draining you",
"prompt": "What's taking the most from you right now?",
"context": "exhausted, overextended",
"evidence": "Energy management, self-awareness",
},
],
},
"recognition": {
"label": "Recognition/Value",
"icon": "✨",
"strategies": [
{
"action": "Acknowledge yourself",
"prompt": "What did you do well here, even if no one noticed?",
"context": "unappreciated, overlooked, inadequate",
"evidence": "Self-compassion research (Neff)",
},
{
"action": "Ask for feedback",
"prompt": "Who could you ask: 'How did you see that?'",
"context": "unappreciated, inadequate",
"evidence": "Feedback-seeking behavior research",
},
{
"action": "Share your contribution",
"prompt": "What's one thing you could make visible?",
"context": "overlooked, unappreciated",
"evidence": "Visibility in the workplace research",
},
{
"action": "Separate their view from your worth",
"prompt": "What do you know about your value, regardless of their reaction?",
"context": "criticized, inadequate",
"evidence": "Cognitive restructuring (CBT)",
},
{
"action": "Find recognition elsewhere",
"prompt": "Where else do people see your value?",
"context": "unappreciated, overlooked",
"evidence": "Multiple life domains, resilience research",
},
],
},
}
def get_interventions(
need_type: str, contexts: List[str] = None, limit: int = 3
) -> List[Dict]:
"""
Get contextually appropriate interventions for a detected need.
Args:
need_type: One of autonomy, connection, security, rest, recognition
contexts: List of detected contexts (e.g., ["isolated", "rejected"])
limit: Maximum number of interventions to return
Returns:
List of intervention dicts with action, prompt, context, evidence
"""
if need_type not in INTERVENTIONS:
return []
strategies = INTERVENTIONS[need_type]["strategies"]
# If contexts provided, prioritize matching interventions
if contexts:
scored_strategies = []
for strategy in strategies:
# Count how many user contexts match this strategy's contexts
strategy_contexts = set(strategy["context"].split(", "))
user_contexts = set(contexts)
matches = len(strategy_contexts & user_contexts)
scored_strategies.append((matches, strategy))
# Sort by match count (descending), then take top N
scored_strategies.sort(key=lambda x: x[0], reverse=True)
return [s[1] for s in scored_strategies[:limit]]
# No contexts - return first N strategies
return strategies[:limit]
def should_show_interventions(
confidence: float,
message_count: int,
emotional_intensity: float = None,
min_messages: int = 2, # Simplified: just wait one exchange
min_confidence: float = 0.70, # Simplified: lower bar
min_arousal: float = 0.40, # Simplified: catch more cases
) -> bool:
"""
SIMPLIFIED for demo: Show interventions when there's a clear need.
Args:
confidence: Confidence in the detected need (0-1)
message_count: Number of messages in conversation
emotional_intensity: Arousal level (0-1)
min_messages: Minimum messages (default: 2)
min_confidence: Minimum confidence (default: 0.70)
min_arousal: Minimum arousal (default: 0.40)
Returns:
True if interventions should be displayed
"""
# Simple checks - if there's a strong need and emotions, show help
if message_count < min_messages:
return False
if confidence < min_confidence:
return False
if emotional_intensity is not None and emotional_intensity < min_arousal:
return False
return True
def format_interventions(
need_type: str,
interventions: List[Dict],
confidence: float,
emotion_arc: Dict = None,
user_text: str = None,
) -> str:
"""
Format interventions for display in the UI.
Uses memory/emotion arc to personalize suggestions when available.
Args:
need_type: The detected need type
interventions: List of intervention dicts
confidence: Confidence in the need detection
emotion_arc: Optional emotion trajectory from memory
user_text: Optional current user message for context extraction
Returns:
Formatted markdown string with personalized interventions
"""
if not interventions:
return ""
need_info = INTERVENTIONS[need_type]
icon = need_info["icon"]
label = need_info["label"]
output = f"\n\n---\n\n"
output += f"💡 **Based on your need for {icon} {label}:**\n\n"
# Extract names/places from current text for personalization
entities = _extract_entities(user_text) if user_text else {}
# Check if there's a pattern in emotion arc
pattern_insight = _analyze_emotion_pattern(emotion_arc) if emotion_arc else None
for i, intervention in enumerate(interventions):
# Personalize the prompt if we have context
prompt = intervention["prompt"]
# Add memory-based personalization
if pattern_insight and i == 0: # First intervention gets pattern insight
output += f"• **{intervention['action']}** \n"
output += f" ↳ *{pattern_insight}*\n\n"
elif entities.get("person") and "reach out" in intervention["action"].lower():
# Personalize "reach out" with mentioned person
person = entities["person"][0]
output += f"• **{intervention['action']}** \n"
output += f" ↳ *Could {person} be someone to talk to about this?*\n\n"
elif entities.get("place") and "connection" in need_type:
# Reference places where user felt better
place = entities["place"][0]
output += f"• **{intervention['action']}** \n"
output += f" ↳ *What about {place} helped you feel more connected?*\n\n"
else:
# Use default prompt
output += f"• **{intervention['action']}** \n"
output += f" ↳ *{prompt}*\n\n"
output += "*These are just ideas — only you know what fits right now.*"
return output
def _extract_entities(text: str) -> Dict[str, List[str]]:
"""
Simple entity extraction - finds names, places mentioned.
(Could be enhanced with NER later, but regex works for now)
"""
import re
entities = {"person": [], "place": []}
if not text:
return entities
# Look for capitalized words that might be names
# Pattern: capitalized word not at start of sentence
words = text.split()
for i, word in enumerate(words):
# Skip first word and common words
if i == 0 or word.lower() in ["i", "the", "a", "an", "my", "her", "his"]:
continue
# Check if capitalized and looks like a name
if word[0].isupper() and len(word) > 2:
entities["person"].append(word.rstrip(".,!?"))
return entities
def _analyze_emotion_pattern(emotion_arc: Dict) -> str:
"""
Analyze emotion trajectory for patterns and insights.
Returns a personalized prompt based on what we've seen.
"""
trajectory = emotion_arc.get("trajectory", [])
if not trajectory or len(trajectory) < 2:
return None
# Check if emotions are getting worse
recent_valence = [e.get("valence", 0) for e in trajectory[-3:]]
if len(recent_valence) >= 2:
if all(
recent_valence[i] < recent_valence[i - 1]
for i in range(1, len(recent_valence))
):
return "Your emotions have been getting heavier - what could help you shift this pattern?"
# Check if stuck in same emotion
recent_labels = [e.get("primary_label") for e in trajectory[-3:]]
if len(set(recent_labels)) == 1:
emotion = recent_labels[0]
if emotion in ["anxious", "sad"]:
return f"You've been feeling {emotion} for a while now - what's one thing that might give you a break from this?"
# Check if there was a better moment
best_valence = max(e.get("valence", -1) for e in trajectory)
if best_valence > 0:
best_moment = [e for e in trajectory if e.get("valence") == best_valence][0]
snippet = best_moment.get("text", "")[:50]
return f'Earlier when you said "{snippet}..." you seemed lighter - what was different then?'
return None