def normalize_emotional_state(persona): """Extract and normalize emotional state from the new persona structure.""" default = { "anxiety": 0.5, "trust": 0.5, "openness": 0.5, "mode": "baseline", "emotional_memory": [] } return persona.get("default_state", default).copy() def apply_context_shift(persona, scenario): """Apply contextual scenario effects to persona's current emotional state.""" state = normalize_emotional_state(persona) effects = scenario.get("effects", {}) 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 state[key] = max(0.0, min(1.0, round(new_value, 3))) persona["default_state"] = state return persona def get_current_mode(state): """Determine the client's current emotional mode based on emotional state values.""" anxiety = state.get("anxiety", 0.5) trust = state.get("trust", 0.5) openness = state.get("openness", 0.5) # Raised thresholds significantly to prevent premature crisis states if anxiety > 0.85: # Was 0.8 - now much harder to reach decompensating return "decompensating" if anxiety > 0.75 and trust < 0.25: # Was 0.6 and 0.3 - much higher bar for triggered return "triggered" if openness < 0.3 and trust < 0.4: # Was 0.4 and 0.5 - harder to be guarded return "guarded" if trust > 0.7 and openness > 0.7: # Was 0.6 - higher bar for trusting too return "trusting" if anxiety < 0.3 and openness > 0.6: # Was 0.4 and 0.5 - adjusted return "recovering" return state.get("mode", "baseline") def calculate_state_change(state, student_response): """Calculate how the student's response affects the client's emotional state.""" 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() 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"] advice_words = ["should", "need to", "have to", "must", "why don't you"] minimizing = ["just", "simply", "easy", "only", "at least"] 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) # Reduced magnitude significantly - was causing too-fast emotional escalation positive_impact = (validation_score * 0.02 + open_q_score * 0.015 + empathy_score * 0.01) # Was 0.05, 0.04, 0.03 negative_impact = (advice_score * 0.03 + minimizing_score * 0.025) # Was 0.08, 0.06 changes = { "trust": positive_impact * 0.5 - negative_impact * 0.3, # Was 0.8 and 0.5 - reduced multipliers "anxiety": negative_impact * 0.3 - positive_impact * 0.2, # Was 0.5 and 0.3 - reduced "openness": positive_impact * 0.3 - negative_impact * 0.15 # Was 0.4 and 0.2 - reduced } word_count = len(student_response.split()) if word_count < 5: changes["trust"] -= 0.02 # Was 0.05 - reduced elif word_count > 100: changes["anxiety"] += 0.02 # Was 0.05 - reduced return changes def apply_response_effects(state, student_response): """Apply the effects of the student's response to the client's emotional state.""" 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, persona=None): """Generate pedagogical feedback based on the literary interaction.""" response_lower = student_response.lower() notes = [] # Get character-specific context if available char_name = persona.get("persona_name", "the character") if persona else "the character" source_text = persona.get("source_text", {}) if persona else {} author = source_text.get("author", "the author") title = source_text.get("title", "the text") # Question quality - literary analysis focused if any(word in response_lower for word in ["why did you", "why do you", "why are you"]): notes.append("💭 Literary Technique: 'Why' questions can sound accusatory. Consider using 'what' or 'how' to explore motivations more naturally (e.g., 'What were you thinking when...' or 'How did that moment feel?')") if any(word in response_lower for word in ["should", "need to", "have to"]): notes.append("📚 Character Analysis Tip: Avoid directive language like 'should' - it judges the character. Instead, explore their perspective: 'What other choices did you consider?' This reveals internal conflict.") if response_lower.count("?") > 2: notes.append("🎯 Focus Your Inquiry: Multiple questions can overwhelm. Pick your most important question for deeper exploration of theme, motivation, or symbolism.") # Question depth - connect to literary concepts if len(student_response.split()) < 8: if author and title: notes.append(f"📖 Add Textual Context: Reference specific moments from '{title}' to ground your question. Connect to scenes, relationships, or conflicts from {author}'s narrative.") else: notes.append("📖 Add Textual Context: Reference specific moments from the story to ground your question in the character's actual experiences.") if any(phrase in response_lower for phrase in ["tell me about", "tell me more", "what's that like", "how do you feel"]): notes.append("✅ Strong Question: Open-ended questions like this encourage the character to reveal subtext, internal conflict, and thematic depth. This is effective literary analysis.") if any(word in response_lower for word in ["symbol", "represent", "meaning", "theme"]): notes.append("🌟 Excellent: You're connecting character experience to broader literary themes. This kind of analysis reveals how authors use character to explore universal ideas.") # Character engagement - literary context if mode == "triggered" and state.get("trust", 0) < 0.4: if author: notes.append(f"⚠️ Character Defensive: Notice how {char_name} withdraws when challenged - this mirrors how {author} shows psychological realism. Try questions that validate their experience first.") else: notes.append("⚠️ Character Defensive: The character is withdrawing. Try questions that validate their experience before probing deeper.") if mode == "trusting" and state.get("anxiety", 0) > 0.6: notes.append("✨ Key Moment: The character is vulnerable right now - this is where authors reveal crucial backstory and motivation. Press deeper into their fears and desires.") if mode == "decompensating": notes.append("📊 Character State: The character is emotionally overwhelmed. In literary analysis, these breaking points often reveal theme. Consider: What does this breakdown tell us about the story's larger meaning?") if mode == "recovering": notes.append("🔍 Reflection Point: The character is stabilizing. Good time to ask: 'What have you learned?' or 'How have you changed?' - this reveals character development.") # Add literary context based on specific terms used if any(word in response_lower for word in ["choice", "decision", "decide"]): notes.append("💡 Theme Connection: You're exploring agency and choice - a key theme in literature. How does paralysis or action define this character?") if any(word in response_lower for word in ["family", "mother", "father", "parent", "sister", "brother"]): notes.append("👨‍👩‍👧 Family Dynamics: You're examining how family obligations shape identity - analyze how the author uses familial duty to explore broader social constraints.") if not notes: notes.append("✅ Good Engagement: You're exploring the character thoughtfully. Consider connecting their responses to the story's central themes and conflicts.") # Join notes with proper line breaks for HTML rendering return "

".join(notes)