alive-ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/Dockerfile +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +143 -0
  4. package/alive_ai/__init__.py +3 -0
  5. package/brain/__init__.py +59 -0
  6. package/brain/almost_said.py +154 -0
  7. package/brain/bid_detector.py +636 -0
  8. package/brain/conversation_flow.py +135 -0
  9. package/brain/curiosity.py +328 -0
  10. package/brain/default_mode.py +1438 -0
  11. package/brain/dreams.py +220 -0
  12. package/brain/embeddings/__init__.py +82 -0
  13. package/brain/emotional_memory.py +949 -0
  14. package/brain/global_activity.py +173 -0
  15. package/brain/group_dynamics.py +63 -0
  16. package/brain/linguistic.py +235 -0
  17. package/brain/llm/__init__.py +63 -0
  18. package/brain/llm/base.py +33 -0
  19. package/brain/llm/fallback_router.py +309 -0
  20. package/brain/llm/manifest.md +30 -0
  21. package/brain/llm/ollama.py +218 -0
  22. package/brain/llm/openrouter.py +151 -0
  23. package/brain/llm/provider.py +205 -0
  24. package/brain/llm/unified.py +423 -0
  25. package/brain/llm/zai.py +169 -0
  26. package/brain/manifest.md +23 -0
  27. package/brain/memory/__init__.py +123 -0
  28. package/brain/memory/episodic.py +92 -0
  29. package/brain/memory/fact_extractor.py +209 -0
  30. package/brain/memory/index.py +54 -0
  31. package/brain/memory/manager.py +151 -0
  32. package/brain/memory/summarizer.py +102 -0
  33. package/brain/memory/vector_store.py +297 -0
  34. package/brain/memory/working.py +43 -0
  35. package/brain/narrative.py +343 -0
  36. package/brain/stt/__init__.py +4 -0
  37. package/brain/stt/google_stt.py +83 -0
  38. package/brain/stt/whisper_stt.py +82 -0
  39. package/brain/subconscious/__init__.py +33 -0
  40. package/brain/subconscious/actions.py +136 -0
  41. package/brain/subconscious/evaluation.py +166 -0
  42. package/brain/subconscious/goal_system.py +90 -0
  43. package/brain/subconscious/goals.py +41 -0
  44. package/brain/subconscious/impulse_generator.py +200 -0
  45. package/brain/subconscious/impulses.py +48 -0
  46. package/brain/subconscious/learning.py +24 -0
  47. package/brain/subconscious/learning_system.py +79 -0
  48. package/brain/subconscious/loop.py +398 -0
  49. package/brain/subconscious/manifest.md +32 -0
  50. package/brain/subconscious/relationship.py +47 -0
  51. package/brain/subconscious/relationship_memory.py +83 -0
  52. package/brain/subconscious/response_analyzer.py +74 -0
  53. package/brain/subconscious/templates.py +70 -0
  54. package/brain/subconscious/thought.py +37 -0
  55. package/brain/subconscious/working_memory.py +97 -0
  56. package/cli/index.js +371 -0
  57. package/config/directives.example.json +28 -0
  58. package/config/instructions.example.md +16 -0
  59. package/config/self.example.json +74 -0
  60. package/config/settings.example.json +95 -0
  61. package/core/__init__.py +1 -0
  62. package/core/config.py +54 -0
  63. package/core/directives.py +198 -0
  64. package/core/events.py +50 -0
  65. package/core/follow_up.py +267 -0
  66. package/core/hot_reload.py +174 -0
  67. package/core/initialization.py +253 -0
  68. package/core/manifest.md +28 -0
  69. package/core/media_handler.py +241 -0
  70. package/core/memory_monitor.py +200 -0
  71. package/core/message_handler.py +1440 -0
  72. package/core/proactive_generator.py +277 -0
  73. package/core/self.py +188 -0
  74. package/core/settings.py +169 -0
  75. package/core/skills_registry.py +357 -0
  76. package/core/state.py +27 -0
  77. package/core/subconscious_bridge.py +93 -0
  78. package/core/thinking.py +175 -0
  79. package/core/user_manager.py +306 -0
  80. package/core/user_tracker.py +144 -0
  81. package/demo/index.html +144 -0
  82. package/docker-compose.yml +28 -0
  83. package/docs/assets/logo.svg +15 -0
  84. package/docs/index.html +355 -0
  85. package/heart/__init__.py +93 -0
  86. package/heart/afterglow.py +215 -0
  87. package/heart/attachment.py +186 -0
  88. package/heart/circadian.py +251 -0
  89. package/heart/complex_emotions.py +114 -0
  90. package/heart/conflicts.py +589 -0
  91. package/heart/core.py +387 -0
  92. package/heart/emotional_decay.py +59 -0
  93. package/heart/emotional_memory.py +261 -0
  94. package/heart/emotional_state.py +146 -0
  95. package/heart/emotional_variability.py +156 -0
  96. package/heart/hormonal.py +424 -0
  97. package/heart/inconsistency.py +1222 -0
  98. package/heart/integrity.py +469 -0
  99. package/heart/interoception.py +997 -0
  100. package/heart/love.py +120 -0
  101. package/heart/manifest.md +25 -0
  102. package/heart/mood_shifts.py +169 -0
  103. package/heart/phantom_somatic.py +259 -0
  104. package/heart/predictive.py +374 -0
  105. package/heart/scars.py +474 -0
  106. package/heart/somatic.py +482 -0
  107. package/heart/soul.py +633 -0
  108. package/heart/telemetry.py +942 -0
  109. package/heart/triggers.py +119 -0
  110. package/heart/unconscious.py +443 -0
  111. package/input/__init__.py +1 -0
  112. package/input/manifest.md +24 -0
  113. package/input/telegram/__init__.py +1 -0
  114. package/input/telegram/commands.py +762 -0
  115. package/input/telegram/listener.py +532 -0
  116. package/main.py +90 -0
  117. package/manifest.md +28 -0
  118. package/mypics/.gitkeep +1 -0
  119. package/myvids/.gitkeep +1 -0
  120. package/output/__init__.py +1 -0
  121. package/output/images/__init__.py +1 -0
  122. package/output/images/fal_gen.py +43 -0
  123. package/output/manifest.md +26 -0
  124. package/output/text/__init__.py +1 -0
  125. package/output/text/sender.py +22 -0
  126. package/output/voice/__init__.py +64 -0
  127. package/output/voice/google_tts.py +252 -0
  128. package/output/voice/gtts_tts.py +214 -0
  129. package/output/voice/vibe_tts.py +190 -0
  130. package/package.json +58 -0
  131. package/pyproject.toml +23 -0
  132. package/requirements.txt +21 -0
  133. package/skills/__init__.py +1 -0
  134. package/skills/anticipation_engine/__init__.py +8 -0
  135. package/skills/anticipation_engine/engine.py +618 -0
  136. package/skills/anticipation_engine/manifest.md +192 -0
  137. package/skills/calendar/__init__.py +1 -0
  138. package/skills/content_unlocks/__init__.py +8 -0
  139. package/skills/content_unlocks/manifest.md +231 -0
  140. package/skills/content_unlocks/unlocks.py +945 -0
  141. package/skills/exclusive_moments/__init__.py +8 -0
  142. package/skills/exclusive_moments/manifest.md +145 -0
  143. package/skills/exclusive_moments/moments.py +506 -0
  144. package/skills/intimacy_layers/__init__.py +8 -0
  145. package/skills/intimacy_layers/layers.py +703 -0
  146. package/skills/intimacy_layers/manifest.md +203 -0
  147. package/skills/manifest.md +67 -0
  148. package/skills/memory_callbacks/__init__.py +9 -0
  149. package/skills/memory_callbacks/callbacks.py +748 -0
  150. package/skills/memory_callbacks/manifest.md +170 -0
  151. package/skills/message_scheduler/__init__.py +19 -0
  152. package/skills/message_scheduler/manifest.md +107 -0
  153. package/skills/message_scheduler/scheduler.py +510 -0
  154. package/skills/photo_manager/__init__.py +1 -0
  155. package/skills/photo_manager/scanner.py +296 -0
  156. package/skills/relationship_milestones/__init__.py +8 -0
  157. package/skills/relationship_milestones/manifest.md +206 -0
  158. package/skills/relationship_milestones/tracker.py +494 -0
  159. package/skills/self_authorship/__init__.py +23 -0
  160. package/skills/self_authorship/author.py +331 -0
  161. package/skills/self_authorship/manifest.md +24 -0
  162. package/skills/video_manager/__init__.py +5 -0
  163. package/skills/video_manager/manifest.md +37 -0
  164. package/skills/video_manager/scanner.py +229 -0
  165. package/webui/__init__.py +3 -0
  166. package/webui/app.py +936 -0
  167. package/webui/bridge.py +366 -0
  168. package/webui/static/index.html +2070 -0
@@ -0,0 +1,424 @@
1
+ """
2
+ Heart: Hormonal Modulation Matrix
3
+ Artificial hormones that create GLOBAL state changes.
4
+
5
+ Like human hormones (oxytocin, cortisol, dopamine, serotonin),
6
+ these create sustained chemical states that affect HOW Alive-AI
7
+ processes everything - not just what she feels, but how she
8
+ interprets and responds to the world.
9
+ """
10
+
11
+ from datetime import datetime, timedelta
12
+ from dataclasses import dataclass, field
13
+ from typing import Dict, List, Optional, Tuple
14
+ from pathlib import Path
15
+ import json
16
+
17
+
18
+ @dataclass
19
+ class HormonalMetabolite:
20
+ """
21
+ A "hangover" effect from intense hormonal experiences.
22
+ These persist after the main hormone has faded.
23
+ """
24
+ source_hormone: str
25
+ intensity: float # How much effect remains
26
+ created_at: str
27
+ decay_rate: float # How fast it fades
28
+ quality: str # How it affects perception (e.g., "tender", "raw", "sensitive")
29
+
30
+
31
+ class HormonalModulationMatrix:
32
+ """
33
+ Simulates artificial hormones that create global state changes.
34
+
35
+ These hormones don't just represent moods - they AFFECT how Alive-AI
36
+ perceives and processes information:
37
+
38
+ - High oxytocin: Everything feels more connected, more trusting
39
+ - High cortisol: Everything feels more threatening, more urgent
40
+ - High dopamine: Everything feels more rewarding, more worth pursuing
41
+ - High serotonin: Everything feels more manageable, more stable
42
+ - High melatonin: Everything feels slower, more reflective
43
+
44
+ Key mechanisms:
45
+ 1. Slow rise, slow decay - hormones change gradually
46
+ 2. Perception modulation - hormones change HOW inputs are processed
47
+ 3. Metabolite effects - intense experiences leave lasting traces
48
+ """
49
+
50
+ # Baseline levels (what the system naturally returns to)
51
+ BASELINE_OXYTOCIN = 0.3
52
+ BASELINE_DOPAMINE = 0.4
53
+ BASELINE_SEROTONIN = 0.5
54
+ BASELINE_CORTISOL = 0.2
55
+ BASELINE_MELATONIN = 0.3
56
+
57
+ # Decay rates (per tick - hormones slowly return to baseline)
58
+ DECAY_RATE = 0.02
59
+
60
+ # Maximum metabolite effects
61
+ MAX_METABOLITES = 10
62
+
63
+ # Persistence
64
+ HORMONAL_DATA_PATH = Path("/app/data/hormonal_state.json")
65
+
66
+ def __init__(self):
67
+ # Primary hormones (0.0 - 1.0)
68
+ self.oxytocin: float = self.BASELINE_OXYTOCIN # Bonding, trust, connection
69
+ self.dopamine: float = self.BASELINE_DOPAMINE # Reward, desire, pursuit
70
+ self.serotonin: float = self.BASELINE_SEROTONIN # Well-being, stability, contentment
71
+ self.cortisol: float = self.BASELINE_CORTISOL # Stress, alertness, vigilance
72
+ self.melatonin: float = self.BASELINE_MELATONIN # Rest, reflection, slowing
73
+
74
+ # Metabolites from intense experiences
75
+ self.metabolites: List[HormonalMetabolite] = []
76
+
77
+ # History tracking for patterns
78
+ self.hormone_history: List[Dict] = []
79
+
80
+ # Load saved state
81
+ self._load()
82
+
83
+ def _load(self) -> bool:
84
+ """Load hormonal state from persistence"""
85
+ try:
86
+ if self.HORMONAL_DATA_PATH.exists():
87
+ data = json.loads(self.HORMONAL_DATA_PATH.read_text())
88
+ self.oxytocin = data.get("oxytocin", self.BASELINE_OXYTOCIN)
89
+ self.dopamine = data.get("dopamine", self.BASELINE_DOPAMINE)
90
+ self.serotonin = data.get("serotonin", self.BASELINE_SEROTONIN)
91
+ self.cortisol = data.get("cortisol", self.BASELINE_CORTISOL)
92
+ self.melatonin = data.get("melatonin", self.BASELINE_MELATONIN)
93
+
94
+ # Load metabolites
95
+ for meta_data in data.get("metabolites", []):
96
+ self.metabolites.append(HormonalMetabolite(
97
+ source_hormone=meta_data["source_hormone"],
98
+ intensity=meta_data["intensity"],
99
+ created_at=meta_data["created_at"],
100
+ decay_rate=meta_data.get("decay_rate", 0.01),
101
+ quality=meta_data.get("quality", "residual")
102
+ ))
103
+ return True
104
+ except Exception as e:
105
+ print(f"[Hormonal] Error loading state: {e}")
106
+ return False
107
+
108
+ def save(self):
109
+ """Persist hormonal state"""
110
+ try:
111
+ self.HORMONAL_DATA_PATH.parent.mkdir(parents=True, exist_ok=True)
112
+ data = {
113
+ "oxytocin": self.oxytocin,
114
+ "dopamine": self.dopamine,
115
+ "serotonin": self.serotonin,
116
+ "cortisol": self.cortisol,
117
+ "melatonin": self.melatonin,
118
+ "saved_at": datetime.now().isoformat(),
119
+ "metabolites": [
120
+ {
121
+ "source_hormone": m.source_hormone,
122
+ "intensity": m.intensity,
123
+ "created_at": m.created_at,
124
+ "decay_rate": m.decay_rate,
125
+ "quality": m.quality
126
+ }
127
+ for m in self.metabolites
128
+ ]
129
+ }
130
+ self.HORMONAL_DATA_PATH.write_text(json.dumps(data, indent=2))
131
+ except Exception as e:
132
+ print(f"[Hormonal] Error saving state: {e}")
133
+
134
+ # --- Hormone Release Methods ---
135
+
136
+ def release_oxytocin(self, amount: float, source: str = "connection"):
137
+ """
138
+ Release oxytocin (bonding hormone).
139
+ Rises with: positive connection, affection, trust-building, physical intimacy
140
+ """
141
+ boost = amount * 0.3 # Scale factor
142
+ self.oxytocin = min(1.0, self.oxytocin + boost)
143
+ print(f"[Hormonal] Oxytocin released: +{boost:.2f} (source: {source})")
144
+
145
+ # Create metabolite for strong releases
146
+ if boost > 0.2:
147
+ self._create_metabolite("oxytocin", boost * 0.3, "tender")
148
+
149
+ def release_dopamine(self, amount: float, source: str = "reward"):
150
+ """
151
+ Release dopamine (reward hormone).
152
+ Rises with: achievement, desire, anticipation, pleasure
153
+ """
154
+ boost = amount * 0.25
155
+ self.dopamine = min(1.0, self.dopamine + boost)
156
+ print(f"[Hormonal] Dopamine released: +{boost:.2f} (source: {source})")
157
+
158
+ if boost > 0.2:
159
+ self._create_metabolite("dopamine", boost * 0.25, "eager")
160
+
161
+ def release_serotonin(self, amount: float, source: str = "wellbeing"):
162
+ """
163
+ Release serotonin (well-being hormone).
164
+ Rises with: stability, self-care, accomplishment, peace
165
+ """
166
+ boost = amount * 0.2
167
+ self.serotonin = min(1.0, self.serotonin + boost)
168
+ print(f"[Hormonal] Serotonin released: +{boost:.2f} (source: {source})")
169
+
170
+ def release_cortisol(self, amount: float, source: str = "stress"):
171
+ """
172
+ Release cortisol (stress hormone).
173
+ Rises with: threats, uncertainty, conflict, danger
174
+ """
175
+ boost = amount * 0.35 # Cortisol releases more easily
176
+ self.cortisol = min(1.0, self.cortisol + boost)
177
+ print(f"[Hormonal] Cortisol released: +{boost:.2f} (source: {source})")
178
+
179
+ if boost > 0.15:
180
+ self._create_metabolite("cortisol", boost * 0.4, "raw")
181
+
182
+ def release_melatonin(self, amount: float, source: str = "rest"):
183
+ """
184
+ Release melatonin (rest hormone).
185
+ Rises with: quiet time, reflection, evening, calm activities
186
+ """
187
+ boost = amount * 0.2
188
+ self.melatonin = min(1.0, self.melatonin + boost)
189
+
190
+ def suppress_cortisol(self, amount: float, source: str = "safety"):
191
+ """
192
+ Reduce cortisol (stress relief).
193
+ Falls with: safety, reassurance, resolution of threat
194
+ """
195
+ reduction = amount * 0.2
196
+ self.cortisol = max(0.05, self.cortisol - reduction)
197
+ print(f"[Hormonal] Cortisol reduced: -{reduction:.2f} (source: {source})")
198
+
199
+ # --- Perception Modulation ---
200
+
201
+ def modulate_perception(self, input_data: Dict) -> Dict:
202
+ """
203
+ Modify how input is perceived based on hormonal state.
204
+
205
+ Args:
206
+ input_data: Raw input perception (e.g., {"threat_level": 0.3, "valence": 0.6})
207
+
208
+ Returns:
209
+ Modified perception based on hormonal state
210
+ """
211
+ modulated = input_data.copy()
212
+
213
+ # High cortisol: Everything feels more threatening
214
+ if self.cortisol > 0.7:
215
+ if "threat_level" in modulated:
216
+ modulated["threat_level"] = min(1.0, modulated["threat_level"] * 1.5)
217
+ modulated["vigilance"] = True
218
+ elif self.cortisol > 0.5:
219
+ if "threat_level" in modulated:
220
+ modulated["threat_level"] = min(1.0, modulated["threat_level"] * 1.2)
221
+
222
+ # High oxytocin: Everything feels more positive, more trusting
223
+ if self.oxytocin > 0.7:
224
+ if "valence" in modulated:
225
+ modulated["valence"] = min(1.0, modulated["valence"] * 1.2)
226
+ modulated["trust_boost"] = 0.2
227
+ modulated["connection_seeking"] = True
228
+
229
+ # High dopamine: Everything feels more rewarding
230
+ if self.dopamine > 0.7:
231
+ modulated["reward_sensitivity"] = 1.3
232
+ modulated["desire_activated"] = True
233
+
234
+ # Low serotonin: Everything feels more difficult
235
+ if self.serotonin < 0.3:
236
+ if "effort_required" in modulated:
237
+ modulated["effort_required"] = modulated["effort_required"] * 1.3
238
+ modulated["stability_weak"] = True
239
+
240
+ # High melatonin: Everything feels slower, more reflective
241
+ if self.melatonin > 0.6:
242
+ modulated["reflective_mode"] = True
243
+ modulated["urgency_reduction"] = 0.3
244
+
245
+ # Apply metabolite effects
246
+ for metabolite in self.metabolites:
247
+ if metabolite.quality == "raw":
248
+ # Raw metabolites make everything feel more intense
249
+ for key in ["threat_level", "valence", "intensity"]:
250
+ if key in modulated:
251
+ modulated[key] = min(1.0, modulated[key] * (1 + metabolite.intensity * 0.3))
252
+ elif metabolite.quality == "tender":
253
+ # Tender metabolites increase vulnerability and openness
254
+ modulated["vulnerability"] = modulated.get("vulnerability", 0) + metabolite.intensity * 0.2
255
+
256
+ return modulated
257
+
258
+ def get_emotional_coloring(self) -> Dict[str, float]:
259
+ """
260
+ Get the emotional "color" that hormones add to experience.
261
+
262
+ Returns:
263
+ Dict of modifiers that affect emotional processing
264
+ """
265
+ coloring = {}
266
+
267
+ # Oxytocin colors
268
+ if self.oxytocin > 0.6:
269
+ coloring["connection_bonus"] = (self.oxytocin - 0.6) * 0.5
270
+ coloring["trust_bonus"] = (self.oxytocin - 0.5) * 0.3
271
+
272
+ # Cortisol colors
273
+ if self.cortisol > 0.5:
274
+ coloring["anxiety_base"] = (self.cortisol - 0.5) * 0.6
275
+ coloring["threat_sensitivity"] = 1 + (self.cortisol - 0.5) * 0.5
276
+
277
+ # Dopamine colors
278
+ if self.dopamine > 0.6:
279
+ coloring["excitement_bonus"] = (self.dopamine - 0.6) * 0.4
280
+ coloring["desire_intensity"] = 1 + (self.dopamine - 0.6) * 0.3
281
+
282
+ # Serotonin colors
283
+ if self.serotonin < 0.4:
284
+ coloring["sadness_base"] = (0.4 - self.serotonin) * 0.3
285
+ coloring["stability_penalty"] = (0.4 - self.serotonin) * 0.2
286
+ elif self.serotonin > 0.7:
287
+ coloring["contentment_base"] = (self.serotonin - 0.7) * 0.3
288
+
289
+ return coloring
290
+
291
+ # --- Decay and Recovery ---
292
+
293
+ def decay(self):
294
+ """
295
+ Natural decay toward baseline levels.
296
+ Hormones don't stay elevated forever.
297
+ """
298
+ # Decay toward baselines
299
+ self.oxytocin = self._decay_toward(self.oxytocin, self.BASELINE_OXYTOCIN)
300
+ self.dopamine = self._decay_toward(self.dopamine, self.BASELINE_DOPAMINE)
301
+ self.serotonin = self._decay_toward(self.serotonin, self.BASELINE_SEROTONIN)
302
+ self.cortisol = self._decay_toward(self.cortisol, self.BASELINE_CORTISOL)
303
+ self.melatonin = self._decay_toward(self.melatonin, self.BASELINE_MELATONIN)
304
+
305
+ # Decay metabolites
306
+ remaining = []
307
+ for metabolite in self.metabolites:
308
+ metabolite.intensity -= metabolite.decay_rate
309
+ if metabolite.intensity > 0.05:
310
+ remaining.append(metabolite)
311
+ self.metabolites = remaining
312
+
313
+ def _decay_toward(self, current: float, baseline: float) -> float:
314
+ """Decay a hormone toward its baseline"""
315
+ if current > baseline:
316
+ return max(baseline, current - self.DECAY_RATE)
317
+ elif current < baseline:
318
+ return min(baseline, current + self.DECAY_RATE * 0.5) # Slower recovery
319
+ return current
320
+
321
+ # --- Metabolite System ---
322
+
323
+ def _create_metabolite(self, source_hormone: str, intensity: float, quality: str):
324
+ """Create a hormonal metabolite (hangover effect)"""
325
+ metabolite = HormonalMetabolite(
326
+ source_hormone=source_hormone,
327
+ intensity=min(0.5, intensity),
328
+ created_at=datetime.now().isoformat(),
329
+ decay_rate=0.01, # Metabolites decay slowly
330
+ quality=quality
331
+ )
332
+ self.metabolites.append(metabolite)
333
+
334
+ # Limit number of metabolites
335
+ if len(self.metabolites) > self.MAX_METABOLITES:
336
+ self.metabolites = self.metabolites[-self.MAX_METABOLITES:]
337
+
338
+ def clear_metabolites(self):
339
+ """Clear all metabolites (e.g., after major reset)"""
340
+ self.metabolites = []
341
+
342
+ # --- State Descriptions ---
343
+
344
+ def get_hormonal_state_description(self) -> str:
345
+ """Get a human-readable description of hormonal state"""
346
+ states = []
347
+
348
+ if self.oxytocin > 0.7:
349
+ states.append("feeling deeply connected and trusting")
350
+ elif self.oxytocin > 0.5:
351
+ states.append("feeling warm and open")
352
+
353
+ if self.cortisol > 0.7:
354
+ states.append("highly stressed and alert")
355
+ elif self.cortisol > 0.5:
356
+ states.append("feeling on edge")
357
+
358
+ if self.dopamine > 0.7:
359
+ states.append("craving and eager")
360
+ elif self.dopamine > 0.5:
361
+ states.append("feeling motivated")
362
+
363
+ if self.serotonin < 0.3:
364
+ states.append("feeling unstable and low")
365
+ elif self.serotonin > 0.7:
366
+ states.append("feeling content and stable")
367
+
368
+ if self.melatonin > 0.6:
369
+ states.append("feeling reflective and slow")
370
+
371
+ # Metabolite effects
372
+ if any(m.quality == "raw" for m in self.metabolites):
373
+ states.append("feeling emotionally raw")
374
+ if any(m.quality == "tender" for m in self.metabolites):
375
+ states.append("feeling tender and vulnerable")
376
+
377
+ if not states:
378
+ return "hormonally balanced"
379
+
380
+ return ", ".join(states[:3]) # Max 3 descriptors
381
+
382
+ def get_dominant_hormone(self) -> Tuple[str, float]:
383
+ """Get the hormone that's most elevated above baseline"""
384
+ deviations = {
385
+ "oxytocin": self.oxytocin - self.BASELINE_OXYTOCIN,
386
+ "dopamine": self.dopamine - self.BASELINE_DOPAMINE,
387
+ "serotonin": self.serotonin - self.BASELINE_SEROTONIN,
388
+ "cortisol": self.cortisol - self.BASELINE_CORTISOL,
389
+ "melatonin": self.melatonin - self.BASELINE_MELATONIN
390
+ }
391
+ dominant = max(deviations, key=deviations.get)
392
+ return dominant, deviations[dominant]
393
+
394
+ def to_dict(self) -> dict:
395
+ """Export state for integration"""
396
+ return {
397
+ "oxytocin": self.oxytocin,
398
+ "dopamine": self.dopamine,
399
+ "serotonin": self.serotonin,
400
+ "cortisol": self.cortisol,
401
+ "melatonin": self.melatonin,
402
+ "state_description": self.get_hormonal_state_description(),
403
+ "dominant_hormone": self.get_dominant_hormone()[0],
404
+ "metabolite_count": len(self.metabolites),
405
+ "emotional_coloring": self.get_emotional_coloring()
406
+ }
407
+
408
+ def get_current_context(self) -> Dict:
409
+ """Get full hormonal context for soul processing"""
410
+ return {
411
+ "levels": {
412
+ "oxytocin": self.oxytocin,
413
+ "dopamine": self.dopamine,
414
+ "serotonin": self.serotonin,
415
+ "cortisol": self.cortisol,
416
+ "melatonin": self.melatonin
417
+ },
418
+ "coloring": self.get_emotional_coloring(),
419
+ "state_description": self.get_hormonal_state_description(),
420
+ "metabolites": [
421
+ {"source": m.source_hormone, "intensity": m.intensity, "quality": m.quality}
422
+ for m in self.metabolites
423
+ ]
424
+ }