jmisak commited on
Commit
43a6973
·
verified ·
1 Parent(s): be755d9

Update engine/loader.py

Browse files
Files changed (1) hide show
  1. engine/loader.py +152 -190
engine/loader.py CHANGED
@@ -1,191 +1,153 @@
1
- import yaml
2
- import os
3
-
4
- def load_persona(path):
5
- """
6
- Load a mental health persona from YAML file.
7
- Validates required fields for OT simulation.
8
- """
9
- if not os.path.exists(path):
10
- raise FileNotFoundError(f"Persona file not found: {path}")
11
-
12
- with open(path, "r", encoding="utf-8") as f:
13
- persona = yaml.safe_load(f)
14
-
15
- # Required keys for mental health personas
16
- required_keys = [
17
- "persona_name",
18
- "age",
19
- "role",
20
- "system_prompt",
21
- "facts",
22
- "default_state"
23
- ]
24
-
25
- for key in required_keys:
26
- if key not in persona:
27
- raise ValueError(f"Missing required key in persona: {key}")
28
-
29
- # Ensure default_state has required emotional metrics
30
- state = persona.get("default_state", {})
31
- required_state_keys = ["anxiety", "trust", "openness", "mode"]
32
-
33
- for key in required_state_keys:
34
- if key not in state:
35
- print(f"Warning: Missing state key '{key}' in persona. Using default value.")
36
- if key == "mode":
37
- state[key] = "baseline"
38
- else:
39
- state[key] = 0.5
40
-
41
- # Initialize emotional_memory if not present
42
- if "emotional_memory" not in state:
43
- state["emotional_memory"] = []
44
-
45
- # Ensure facts is a list
46
- if not isinstance(persona.get("facts"), list):
47
- print("Warning: facts should be a list. Converting.")
48
- facts = persona.get("facts", [])
49
- if isinstance(facts, dict):
50
- persona["facts"] = list(facts.values())
51
- else:
52
- persona["facts"] = []
53
-
54
- return persona
55
-
56
-
57
- def validate_persona(persona):
58
- """
59
- Validate that a persona has all necessary components for simulation.
60
- Returns (is_valid, error_message)
61
- """
62
- # Check persona name
63
- if not persona.get("persona_name"):
64
- return False, "Persona must have a name"
65
-
66
- # Check default state
67
- state = persona.get("default_state", {})
68
- if not state:
69
- return False, "Persona must have a default_state"
70
-
71
- # Check that anxiety, trust, openness are numeric
72
- for key in ["anxiety", "trust", "openness"]:
73
- value = state.get(key)
74
- if value is None:
75
- return False, f"default_state missing required key: {key}"
76
- if not isinstance(value, (int, float)):
77
- return False, f"default_state.{key} must be numeric"
78
- if not 0 <= value <= 1:
79
- return False, f"default_state.{key} must be between 0 and 1"
80
-
81
- # Check tone_guidance exists
82
- if not persona.get("tone_guidance"):
83
- return False, "Persona must have tone_guidance"
84
-
85
- # Check that tone_guidance has mode entries
86
- tone_guidance = persona.get("tone_guidance", {})
87
- recommended_modes = ["baseline", "guarded", "triggered", "trusting", "decompensating"]
88
-
89
- missing_modes = [mode for mode in recommended_modes if mode not in tone_guidance]
90
- if missing_modes:
91
- print(f"Warning: tone_guidance missing modes: {missing_modes}")
92
-
93
- return True, "Persona is valid"
94
-
95
-
96
- def save_persona(persona, path):
97
- """
98
- Save a persona to YAML file.
99
- """
100
- with open(path, "w", encoding="utf-8") as f:
101
- yaml.dump(persona, f, sort_keys=False, default_flow_style=False)
102
-
103
- return path
104
-
105
-
106
- def create_default_persona(name, age, role):
107
- """
108
- Create a basic persona template for development/testing.
109
- """
110
- persona = {
111
- "persona_name": name,
112
- "age": age,
113
- "role": role,
114
- "system_prompt": f"You are {name}, a {age}-year-old {role}. Respond naturally and stay in character.",
115
- "facts": [
116
- f"{name} is {age} years old",
117
- f"{name} works as a {role}"
118
- ],
119
- "triggers": [
120
- "criticism",
121
- "pressure",
122
- "isolation"
123
- ],
124
- "reasoning_style": "Tends to analyze situations carefully before responding.",
125
- "tone_guidance": {
126
- "baseline": {
127
- "voice": "Calm and thoughtful",
128
- "example": "I'm doing okay today, thanks for asking."
129
- },
130
- "guarded": {
131
- "voice": "Brief and cautious",
132
- "example": "I'd rather not talk about that right now."
133
- },
134
- "triggered": {
135
- "voice": "Defensive or withdrawn",
136
- "example": "I don't see how that's relevant."
137
- },
138
- "trusting": {
139
- "voice": "Open and reflective",
140
- "example": "You know, I've been thinking about what you said last time..."
141
- },
142
- "decompensating": {
143
- "voice": "Fragmented and overwhelmed",
144
- "example": "I just... I can't... it's too much."
145
- }
146
- },
147
- "default_state": {
148
- "anxiety": 0.5,
149
- "trust": 0.5,
150
- "openness": 0.5,
151
- "mode": "baseline",
152
- "emotional_memory": []
153
- },
154
- "scripts": {
155
- "crisis": "I'm not feeling safe right now. I need to step away.",
156
- "deflection": "It's fine. I don't want to make a big deal out of it.",
157
- "testing_trust": "Why are you asking about that?",
158
- "resistance": "I don't see how talking about this helps."
159
- },
160
- "resilience_hooks": [
161
- f"{name} has coping strategies they've used before",
162
- f"{name} values certain relationships in their life"
163
- ]
164
- }
165
-
166
- return persona
167
-
168
-
169
- def list_available_personas(persona_dir="./personas"):
170
- """
171
- List all available persona files.
172
- """
173
- if not os.path.exists(persona_dir):
174
- return []
175
-
176
- personas = []
177
- for filename in os.listdir(persona_dir):
178
- if filename.endswith(".yml") or filename.endswith(".yaml"):
179
- path = os.path.join(persona_dir, filename)
180
- try:
181
- persona = load_persona(path)
182
- personas.append({
183
- "filename": filename,
184
- "name": persona.get("persona_name", "Unknown"),
185
- "age": persona.get("age", ""),
186
- "role": persona.get("role", "")
187
- })
188
- except Exception as e:
189
- print(f"Error loading {filename}: {e}")
190
-
191
  return personas
 
1
+ import yaml
2
+ import os
3
+
4
+ def load_persona(path):
5
+ """
6
+ Load a LitDigitalTwin persona from YAML file.
7
+ Ensures required fields and initializes missing defaults.
8
+ """
9
+ if not os.path.exists(path):
10
+ raise FileNotFoundError(f"Persona file not found: {path}")
11
+
12
+ with open(path, "r", encoding="utf-8") as f:
13
+ persona = yaml.safe_load(f)
14
+
15
+ # Required top-level keys
16
+ required_keys = [
17
+ "persona_name",
18
+ "age",
19
+ "role",
20
+ "system_prompt",
21
+ "facts"
22
+ ]
23
+ for key in required_keys:
24
+ if key not in persona:
25
+ raise ValueError(f"Missing required key in persona: {key}")
26
+
27
+ # Ensure facts is a list
28
+ if not isinstance(persona.get("facts"), list):
29
+ print("Warning: 'facts' should be a list. Converting.")
30
+ facts = persona.get("facts", [])
31
+ if isinstance(facts, dict):
32
+ persona["facts"] = list(facts.values())
33
+ else:
34
+ persona["facts"] = []
35
+
36
+ # Initialize default_state if missing
37
+ if "default_state" not in persona:
38
+ persona["default_state"] = {}
39
+
40
+ state = persona["default_state"]
41
+ default_state_keys = {
42
+ "anxiety": 0.5,
43
+ "trust": 0.5,
44
+ "openness": 0.5,
45
+ "mode": "baseline",
46
+ "emotional_memory": []
47
+ }
48
+
49
+ for key, default in default_state_keys.items():
50
+ if key not in state:
51
+ print(f"Warning: Missing state key '{key}' in persona. Using default value.")
52
+ state[key] = default
53
+
54
+ # Ensure tone_guidance exists and has at least 'baseline'
55
+ if "tone_guidance" not in persona:
56
+ persona["tone_guidance"] = {
57
+ "baseline": {
58
+ "voice": "Natural and authentic",
59
+ "example": "I'm doing okay today, thanks for asking."
60
+ }
61
+ }
62
+
63
+ return persona
64
+
65
+
66
+ def validate_persona(persona):
67
+ """
68
+ Validate that a persona has all necessary components for simulation.
69
+ Returns (is_valid, error_message)
70
+ """
71
+ if not persona.get("persona_name"):
72
+ return False, "Persona must have a name"
73
+
74
+ if not persona.get("system_prompt"):
75
+ return False, "Persona must have a system_prompt"
76
+
77
+ if not isinstance(persona.get("facts"), list):
78
+ return False, "Persona 'facts' must be a list"
79
+
80
+ state = persona.get("default_state", {})
81
+ for key in ["anxiety", "trust", "openness"]:
82
+ value = state.get(key)
83
+ if value is None:
84
+ return False, f"default_state missing required key: {key}"
85
+ if not isinstance(value, (int, float)):
86
+ return False, f"default_state.{key} must be numeric"
87
+ if not 0 <= value <= 1:
88
+ return False, f"default_state.{key} must be between 0 and 1"
89
+
90
+ return True, "Persona is valid"
91
+
92
+
93
+ def save_persona(persona, path):
94
+ """
95
+ Save a persona to YAML file.
96
+ """
97
+ with open(path, "w", encoding="utf-8") as f:
98
+ yaml.dump(persona, f, sort_keys=False, default_flow_style=False)
99
+ return path
100
+
101
+
102
+ def create_default_persona(name, age, role):
103
+ """
104
+ Create a basic LitDigitalTwin persona template.
105
+ """
106
+ persona = {
107
+ "persona_name": name,
108
+ "age": age,
109
+ "role": role,
110
+ "system_prompt": f"You are {name}, a {age}-year-old {role}. Respond naturally and stay in character.",
111
+ "facts": [
112
+ f"{name} is {age} years old.",
113
+ f"{name} works as a {role}."
114
+ ],
115
+ "tone_guidance": {
116
+ "baseline": {
117
+ "voice": "Natural and authentic",
118
+ "example": "I'm doing okay today, thanks for asking."
119
+ }
120
+ },
121
+ "default_state": {
122
+ "anxiety": 0.5,
123
+ "trust": 0.5,
124
+ "openness": 0.5,
125
+ "mode": "baseline",
126
+ "emotional_memory": []
127
+ }
128
+ }
129
+ return persona
130
+
131
+
132
+ def list_available_personas(persona_dir="./personas"):
133
+ """
134
+ List all available persona files.
135
+ """
136
+ if not os.path.exists(persona_dir):
137
+ return []
138
+
139
+ personas = []
140
+ for filename in os.listdir(persona_dir):
141
+ if filename.endswith(".yml") or filename.endswith(".yaml"):
142
+ path = os.path.join(persona_dir, filename)
143
+ try:
144
+ persona = load_persona(path)
145
+ personas.append({
146
+ "filename": filename,
147
+ "name": persona.get("persona_name", "Unknown"),
148
+ "age": persona.get("age", ""),
149
+ "role": persona.get("role", "")
150
+ })
151
+ except Exception as e:
152
+ print(f"Error loading {filename}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  return personas