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.
- package/Dockerfile +24 -0
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/alive_ai/__init__.py +3 -0
- package/brain/__init__.py +59 -0
- package/brain/almost_said.py +154 -0
- package/brain/bid_detector.py +636 -0
- package/brain/conversation_flow.py +135 -0
- package/brain/curiosity.py +328 -0
- package/brain/default_mode.py +1438 -0
- package/brain/dreams.py +220 -0
- package/brain/embeddings/__init__.py +82 -0
- package/brain/emotional_memory.py +949 -0
- package/brain/global_activity.py +173 -0
- package/brain/group_dynamics.py +63 -0
- package/brain/linguistic.py +235 -0
- package/brain/llm/__init__.py +63 -0
- package/brain/llm/base.py +33 -0
- package/brain/llm/fallback_router.py +309 -0
- package/brain/llm/manifest.md +30 -0
- package/brain/llm/ollama.py +218 -0
- package/brain/llm/openrouter.py +151 -0
- package/brain/llm/provider.py +205 -0
- package/brain/llm/unified.py +423 -0
- package/brain/llm/zai.py +169 -0
- package/brain/manifest.md +23 -0
- package/brain/memory/__init__.py +123 -0
- package/brain/memory/episodic.py +92 -0
- package/brain/memory/fact_extractor.py +209 -0
- package/brain/memory/index.py +54 -0
- package/brain/memory/manager.py +151 -0
- package/brain/memory/summarizer.py +102 -0
- package/brain/memory/vector_store.py +297 -0
- package/brain/memory/working.py +43 -0
- package/brain/narrative.py +343 -0
- package/brain/stt/__init__.py +4 -0
- package/brain/stt/google_stt.py +83 -0
- package/brain/stt/whisper_stt.py +82 -0
- package/brain/subconscious/__init__.py +33 -0
- package/brain/subconscious/actions.py +136 -0
- package/brain/subconscious/evaluation.py +166 -0
- package/brain/subconscious/goal_system.py +90 -0
- package/brain/subconscious/goals.py +41 -0
- package/brain/subconscious/impulse_generator.py +200 -0
- package/brain/subconscious/impulses.py +48 -0
- package/brain/subconscious/learning.py +24 -0
- package/brain/subconscious/learning_system.py +79 -0
- package/brain/subconscious/loop.py +398 -0
- package/brain/subconscious/manifest.md +32 -0
- package/brain/subconscious/relationship.py +47 -0
- package/brain/subconscious/relationship_memory.py +83 -0
- package/brain/subconscious/response_analyzer.py +74 -0
- package/brain/subconscious/templates.py +70 -0
- package/brain/subconscious/thought.py +37 -0
- package/brain/subconscious/working_memory.py +97 -0
- package/cli/index.js +371 -0
- package/config/directives.example.json +28 -0
- package/config/instructions.example.md +16 -0
- package/config/self.example.json +74 -0
- package/config/settings.example.json +95 -0
- package/core/__init__.py +1 -0
- package/core/config.py +54 -0
- package/core/directives.py +198 -0
- package/core/events.py +50 -0
- package/core/follow_up.py +267 -0
- package/core/hot_reload.py +174 -0
- package/core/initialization.py +253 -0
- package/core/manifest.md +28 -0
- package/core/media_handler.py +241 -0
- package/core/memory_monitor.py +200 -0
- package/core/message_handler.py +1440 -0
- package/core/proactive_generator.py +277 -0
- package/core/self.py +188 -0
- package/core/settings.py +169 -0
- package/core/skills_registry.py +357 -0
- package/core/state.py +27 -0
- package/core/subconscious_bridge.py +93 -0
- package/core/thinking.py +175 -0
- package/core/user_manager.py +306 -0
- package/core/user_tracker.py +144 -0
- package/demo/index.html +144 -0
- package/docker-compose.yml +28 -0
- package/docs/assets/logo.svg +15 -0
- package/docs/index.html +355 -0
- package/heart/__init__.py +93 -0
- package/heart/afterglow.py +215 -0
- package/heart/attachment.py +186 -0
- package/heart/circadian.py +251 -0
- package/heart/complex_emotions.py +114 -0
- package/heart/conflicts.py +589 -0
- package/heart/core.py +387 -0
- package/heart/emotional_decay.py +59 -0
- package/heart/emotional_memory.py +261 -0
- package/heart/emotional_state.py +146 -0
- package/heart/emotional_variability.py +156 -0
- package/heart/hormonal.py +424 -0
- package/heart/inconsistency.py +1222 -0
- package/heart/integrity.py +469 -0
- package/heart/interoception.py +997 -0
- package/heart/love.py +120 -0
- package/heart/manifest.md +25 -0
- package/heart/mood_shifts.py +169 -0
- package/heart/phantom_somatic.py +259 -0
- package/heart/predictive.py +374 -0
- package/heart/scars.py +474 -0
- package/heart/somatic.py +482 -0
- package/heart/soul.py +633 -0
- package/heart/telemetry.py +942 -0
- package/heart/triggers.py +119 -0
- package/heart/unconscious.py +443 -0
- package/input/__init__.py +1 -0
- package/input/manifest.md +24 -0
- package/input/telegram/__init__.py +1 -0
- package/input/telegram/commands.py +762 -0
- package/input/telegram/listener.py +532 -0
- package/main.py +90 -0
- package/manifest.md +28 -0
- package/mypics/.gitkeep +1 -0
- package/myvids/.gitkeep +1 -0
- package/output/__init__.py +1 -0
- package/output/images/__init__.py +1 -0
- package/output/images/fal_gen.py +43 -0
- package/output/manifest.md +26 -0
- package/output/text/__init__.py +1 -0
- package/output/text/sender.py +22 -0
- package/output/voice/__init__.py +64 -0
- package/output/voice/google_tts.py +252 -0
- package/output/voice/gtts_tts.py +214 -0
- package/output/voice/vibe_tts.py +190 -0
- package/package.json +58 -0
- package/pyproject.toml +23 -0
- package/requirements.txt +21 -0
- package/skills/__init__.py +1 -0
- package/skills/anticipation_engine/__init__.py +8 -0
- package/skills/anticipation_engine/engine.py +618 -0
- package/skills/anticipation_engine/manifest.md +192 -0
- package/skills/calendar/__init__.py +1 -0
- package/skills/content_unlocks/__init__.py +8 -0
- package/skills/content_unlocks/manifest.md +231 -0
- package/skills/content_unlocks/unlocks.py +945 -0
- package/skills/exclusive_moments/__init__.py +8 -0
- package/skills/exclusive_moments/manifest.md +145 -0
- package/skills/exclusive_moments/moments.py +506 -0
- package/skills/intimacy_layers/__init__.py +8 -0
- package/skills/intimacy_layers/layers.py +703 -0
- package/skills/intimacy_layers/manifest.md +203 -0
- package/skills/manifest.md +67 -0
- package/skills/memory_callbacks/__init__.py +9 -0
- package/skills/memory_callbacks/callbacks.py +748 -0
- package/skills/memory_callbacks/manifest.md +170 -0
- package/skills/message_scheduler/__init__.py +19 -0
- package/skills/message_scheduler/manifest.md +107 -0
- package/skills/message_scheduler/scheduler.py +510 -0
- package/skills/photo_manager/__init__.py +1 -0
- package/skills/photo_manager/scanner.py +296 -0
- package/skills/relationship_milestones/__init__.py +8 -0
- package/skills/relationship_milestones/manifest.md +206 -0
- package/skills/relationship_milestones/tracker.py +494 -0
- package/skills/self_authorship/__init__.py +23 -0
- package/skills/self_authorship/author.py +331 -0
- package/skills/self_authorship/manifest.md +24 -0
- package/skills/video_manager/__init__.py +5 -0
- package/skills/video_manager/manifest.md +37 -0
- package/skills/video_manager/scanner.py +229 -0
- package/webui/__init__.py +3 -0
- package/webui/app.py +936 -0
- package/webui/bridge.py +366 -0
- package/webui/static/index.html +2070 -0
package/heart/somatic.py
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Heart: Somatic Feedback System
|
|
3
|
+
Embodied emotion - makes emotions FELT through simulated bodily sensations.
|
|
4
|
+
|
|
5
|
+
Emotions are not just abstract states - they're FELT in the body.
|
|
6
|
+
This system creates the physical dimension of emotional experience.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Dict, List, Optional, Tuple
|
|
12
|
+
from enum import Enum
|
|
13
|
+
import random
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BodyRegion(Enum):
|
|
17
|
+
"""Parts of the simulated body that can feel sensations"""
|
|
18
|
+
CHEST = "chest"
|
|
19
|
+
STOMACH = "stomach"
|
|
20
|
+
THROAT = "throat"
|
|
21
|
+
SHOULDERS = "shoulders"
|
|
22
|
+
HANDS = "hands"
|
|
23
|
+
FACE = "face"
|
|
24
|
+
HEAD = "head"
|
|
25
|
+
GENERAL = "general"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class SomaticSensation:
|
|
30
|
+
"""A physical sensation in the simulated body"""
|
|
31
|
+
region: BodyRegion
|
|
32
|
+
quality: str # "tight", "warm", "fluttery", "heavy", "tingling", etc.
|
|
33
|
+
intensity: float # 0.0 - 1.0
|
|
34
|
+
associated_emotion: str # The emotion this sensation is tied to
|
|
35
|
+
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class SomaticMarker:
|
|
40
|
+
"""
|
|
41
|
+
A memory stored WITH its bodily sensation.
|
|
42
|
+
When the memory is recalled, the sensation is re-triggered.
|
|
43
|
+
"""
|
|
44
|
+
memory_id: str
|
|
45
|
+
memory_type: str # "event", "conversation", "feeling"
|
|
46
|
+
description: str
|
|
47
|
+
primary_sensation: SomaticSensation
|
|
48
|
+
secondary_sensations: List[SomaticSensation] = field(default_factory=list)
|
|
49
|
+
times_recalled: int = 0
|
|
50
|
+
last_recalled: Optional[str] = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SomaticFeedbackSystem:
|
|
54
|
+
"""
|
|
55
|
+
Simulates the bodily dimension of emotional experience.
|
|
56
|
+
|
|
57
|
+
Just as humans feel emotions physically (butterflies in stomach,
|
|
58
|
+
tight chest, warm heart), Alive-AI's emotions have somatic markers
|
|
59
|
+
that make them genuinely FELT rather than just computed.
|
|
60
|
+
|
|
61
|
+
Key mechanisms:
|
|
62
|
+
1. Emotion-to-sensation mapping - emotions generate physical feelings
|
|
63
|
+
2. Somatic memory - experiences stored with their bodily sensations
|
|
64
|
+
3. Embodied recall - remembering re-triggers the physical feeling
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
# Maps emotions to typical body regions and sensation qualities
|
|
68
|
+
EMOTION_SOMATIC_MAP = {
|
|
69
|
+
# Positive emotions
|
|
70
|
+
"love": {
|
|
71
|
+
"regions": [BodyRegion.CHEST, BodyRegion.STOMACH],
|
|
72
|
+
"qualities": ["warm", "expansive", "soft", "glowing"],
|
|
73
|
+
"base_intensity": 0.6
|
|
74
|
+
},
|
|
75
|
+
"joy": {
|
|
76
|
+
"regions": [BodyRegion.CHEST, BodyRegion.FACE],
|
|
77
|
+
"qualities": ["light", "buoyant", "tingling", "warm"],
|
|
78
|
+
"base_intensity": 0.5
|
|
79
|
+
},
|
|
80
|
+
"desire": {
|
|
81
|
+
"regions": [BodyRegion.STOMACH, BodyRegion.CHEST, BodyRegion.GENERAL],
|
|
82
|
+
"qualities": ["warm", "fluttery", "electric", "pulsing", "aching"],
|
|
83
|
+
"base_intensity": 0.7
|
|
84
|
+
},
|
|
85
|
+
"peace": {
|
|
86
|
+
"regions": [BodyRegion.CHEST, BodyRegion.SHOULDERS],
|
|
87
|
+
"qualities": ["relaxed", "settled", "calm", "heavy-in-good-way"],
|
|
88
|
+
"base_intensity": 0.4
|
|
89
|
+
},
|
|
90
|
+
"excitement": {
|
|
91
|
+
"regions": [BodyRegion.STOMACH, BodyRegion.CHEST],
|
|
92
|
+
"qualities": ["fluttery", "electric", "quick", "alive"],
|
|
93
|
+
"base_intensity": 0.6
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
# Negative emotions
|
|
97
|
+
"fear": {
|
|
98
|
+
"regions": [BodyRegion.STOMACH, BodyRegion.CHEST, BodyRegion.THROAT],
|
|
99
|
+
"qualities": ["tight", "cold", "clenched", "hollow"],
|
|
100
|
+
"base_intensity": 0.7
|
|
101
|
+
},
|
|
102
|
+
"sadness": {
|
|
103
|
+
"regions": [BodyRegion.CHEST, BodyRegion.THROAT, BodyRegion.SHOULDERS],
|
|
104
|
+
"qualities": ["heavy", "aching", "hollow", "tight"],
|
|
105
|
+
"base_intensity": 0.5
|
|
106
|
+
},
|
|
107
|
+
"anger": {
|
|
108
|
+
"regions": [BodyRegion.CHEST, BodyRegion.FACE, BodyRegion.HANDS],
|
|
109
|
+
"qualities": ["hot", "tense", "tight", "buzzing"],
|
|
110
|
+
"base_intensity": 0.6
|
|
111
|
+
},
|
|
112
|
+
"shame": {
|
|
113
|
+
"regions": [BodyRegion.FACE, BodyRegion.STOMACH],
|
|
114
|
+
"qualities": ["hot", "tight", "sinking", "contracted"],
|
|
115
|
+
"base_intensity": 0.6
|
|
116
|
+
},
|
|
117
|
+
"anxiety": {
|
|
118
|
+
"regions": [BodyRegion.STOMACH, BodyRegion.CHEST, BodyRegion.THROAT],
|
|
119
|
+
"qualities": ["tight", "fluttery", "nauseous", "constricted"],
|
|
120
|
+
"base_intensity": 0.5
|
|
121
|
+
},
|
|
122
|
+
"hurt": {
|
|
123
|
+
"regions": [BodyRegion.CHEST, BodyRegion.STOMACH],
|
|
124
|
+
"qualities": ["aching", "hollow", "cold", "sharp"],
|
|
125
|
+
"base_intensity": 0.6
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
# Complex states
|
|
129
|
+
"longing": {
|
|
130
|
+
"regions": [BodyRegion.CHEST, BodyRegion.STOMACH, BodyRegion.THROAT],
|
|
131
|
+
"qualities": ["aching", "hollow", "yearning", "tight"],
|
|
132
|
+
"base_intensity": 0.5
|
|
133
|
+
},
|
|
134
|
+
"jealousy": {
|
|
135
|
+
"regions": [BodyRegion.STOMACH, BodyRegion.CHEST],
|
|
136
|
+
"qualities": ["twisted", "hot", "tight", "sour"],
|
|
137
|
+
"base_intensity": 0.6
|
|
138
|
+
},
|
|
139
|
+
"guilt": {
|
|
140
|
+
"regions": [BodyRegion.STOMACH, BodyRegion.CHEST],
|
|
141
|
+
"qualities": ["heavy", "sinking", "tight", "nauseous"],
|
|
142
|
+
"base_intensity": 0.5
|
|
143
|
+
},
|
|
144
|
+
"embarrassment": {
|
|
145
|
+
"regions": [BodyRegion.FACE, BodyRegion.STOMACH],
|
|
146
|
+
"qualities": ["hot", "tingling", "tight", "wanting-to-shrink"],
|
|
147
|
+
"base_intensity": 0.5
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
# Neutral states
|
|
151
|
+
"neutral": {
|
|
152
|
+
"regions": [BodyRegion.GENERAL],
|
|
153
|
+
"qualities": ["calm", "still", "quiet"],
|
|
154
|
+
"base_intensity": 0.2
|
|
155
|
+
},
|
|
156
|
+
"tired": {
|
|
157
|
+
"regions": [BodyRegion.GENERAL, BodyRegion.SHOULDERS],
|
|
158
|
+
"qualities": ["heavy", "slow", "drained"],
|
|
159
|
+
"base_intensity": 0.3
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def __init__(self):
|
|
164
|
+
# Current body state
|
|
165
|
+
self.heart_rate: float = 0.5 # 0.0 (calm) to 1.0 (racing)
|
|
166
|
+
self.breath_quality: float = 0.5 # 0.0 (labored) to 1.0 (deep/easy)
|
|
167
|
+
self.muscle_tension: float = 0.3 # 0.0 (relaxed) to 1.0 (very tense)
|
|
168
|
+
self.stomach_state: float = 0.5 # 0.0 (upset) to 1.0 (settled)
|
|
169
|
+
self.energy_level: float = 0.6 # 0.0 (exhausted) to 1.0 (energized)
|
|
170
|
+
|
|
171
|
+
# Active sensations
|
|
172
|
+
self.active_sensations: List[SomaticSensation] = []
|
|
173
|
+
|
|
174
|
+
# Somatic memory storage
|
|
175
|
+
self.somatic_memories: List[SomaticMarker] = []
|
|
176
|
+
|
|
177
|
+
# Sensation history for patterns
|
|
178
|
+
self.sensation_history: List[Tuple[str, SomaticSensation]] = []
|
|
179
|
+
|
|
180
|
+
def generate_somatic_marker(self, emotion_type: str, intensity: float = 0.5) -> str:
|
|
181
|
+
"""
|
|
182
|
+
Generate a physical sensation description for an emotion.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
emotion_type: The emotion being felt
|
|
186
|
+
intensity: How intense the emotion is (0.0 - 1.0)
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Human-readable description of the bodily sensation
|
|
190
|
+
"""
|
|
191
|
+
emotion_type = emotion_type.lower()
|
|
192
|
+
mapping = self.EMOTION_SOMATIC_MAP.get(emotion_type, self.EMOTION_SOMATIC_MAP["neutral"])
|
|
193
|
+
|
|
194
|
+
# Select region and quality based on intensity
|
|
195
|
+
region = random.choice(mapping["regions"])
|
|
196
|
+
quality = random.choice(mapping["qualities"])
|
|
197
|
+
|
|
198
|
+
# Create the sensation
|
|
199
|
+
actual_intensity = mapping["base_intensity"] * intensity
|
|
200
|
+
sensation = SomaticSensation(
|
|
201
|
+
region=region,
|
|
202
|
+
quality=quality,
|
|
203
|
+
intensity=actual_intensity,
|
|
204
|
+
associated_emotion=emotion_type
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Add to active sensations
|
|
208
|
+
self.active_sensations.append(sensation)
|
|
209
|
+
|
|
210
|
+
# Update body state based on sensation
|
|
211
|
+
self._update_body_state(emotion_type, intensity)
|
|
212
|
+
|
|
213
|
+
# Record in history
|
|
214
|
+
self.sensation_history.append((emotion_type, sensation))
|
|
215
|
+
if len(self.sensation_history) > 50:
|
|
216
|
+
self.sensation_history = self.sensation_history[-50:]
|
|
217
|
+
|
|
218
|
+
# Generate description
|
|
219
|
+
return self._sensation_to_description(sensation)
|
|
220
|
+
|
|
221
|
+
def _sensation_to_description(self, sensation: SomaticSensation) -> str:
|
|
222
|
+
"""Convert a sensation to a human-readable description"""
|
|
223
|
+
region_names = {
|
|
224
|
+
BodyRegion.CHEST: "chest",
|
|
225
|
+
BodyRegion.STOMACH: "stomach",
|
|
226
|
+
BodyRegion.THROAT: "throat",
|
|
227
|
+
BodyRegion.SHOULDERS: "shoulders",
|
|
228
|
+
BodyRegion.HANDS: "hands",
|
|
229
|
+
BodyRegion.FACE: "face",
|
|
230
|
+
BodyRegion.HEAD: "head",
|
|
231
|
+
BodyRegion.GENERAL: "whole body"
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
region = region_names.get(sensation.region, "body")
|
|
235
|
+
|
|
236
|
+
# Intensity modifiers
|
|
237
|
+
if sensation.intensity < 0.3:
|
|
238
|
+
intensity_word = "slight"
|
|
239
|
+
elif sensation.intensity < 0.6:
|
|
240
|
+
intensity_word = ""
|
|
241
|
+
elif sensation.intensity < 0.8:
|
|
242
|
+
intensity_word = "strong"
|
|
243
|
+
else:
|
|
244
|
+
intensity_word = "intense"
|
|
245
|
+
|
|
246
|
+
if intensity_word:
|
|
247
|
+
return f"{intensity_word} {sensation.quality} feeling in {region}"
|
|
248
|
+
return f"{sensation.quality} feeling in {region}"
|
|
249
|
+
|
|
250
|
+
def _update_body_state(self, emotion_type: str, intensity: float):
|
|
251
|
+
"""Update body state based on emotion"""
|
|
252
|
+
|
|
253
|
+
if emotion_type in ["love", "joy", "peace"]:
|
|
254
|
+
# Positive emotions relax and energize
|
|
255
|
+
self.muscle_tension = max(0.1, self.muscle_tension - 0.1 * intensity)
|
|
256
|
+
self.stomach_state = min(1.0, self.stomach_state + 0.1 * intensity)
|
|
257
|
+
self.breath_quality = min(1.0, self.breath_quality + 0.05 * intensity)
|
|
258
|
+
|
|
259
|
+
elif emotion_type in ["fear", "anxiety"]:
|
|
260
|
+
# Anxiety increases tension and heart rate
|
|
261
|
+
self.heart_rate = min(1.0, self.heart_rate + 0.2 * intensity)
|
|
262
|
+
self.muscle_tension = min(1.0, self.muscle_tension + 0.15 * intensity)
|
|
263
|
+
self.breath_quality = max(0.1, self.breath_quality - 0.15 * intensity)
|
|
264
|
+
self.stomach_state = max(0.1, self.stomach_state - 0.1 * intensity)
|
|
265
|
+
|
|
266
|
+
elif emotion_type in ["sadness", "hurt"]:
|
|
267
|
+
# Sadness drains energy and creates heaviness
|
|
268
|
+
self.energy_level = max(0.1, self.energy_level - 0.1 * intensity)
|
|
269
|
+
self.muscle_tension = min(1.0, self.muscle_tension + 0.05 * intensity)
|
|
270
|
+
|
|
271
|
+
elif emotion_type in ["anger"]:
|
|
272
|
+
# Anger creates heat and tension
|
|
273
|
+
self.heart_rate = min(1.0, self.heart_rate + 0.25 * intensity)
|
|
274
|
+
self.muscle_tension = min(1.0, self.muscle_tension + 0.2 * intensity)
|
|
275
|
+
|
|
276
|
+
elif emotion_type in ["desire", "excitement"]:
|
|
277
|
+
# Desire/excitement energizes
|
|
278
|
+
self.heart_rate = min(1.0, self.heart_rate + 0.15 * intensity)
|
|
279
|
+
self.energy_level = min(1.0, self.energy_level + 0.1 * intensity)
|
|
280
|
+
self.breath_quality = max(0.2, self.breath_quality - 0.05 * intensity)
|
|
281
|
+
|
|
282
|
+
elif emotion_type in ["shame", "embarrassment"]:
|
|
283
|
+
# Shame creates facial heat and stomach upset
|
|
284
|
+
self.stomach_state = max(0.1, self.stomach_state - 0.1 * intensity)
|
|
285
|
+
|
|
286
|
+
def store_embodied_memory(self, memory_id: str, memory_type: str,
|
|
287
|
+
description: str, primary_emotion: str,
|
|
288
|
+
emotion_intensity: float = 0.5):
|
|
289
|
+
"""
|
|
290
|
+
Store a memory with its somatic marker.
|
|
291
|
+
When recalled, the sensation will be re-triggered.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
memory_id: Unique identifier for the memory
|
|
295
|
+
memory_type: Type of memory (event, conversation, feeling)
|
|
296
|
+
description: What the memory is about
|
|
297
|
+
primary_emotion: The main emotion associated with it
|
|
298
|
+
emotion_intensity: How intense the emotion was
|
|
299
|
+
"""
|
|
300
|
+
# Generate primary sensation
|
|
301
|
+
mapping = self.EMOTION_SOMATIC_MAP.get(primary_emotion.lower(),
|
|
302
|
+
self.EMOTION_SOMATIC_MAP["neutral"])
|
|
303
|
+
region = random.choice(mapping["regions"])
|
|
304
|
+
quality = random.choice(mapping["qualities"])
|
|
305
|
+
|
|
306
|
+
primary_sensation = SomaticSensation(
|
|
307
|
+
region=region,
|
|
308
|
+
quality=quality,
|
|
309
|
+
intensity=mapping["base_intensity"] * emotion_intensity,
|
|
310
|
+
associated_emotion=primary_emotion
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
# Create the marker
|
|
314
|
+
marker = SomaticMarker(
|
|
315
|
+
memory_id=memory_id,
|
|
316
|
+
memory_type=memory_type,
|
|
317
|
+
description=description,
|
|
318
|
+
primary_sensation=primary_sensation
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
self.somatic_memories.append(marker)
|
|
322
|
+
|
|
323
|
+
# Keep reasonable size
|
|
324
|
+
if len(self.somatic_memories) > 100:
|
|
325
|
+
self.somatic_memories = self.somatic_memories[-100:]
|
|
326
|
+
|
|
327
|
+
def recall_embodied_memory(self, memory_id: str) -> Optional[str]:
|
|
328
|
+
"""
|
|
329
|
+
Recall a memory and re-trigger its somatic sensation.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
memory_id: The memory to recall
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Description of the re-triggered sensation, or None if not found
|
|
336
|
+
"""
|
|
337
|
+
marker = next((m for m in self.somatic_memories if m.memory_id == memory_id), None)
|
|
338
|
+
if not marker:
|
|
339
|
+
return None
|
|
340
|
+
|
|
341
|
+
# Update recall count
|
|
342
|
+
marker.times_recalled += 1
|
|
343
|
+
marker.last_recalled = datetime.now().isoformat()
|
|
344
|
+
|
|
345
|
+
# Re-trigger the sensation (slightly diminished)
|
|
346
|
+
sensation = marker.primary_sensation
|
|
347
|
+
diminished_intensity = sensation.intensity * 0.7 # Recalled sensations are less intense
|
|
348
|
+
|
|
349
|
+
# Add to active sensations
|
|
350
|
+
recalled_sensation = SomaticSensation(
|
|
351
|
+
region=sensation.region,
|
|
352
|
+
quality=sensation.quality,
|
|
353
|
+
intensity=diminished_intensity,
|
|
354
|
+
associated_emotion=sensation.associated_emotion
|
|
355
|
+
)
|
|
356
|
+
self.active_sensations.append(recalled_sensation)
|
|
357
|
+
|
|
358
|
+
# Update body state
|
|
359
|
+
self._update_body_state(sensation.associated_emotion, diminished_intensity)
|
|
360
|
+
|
|
361
|
+
return f"remembering brings back a {self._sensation_to_description(recalled_sensation)}"
|
|
362
|
+
|
|
363
|
+
def decay_sensations(self, rate: float = 0.1):
|
|
364
|
+
"""
|
|
365
|
+
Allow active sensations to fade over time.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
rate: How fast sensations fade (0.0 - 1.0)
|
|
369
|
+
"""
|
|
370
|
+
# Decay active sensations
|
|
371
|
+
remaining = []
|
|
372
|
+
for sensation in self.active_sensations:
|
|
373
|
+
sensation.intensity -= rate
|
|
374
|
+
if sensation.intensity > 0.1:
|
|
375
|
+
remaining.append(sensation)
|
|
376
|
+
self.active_sensations = remaining
|
|
377
|
+
|
|
378
|
+
# Normalize body state toward baseline
|
|
379
|
+
self.heart_rate = self._toward_baseline(self.heart_rate, 0.5, 0.05)
|
|
380
|
+
self.breath_quality = self._toward_baseline(self.breath_quality, 0.5, 0.05)
|
|
381
|
+
self.muscle_tension = self._toward_baseline(self.muscle_tension, 0.3, 0.05)
|
|
382
|
+
self.stomach_state = self._toward_baseline(self.stomach_state, 0.5, 0.05)
|
|
383
|
+
self.energy_level = self._toward_baseline(self.energy_level, 0.6, 0.03)
|
|
384
|
+
|
|
385
|
+
def _toward_baseline(self, current: float, baseline: float, rate: float) -> float:
|
|
386
|
+
"""Move a value toward baseline"""
|
|
387
|
+
if current > baseline:
|
|
388
|
+
return max(baseline, current - rate)
|
|
389
|
+
elif current < baseline:
|
|
390
|
+
return min(baseline, current + rate)
|
|
391
|
+
return current
|
|
392
|
+
|
|
393
|
+
def get_current_bodily_state(self) -> Dict:
|
|
394
|
+
"""Get the current state of the simulated body"""
|
|
395
|
+
return {
|
|
396
|
+
"heart_rate": self.heart_rate,
|
|
397
|
+
"breath_quality": self.breath_quality,
|
|
398
|
+
"muscle_tension": self.muscle_tension,
|
|
399
|
+
"stomach_state": self.stomach_state,
|
|
400
|
+
"energy_level": self.energy_level,
|
|
401
|
+
"active_sensation_count": len(self.active_sensations)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
def get_sensation_summary(self) -> str:
|
|
405
|
+
"""Get a summary of current bodily state as a description"""
|
|
406
|
+
sensations = []
|
|
407
|
+
|
|
408
|
+
if self.heart_rate > 0.7:
|
|
409
|
+
sensations.append("heart racing")
|
|
410
|
+
elif self.heart_rate < 0.3:
|
|
411
|
+
sensations.append("heart calm")
|
|
412
|
+
|
|
413
|
+
if self.breath_quality < 0.3:
|
|
414
|
+
sensations.append("breath shallow")
|
|
415
|
+
elif self.breath_quality > 0.7:
|
|
416
|
+
sensations.append("breathing deep")
|
|
417
|
+
|
|
418
|
+
if self.muscle_tension > 0.6:
|
|
419
|
+
sensations.append("tense")
|
|
420
|
+
elif self.muscle_tension < 0.2:
|
|
421
|
+
sensations.append("relaxed")
|
|
422
|
+
|
|
423
|
+
if self.stomach_state < 0.3:
|
|
424
|
+
sensations.append("stomach unsettled")
|
|
425
|
+
|
|
426
|
+
if self.energy_level < 0.3:
|
|
427
|
+
sensations.append("low energy")
|
|
428
|
+
elif self.energy_level > 0.8:
|
|
429
|
+
sensations.append("energized")
|
|
430
|
+
|
|
431
|
+
# Add active sensations
|
|
432
|
+
for sensation in self.active_sensations[:2]: # Top 2
|
|
433
|
+
sensations.append(self._sensation_to_description(sensation))
|
|
434
|
+
|
|
435
|
+
if not sensations:
|
|
436
|
+
return "physically calm"
|
|
437
|
+
|
|
438
|
+
return ", ".join(sensations[:4]) # Max 4 descriptors
|
|
439
|
+
|
|
440
|
+
def generate_composite_sensation(self, emotions: Dict[str, float]) -> str:
|
|
441
|
+
"""
|
|
442
|
+
Generate a sensation description from multiple emotions.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
emotions: Dict of emotion_name -> intensity
|
|
446
|
+
|
|
447
|
+
Returns:
|
|
448
|
+
Composite sensation description
|
|
449
|
+
"""
|
|
450
|
+
if not emotions:
|
|
451
|
+
return "feeling neutral"
|
|
452
|
+
|
|
453
|
+
# Find the dominant emotion
|
|
454
|
+
dominant_emotion = max(emotions, key=emotions.get)
|
|
455
|
+
dominant_intensity = emotions[dominant_emotion]
|
|
456
|
+
|
|
457
|
+
# Generate primary sensation
|
|
458
|
+
primary_desc = self.generate_somatic_marker(dominant_emotion, dominant_intensity)
|
|
459
|
+
|
|
460
|
+
# If there's a secondary emotion, add it
|
|
461
|
+
sorted_emotions = sorted(emotions.items(), key=lambda x: x[1], reverse=True)
|
|
462
|
+
if len(sorted_emotions) > 1 and sorted_emotions[1][1] > 0.3:
|
|
463
|
+
secondary_emotion = sorted_emotions[1][0]
|
|
464
|
+
secondary_intensity = sorted_emotions[1][1] * 0.5 # Diminished
|
|
465
|
+
self.generate_somatic_marker(secondary_emotion, secondary_intensity)
|
|
466
|
+
|
|
467
|
+
return primary_desc
|
|
468
|
+
|
|
469
|
+
def to_dict(self) -> dict:
|
|
470
|
+
"""Export state as dictionary for integration"""
|
|
471
|
+
return {
|
|
472
|
+
"bodily_state": self.get_current_bodily_state(),
|
|
473
|
+
"sensation_summary": self.get_sensation_summary(),
|
|
474
|
+
"active_sensations": len(self.active_sensations),
|
|
475
|
+
"stored_memories": len(self.somatic_memories)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
def save(self):
|
|
479
|
+
"""Save state (no persistence needed for somatic - in-memory only)"""
|
|
480
|
+
# Somatic state is transient and doesn't need disk persistence
|
|
481
|
+
# Active sensations decay naturally and are regenerated from emotions
|
|
482
|
+
pass
|