def apply_context_shift(persona, scenario): """ Apply contextual scenario effects to persona's current state. This simulates how external events affect the client's emotional state. """ state = persona.get("default_state", {}) effects = scenario.get("effects", {}) # Apply each effect with bounds checking for key, change in effects.items(): if key in state and isinstance(state[key], (int, float)): current_value = state[key] new_value = current_value + change # Clamp between 0 and 1 state[key] = max(0.0, min(1.0, round(new_value, 3))) # Add context to emotional memory if it exists if "emotional_memory" in state: if not isinstance(state["emotional_memory"], list): state["emotional_memory"] = [] state["emotional_memory"].append( f"context: {scenario.get('description', scenario.get('scenario'))}" ) # Keep only last 5 memories state["emotional_memory"] = state["emotional_memory"][-5:] return persona def get_current_mode(state): """ Determine the client's current emotional mode based on state values. This helps select appropriate response templates and tone. """ anxiety = state.get("anxiety", 0.5) trust = state.get("trust", 0.5) openness = state.get("openness", 0.5) # Crisis threshold if anxiety > 0.8: return "decompensating" # Defensive/triggered if anxiety > 0.6 and openness < 0.3: return "triggered" # Guarded but present if trust < 0.4 and openness < 0.5: return "guarded" # Opening up if trust > 0.6 and openness > 0.6: return "trusting" # Recovering/hopeful if anxiety < 0.4 and trust > 0.5: return "recovering" # Baseline return "baseline" def calculate_state_change(current_state, student_response): """ Calculate how the student's response affects the client's emotional state. This is a simplified heuristic - in production, would use more sophisticated NLP. """ response_lower = student_response.lower() changes = {} if hasattr(student_response, 'value'): student_response = student_response.value student_response = str(student_response) if student_response is not None else "" response_lower = student_response.lower() # Positive indicators (validation, open questions, empathy) validating_words = ["understand", "sounds like", "seems", "feel", "must be", "makes sense"] open_questions = ["tell me more", "what's that like", "how", "what"] empathy_phrases = ["hard", "difficult", "challenging", "tough"] # Negative indicators (advice-giving, minimizing, interrogating) advice_words = ["should", "need to", "have to", "must", "why don't you"] minimizing = ["just", "simply", "easy", "only", "at least"] closed_questions = ["did you", "have you", "are you", "do you"] validation_score = sum(1 for word in validating_words if word in response_lower) open_q_score = sum(1 for phrase in open_questions if phrase in response_lower) empathy_score = sum(1 for phrase in empathy_phrases if phrase in response_lower) advice_score = sum(1 for word in advice_words if word in response_lower) minimizing_score = sum(1 for word in minimizing if word in response_lower) # Calculate changes positive_impact = (validation_score * 0.05 + open_q_score * 0.04 + empathy_score * 0.03) negative_impact = (advice_score * 0.08 + minimizing_score * 0.06) changes["trust"] = positive_impact - negative_impact changes["openness"] = positive_impact * 0.8 - negative_impact * 0.5 changes["anxiety"] = negative_impact * 0.5 - positive_impact * 0.3 # Response length consideration (too short or too long can be problematic) word_count = len(student_response.split()) if word_count < 5: changes["openness"] = changes.get("openness", 0) - 0.05 elif word_count > 100: changes["anxiety"] = changes.get("anxiety", 0) + 0.05 return changes def apply_response_effects(state, student_response): """ Apply the effects of the student's response to the client's state. """ changes = calculate_state_change(state, student_response) # ADD THIS SAFEGUARD: if hasattr(student_response, 'value'): student_response = student_response.value student_response = str(student_response) if student_response is not None else "" changes = calculate_state_change(state, student_response) for key, change in changes.items(): if key in state and isinstance(state[key], (int, float)): current_value = state[key] new_value = current_value + change state[key] = max(0.0, min(1.0, round(new_value, 3))) return state def generate_teaching_note(state, student_response, mode): """ Generate teaching feedback based on the interaction. """ response_lower = student_response.lower() notes = [] # Check for common issues if any(word in response_lower for word in ["should", "need to", "have to"]): notes.append("⚠️ Advice-giving detected. Consider asking open questions instead of giving directives.") if any(word in response_lower for word in ["just", "simply", "only"]): notes.append("⚠️ Potential minimizing language. Avoid words that might diminish the client's experience.") if response_lower.count("?") > 2: notes.append("⚠️ Multiple questions detected. Consider asking one question at a time to avoid overwhelming the client.") if len(student_response.split()) < 10: notes.append("💡 Very brief response. Consider adding validation or reflection before asking questions.") # Check for strengths if any(phrase in response_lower for phrase in ["sounds like", "seems", "hear you"]): notes.append("✅ Good use of reflection and validation.") if "tell me more" in response_lower or "what's that like" in response_lower: notes.append("✅ Effective use of open-ended questions.") # Mode-specific feedback if mode == "triggered" and state.get("trust", 0) < 0.4: notes.append("📊 Client is defensive. Consider backing off and focusing on rapport building.") if mode == "trusting" and state.get("openness", 0) > 0.6: notes.append("📊 Strong therapeutic alliance forming. This is a good time to explore deeper issues.") if mode == "decompensating": notes.append("🚨 Client may be in crisis. Assess safety and consider referral to crisis services.") if not notes: notes.append("✅ Solid therapeutic response. Continue building rapport.") return "\n".join(notes)