Spaces:
Running
Running
| """ | |
| 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 | |