jmisak commited on
Commit
6f393d2
·
verified ·
1 Parent(s): e32ff62

Update engine/drift.py

Browse files
Files changed (1) hide show
  1. engine/drift.py +59 -109
engine/drift.py CHANGED
@@ -1,174 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
  def apply_context_shift(persona, scenario):
2
- """
3
- Apply contextual scenario effects to persona's current state.
4
- This simulates how external events affect the client's emotional state.
5
- """
6
- state = persona.get("default_state", {})
7
  effects = scenario.get("effects", {})
8
-
9
- # Apply each effect with bounds checking
10
  for key, change in effects.items():
11
  if key in state and isinstance(state[key], (int, float)):
12
  current_value = state[key]
13
  new_value = current_value + change
14
- # Clamp between 0 and 1
15
  state[key] = max(0.0, min(1.0, round(new_value, 3)))
16
-
17
- # Add context to emotional memory if it exists
18
- if "emotional_memory" in state:
19
- if not isinstance(state["emotional_memory"], list):
20
- state["emotional_memory"] = []
21
- state["emotional_memory"].append(
22
- f"context: {scenario.get('description', scenario.get('scenario'))}"
23
- )
24
- # Keep only last 5 memories
25
- state["emotional_memory"] = state["emotional_memory"][-5:]
26
-
27
  return persona
28
 
29
 
30
  def get_current_mode(state):
31
- """
32
- Determine the client's current emotional mode based on state values.
33
- This helps select appropriate response templates and tone.
34
- """
35
- anxiety = state.get("anxiety", 0.5)
36
- trust = state.get("trust", 0.5)
37
- openness = state.get("openness", 0.5)
38
-
39
- # Crisis threshold
40
- if anxiety > 0.8:
41
- return "decompensating"
42
-
43
- # Defensive/triggered
44
- if anxiety > 0.6 and openness < 0.3:
45
- return "triggered"
46
-
47
- # Guarded but present
48
- if trust < 0.4 and openness < 0.5:
49
  return "guarded"
50
-
51
- # Opening up
52
- if trust > 0.6 and openness > 0.6:
53
- return "trusting"
54
-
55
- # Recovering/hopeful
56
- if anxiety < 0.4 and trust > 0.5:
57
  return "recovering"
58
-
59
- # Baseline
60
- return "baseline"
61
 
62
 
63
- def calculate_state_change(current_state, student_response):
64
- """
65
- Calculate how the student's response affects the client's emotional state.
66
- This is a simplified heuristic - in production, would use more sophisticated NLP.
67
- """
68
-
69
- response_lower = student_response.lower()
70
- changes = {}
71
-
72
  if hasattr(student_response, 'value'):
73
  student_response = student_response.value
74
  student_response = str(student_response) if student_response is not None else ""
75
-
76
  response_lower = student_response.lower()
77
-
78
- # Positive indicators (validation, open questions, empathy)
79
  validating_words = ["understand", "sounds like", "seems", "feel", "must be", "makes sense"]
80
  open_questions = ["tell me more", "what's that like", "how", "what"]
81
  empathy_phrases = ["hard", "difficult", "challenging", "tough"]
82
-
83
- # Negative indicators (advice-giving, minimizing, interrogating)
84
  advice_words = ["should", "need to", "have to", "must", "why don't you"]
85
  minimizing = ["just", "simply", "easy", "only", "at least"]
86
- closed_questions = ["did you", "have you", "are you", "do you"]
87
-
88
  validation_score = sum(1 for word in validating_words if word in response_lower)
89
  open_q_score = sum(1 for phrase in open_questions if phrase in response_lower)
90
  empathy_score = sum(1 for phrase in empathy_phrases if phrase in response_lower)
91
-
92
  advice_score = sum(1 for word in advice_words if word in response_lower)
93
  minimizing_score = sum(1 for word in minimizing if word in response_lower)
94
-
95
- # Calculate changes
96
  positive_impact = (validation_score * 0.05 + open_q_score * 0.04 + empathy_score * 0.03)
97
  negative_impact = (advice_score * 0.08 + minimizing_score * 0.06)
98
-
99
- changes["trust"] = positive_impact - negative_impact
100
- changes["openness"] = positive_impact * 0.8 - negative_impact * 0.5
101
- changes["anxiety"] = negative_impact * 0.5 - positive_impact * 0.3
102
-
103
- # Response length consideration (too short or too long can be problematic)
 
 
104
  word_count = len(student_response.split())
105
  if word_count < 5:
106
- changes["openness"] = changes.get("openness", 0) - 0.05
107
  elif word_count > 100:
108
- changes["anxiety"] = changes.get("anxiety", 0) + 0.05
109
-
110
  return changes
111
 
112
 
113
  def apply_response_effects(state, student_response):
114
- """
115
- Apply the effects of the student's response to the client's state.
116
- """
117
- changes = calculate_state_change(state, student_response)
118
-
119
- # ADD THIS SAFEGUARD:
120
- if hasattr(student_response, 'value'):
121
- student_response = student_response.value
122
- student_response = str(student_response) if student_response is not None else ""
123
-
124
  changes = calculate_state_change(state, student_response)
125
  for key, change in changes.items():
126
  if key in state and isinstance(state[key], (int, float)):
127
  current_value = state[key]
128
  new_value = current_value + change
129
  state[key] = max(0.0, min(1.0, round(new_value, 3)))
130
-
131
  return state
132
 
133
 
134
  def generate_teaching_note(state, student_response, mode):
135
- """
136
- Generate teaching feedback based on the interaction.
137
- """
138
  response_lower = student_response.lower()
139
  notes = []
140
-
141
- # Check for common issues
142
  if any(word in response_lower for word in ["should", "need to", "have to"]):
143
  notes.append("⚠️ Advice-giving detected. Consider asking open questions instead of giving directives.")
144
-
145
  if any(word in response_lower for word in ["just", "simply", "only"]):
146
  notes.append("⚠️ Potential minimizing language. Avoid words that might diminish the client's experience.")
147
-
148
  if response_lower.count("?") > 2:
149
  notes.append("⚠️ Multiple questions detected. Consider asking one question at a time to avoid overwhelming the client.")
150
-
151
  if len(student_response.split()) < 10:
152
  notes.append("💡 Very brief response. Consider adding validation or reflection before asking questions.")
153
-
154
- # Check for strengths
155
  if any(phrase in response_lower for phrase in ["sounds like", "seems", "hear you"]):
156
  notes.append("✅ Good use of reflection and validation.")
157
-
158
  if "tell me more" in response_lower or "what's that like" in response_lower:
159
  notes.append("✅ Effective use of open-ended questions.")
160
-
161
- # Mode-specific feedback
162
- if mode == "triggered" and state.get("trust", 0) < 0.4:
163
- notes.append("📊 Client is defensive. Consider backing off and focusing on rapport building.")
164
-
165
- if mode == "trusting" and state.get("openness", 0) > 0.6:
166
- notes.append("📊 Strong therapeutic alliance forming. This is a good time to explore deeper issues.")
167
-
168
- if mode == "decompensating":
169
- notes.append("🚨 Client may be in crisis. Assess safety and consider referral to crisis services.")
170
-
171
  if not notes:
172
  notes.append("✅ Solid therapeutic response. Continue building rapport.")
173
-
174
  return "\n".join(notes)
 
1
+ def normalize_emotional_state(persona):
2
+ """Extract and normalize emotional state from the new persona structure."""
3
+ default = {
4
+ "emotional_tension": 0.0,
5
+ "relational_openness": 0.0,
6
+ "narrative_fluidity": 0.0,
7
+ "guilt": 0.0,
8
+ "mode": "baseline"
9
+ }
10
+ return persona.get("emotional_state", default).copy()
11
+
12
+
13
  def apply_context_shift(persona, scenario):
14
+ """Apply contextual scenario effects to persona's current emotional state."""
15
+ state = normalize_emotional_state(persona)
 
 
 
16
  effects = scenario.get("effects", {})
17
+
 
18
  for key, change in effects.items():
19
  if key in state and isinstance(state[key], (int, float)):
20
  current_value = state[key]
21
  new_value = current_value + change
 
22
  state[key] = max(0.0, min(1.0, round(new_value, 3)))
23
+
24
+ persona["emotional_state"] = state
 
 
 
 
 
 
 
 
 
25
  return persona
26
 
27
 
28
  def get_current_mode(state):
29
+ """Determine the client's current emotional mode based on emotional state values."""
30
+ tension = state.get("emotional_tension", 0.5)
31
+ openness = state.get("relational_openness", 0.5)
32
+ fluidity = state.get("narrative_fluidity", 0.5)
33
+
34
+ if tension > 0.8:
35
+ return "crisis"
36
+ if tension > 0.6 and openness < 0.3:
37
+ return "defensive"
38
+ if fluidity < 0.4 and openness < 0.5:
 
 
 
 
 
 
 
 
39
  return "guarded"
40
+ if openness > 0.6 and fluidity > 0.6:
41
+ return "vulnerable"
42
+ if tension < 0.4 and fluidity > 0.5:
 
 
 
 
43
  return "recovering"
44
+ return state.get("mode", "baseline")
 
 
45
 
46
 
47
+ def calculate_state_change(state, student_response):
48
+ """Calculate how the student's response affects the client's emotional state."""
 
 
 
 
 
 
 
49
  if hasattr(student_response, 'value'):
50
  student_response = student_response.value
51
  student_response = str(student_response) if student_response is not None else ""
 
52
  response_lower = student_response.lower()
53
+
 
54
  validating_words = ["understand", "sounds like", "seems", "feel", "must be", "makes sense"]
55
  open_questions = ["tell me more", "what's that like", "how", "what"]
56
  empathy_phrases = ["hard", "difficult", "challenging", "tough"]
 
 
57
  advice_words = ["should", "need to", "have to", "must", "why don't you"]
58
  minimizing = ["just", "simply", "easy", "only", "at least"]
59
+
 
60
  validation_score = sum(1 for word in validating_words if word in response_lower)
61
  open_q_score = sum(1 for phrase in open_questions if phrase in response_lower)
62
  empathy_score = sum(1 for phrase in empathy_phrases if phrase in response_lower)
 
63
  advice_score = sum(1 for word in advice_words if word in response_lower)
64
  minimizing_score = sum(1 for word in minimizing if word in response_lower)
65
+
 
66
  positive_impact = (validation_score * 0.05 + open_q_score * 0.04 + empathy_score * 0.03)
67
  negative_impact = (advice_score * 0.08 + minimizing_score * 0.06)
68
+
69
+ changes = {
70
+ "relational_openness": positive_impact * 0.8 - negative_impact * 0.5,
71
+ "emotional_tension": negative_impact * 0.5 - positive_impact * 0.3,
72
+ "narrative_fluidity": positive_impact * 0.4 - negative_impact * 0.2,
73
+ "guilt": empathy_score * 0.05 if "guilt" in response_lower else 0.0
74
+ }
75
+
76
  word_count = len(student_response.split())
77
  if word_count < 5:
78
+ changes["relational_openness"] -= 0.05
79
  elif word_count > 100:
80
+ changes["emotional_tension"] += 0.05
81
+
82
  return changes
83
 
84
 
85
  def apply_response_effects(state, student_response):
86
+ """Apply the effects of the student's response to the client's emotional state."""
 
 
 
 
 
 
 
 
 
87
  changes = calculate_state_change(state, student_response)
88
  for key, change in changes.items():
89
  if key in state and isinstance(state[key], (int, float)):
90
  current_value = state[key]
91
  new_value = current_value + change
92
  state[key] = max(0.0, min(1.0, round(new_value, 3)))
 
93
  return state
94
 
95
 
96
  def generate_teaching_note(state, student_response, mode):
97
+ """Generate teaching feedback based on the interaction."""
 
 
98
  response_lower = student_response.lower()
99
  notes = []
100
+
 
101
  if any(word in response_lower for word in ["should", "need to", "have to"]):
102
  notes.append("⚠️ Advice-giving detected. Consider asking open questions instead of giving directives.")
 
103
  if any(word in response_lower for word in ["just", "simply", "only"]):
104
  notes.append("⚠️ Potential minimizing language. Avoid words that might diminish the client's experience.")
 
105
  if response_lower.count("?") > 2:
106
  notes.append("⚠️ Multiple questions detected. Consider asking one question at a time to avoid overwhelming the client.")
 
107
  if len(student_response.split()) < 10:
108
  notes.append("💡 Very brief response. Consider adding validation or reflection before asking questions.")
 
 
109
  if any(phrase in response_lower for phrase in ["sounds like", "seems", "hear you"]):
110
  notes.append("✅ Good use of reflection and validation.")
 
111
  if "tell me more" in response_lower or "what's that like" in response_lower:
112
  notes.append("✅ Effective use of open-ended questions.")
113
+
114
+ if mode == "defensive" and state.get("relational_openness", 0) < 0.4:
115
+ notes.append("📊 Client is emotionally guarded. Consider using reflection or shared experience.")
116
+ if mode == "vulnerable" and state.get("emotional_tension", 0) > 0.6:
117
+ notes.append("📊 Client is opening up under emotional strain. Proceed with care.")
118
+ if mode == "crisis":
119
+ notes.append("🚨 Client may be emotionally overwhelmed. Consider grounding or referral.")
120
+
 
 
 
121
  if not notes:
122
  notes.append("✅ Solid therapeutic response. Continue building rapport.")
123
+
124
  return "\n".join(notes)