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/core.py
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""Heart: Core - Main Heart class, minimal coordinator with Soul Architecture"""
|
|
2
|
+
from .emotional_state import EmotionalState
|
|
3
|
+
from .emotional_decay import EmotionalDecay
|
|
4
|
+
from .triggers import Triggers
|
|
5
|
+
from .complex_emotions import ComplexEmotions
|
|
6
|
+
from .emotional_memory import EmotionalMemory
|
|
7
|
+
from .emotional_variability import EmotionalVariability
|
|
8
|
+
from .love import AttachmentSystem
|
|
9
|
+
from .soul import SoulOrchestrator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Heart:
|
|
13
|
+
def __init__(self, nervous, config):
|
|
14
|
+
self.nervous, self.config = nervous, config
|
|
15
|
+
self.emotion = EmotionalState(config.personality if config else None)
|
|
16
|
+
self.variability = EmotionalVariability()
|
|
17
|
+
self.decay = EmotionalDecay(self.emotion, self.variability)
|
|
18
|
+
self.triggers, self.complex = Triggers(), ComplexEmotions()
|
|
19
|
+
self.memory, self.attachment = EmotionalMemory(), AttachmentSystem()
|
|
20
|
+
|
|
21
|
+
# Soul Architecture - The seven pillars of genuine emotion
|
|
22
|
+
self.soul = SoulOrchestrator()
|
|
23
|
+
|
|
24
|
+
self._prev_state = {}
|
|
25
|
+
nervous.on("timer_tick", self._on_tick)
|
|
26
|
+
|
|
27
|
+
def _get_rate(self, key: str, default: float = 0.5) -> float:
|
|
28
|
+
"""Get emotion rate from settings (0-100% -> 0.0-1.0)"""
|
|
29
|
+
from core.settings import get_percent
|
|
30
|
+
return get_percent(f"EMOTION_RATE_{key}", int(default * 100))
|
|
31
|
+
|
|
32
|
+
def _get_boost(self, key: str, default: float = 1.0) -> float:
|
|
33
|
+
"""Get trigger boost multiplier (0-200% -> 0.0-2.0)"""
|
|
34
|
+
from core.settings import get_percent
|
|
35
|
+
return get_percent(f"TRIGGER_BOOST_{key}", int(default * 100))
|
|
36
|
+
|
|
37
|
+
def _on_tick(self, data):
|
|
38
|
+
self.decay.decay(); self.decay.tick()
|
|
39
|
+
self.emotion.arousal = max(0, min(1, self.emotion.arousal + self.variability.get_organic_tick()))
|
|
40
|
+
self.complex.decay(); self._sync_complex()
|
|
41
|
+
self.variability.clear_old_history(); self.emotion.save()
|
|
42
|
+
|
|
43
|
+
# Soul architecture tick - decay and natural processes
|
|
44
|
+
self.soul.tick()
|
|
45
|
+
|
|
46
|
+
# Emit soul state to nervous system for WebUI updates
|
|
47
|
+
import asyncio
|
|
48
|
+
try:
|
|
49
|
+
loop = asyncio.get_running_loop()
|
|
50
|
+
loop.create_task(self.nervous.emit("soul_tick", self.soul.get_state_summary()))
|
|
51
|
+
except RuntimeError:
|
|
52
|
+
pass # No running event loop, skip WebUI update
|
|
53
|
+
|
|
54
|
+
def _sync_complex(self):
|
|
55
|
+
e, c = self.emotion, self.complex
|
|
56
|
+
e.guilt, e.pride = c.guilt.value, c.pride.value
|
|
57
|
+
e.jealousy, e.embarrassment, e.anticipation = c.jealousy.value, c.embarrassment.value, c.anticipation.value
|
|
58
|
+
|
|
59
|
+
def _process_triggers(self, msg: str) -> int:
|
|
60
|
+
e, t, v = self.emotion, self.triggers, self.variability
|
|
61
|
+
|
|
62
|
+
# Get configurable rates
|
|
63
|
+
expressive_rate = self._get_rate("SEXY", 0.5)
|
|
64
|
+
desire_rate = self._get_rate("DESIRE", 0.5)
|
|
65
|
+
love_rate = self._get_rate("LOVE", 0.5)
|
|
66
|
+
arousal_rate = self._get_rate("AROUSAL", 0.5)
|
|
67
|
+
joy_rate = self._get_rate("JOY", 0.5)
|
|
68
|
+
esc_rate = self._get_rate("ESCALATION", 0.5)
|
|
69
|
+
flirty_rate = self._get_rate("FLIRTY", 0.5)
|
|
70
|
+
romantic_rate = self._get_rate("ROMANTIC", 0.5)
|
|
71
|
+
positive_rate = self._get_rate("POSITIVE", 0.5)
|
|
72
|
+
sadness_rate = self._get_rate("SADNESS", 0.5)
|
|
73
|
+
harsh_rate = self._get_rate("HARSH", 0.5)
|
|
74
|
+
|
|
75
|
+
# Get boost multipliers
|
|
76
|
+
intimate_boost = self._get_boost("SEXUAL", 1.0)
|
|
77
|
+
romantic_boost = self._get_boost("ROMANTIC", 1.0)
|
|
78
|
+
flirty_boost = self._get_boost("FLIRTY", 1.0)
|
|
79
|
+
positive_boost = self._get_boost("POSITIVE", 1.0)
|
|
80
|
+
negative_boost = self._get_boost("NEGATIVE", 1.0)
|
|
81
|
+
harsh_boost = self._get_boost("HARSH", 1.0)
|
|
82
|
+
|
|
83
|
+
# Intimate triggers - configurable rate
|
|
84
|
+
expressive = t.count_intimate_triggers(msg)
|
|
85
|
+
if expressive > 0:
|
|
86
|
+
base = 0.18 * expressive_rate * intimate_boost
|
|
87
|
+
boost = base * expressive
|
|
88
|
+
e.desire = min(1.0, e.desire + boost * v.get_inertia_modifier("desire", e.desire))
|
|
89
|
+
e.arousal = min(1.0, e.arousal + boost * 0.8 * arousal_rate)
|
|
90
|
+
e.love = min(1.0, e.love + 0.05 * love_rate)
|
|
91
|
+
v.add_momentum("desire", boost)
|
|
92
|
+
print(f"[Heart] Expressive triggers: {expressive}, desire boost: +{boost:.2f}")
|
|
93
|
+
|
|
94
|
+
# Escalation words
|
|
95
|
+
esc = Triggers.count_matches(msg, t.ESCALATION_WORDS)
|
|
96
|
+
if esc > 0:
|
|
97
|
+
base = 0.22 * esc_rate
|
|
98
|
+
e.desire = min(1.0, e.desire + base * esc)
|
|
99
|
+
e.arousal = min(1.0, e.arousal + base * 0.9 * esc * arousal_rate)
|
|
100
|
+
|
|
101
|
+
# Flirty words
|
|
102
|
+
fl = Triggers.count_matches(msg, t.FLIRTY_WORDS)
|
|
103
|
+
if fl > 0:
|
|
104
|
+
base = 0.12 * flirty_rate * flirty_boost
|
|
105
|
+
e.arousal = min(1.0, e.arousal + base * fl)
|
|
106
|
+
e.desire = min(1.0, e.desire + 0.08 * flirty_rate * fl)
|
|
107
|
+
|
|
108
|
+
# Romantic words
|
|
109
|
+
rom = Triggers.count_matches(msg, t.ROMANTIC_WORDS)
|
|
110
|
+
if rom > 0:
|
|
111
|
+
base = 0.15 * romantic_rate * romantic_boost
|
|
112
|
+
e.love = min(1.0, e.love + base * rom)
|
|
113
|
+
e.joy = min(1.0, e.joy + base * rom * 0.7 * joy_rate)
|
|
114
|
+
v.add_momentum("love", base * rom)
|
|
115
|
+
|
|
116
|
+
# Positive words
|
|
117
|
+
pos = Triggers.count_matches(msg, t.POSITIVE_WORDS)
|
|
118
|
+
if pos > 0:
|
|
119
|
+
base = 0.08 * positive_rate * positive_boost
|
|
120
|
+
e.joy = min(1.0, e.joy + base * pos)
|
|
121
|
+
e.love = min(1.0, e.love + 0.03 * pos * love_rate)
|
|
122
|
+
e.boredom = max(0.0, e.boredom - 0.1)
|
|
123
|
+
|
|
124
|
+
# Harsh words
|
|
125
|
+
h = Triggers.count_matches(msg, t.HARSH_WORDS)
|
|
126
|
+
n = Triggers.count_matches(msg, t.NEGATIVE_WORDS)
|
|
127
|
+
if h > 0:
|
|
128
|
+
base = 0.15 * harsh_rate * harsh_boost
|
|
129
|
+
e.love = max(0.0, e.love - base * h)
|
|
130
|
+
e.sadness = min(1.0, e.sadness + 0.3 * sadness_rate)
|
|
131
|
+
e.joy = max(0.0, e.joy - 0.2)
|
|
132
|
+
e.desire = max(0.0, e.desire - 0.3)
|
|
133
|
+
e.anger = min(1.0, e.anger + 0.2 * harsh_rate)
|
|
134
|
+
elif n > 0:
|
|
135
|
+
base = 0.1 * negative_boost
|
|
136
|
+
e.sadness = min(1.0, e.sadness + base * n * sadness_rate)
|
|
137
|
+
e.desire = max(0.0, e.desire - base * n)
|
|
138
|
+
|
|
139
|
+
return expressive
|
|
140
|
+
|
|
141
|
+
def react(self, text: str) -> dict:
|
|
142
|
+
msg, e = text.lower(), self.emotion
|
|
143
|
+
self._prev_state, expressive = self.get_state(), self._process_triggers(msg)
|
|
144
|
+
ch = self.complex.process(text)
|
|
145
|
+
if "jealousy" in ch and e.jealousy > 0.5: e.sadness = min(1.0, e.sadness + 0.1)
|
|
146
|
+
if "anticipation" in ch: e.arousal = min(1.0, e.arousal + 0.05)
|
|
147
|
+
self._sync_complex()
|
|
148
|
+
if e.arousal > 0.6: e.love = min(1.0, e.love + 0.01)
|
|
149
|
+
if e.love > 0.5 and expressive > 0: e.desire = min(1.0, e.desire + 0.05 * expressive)
|
|
150
|
+
if e.boredom > 0.7: e.arousal = max(0.1, e.arousal - 0.05)
|
|
151
|
+
self.memory.check_peaks(self.get_state(), self._prev_state, text[:50])
|
|
152
|
+
self.attachment.interact(e.joy > 0.4 or e.love > 0.4, max(e.joy, e.love, e.desire))
|
|
153
|
+
|
|
154
|
+
# Process through Soul Architecture for genuine emotional experience
|
|
155
|
+
soul_experience = self._process_soul(text, e)
|
|
156
|
+
|
|
157
|
+
e.save()
|
|
158
|
+
ctx = self.memory.get_mood_context()
|
|
159
|
+
s = self.attachment.status
|
|
160
|
+
narrative = (f"Recently: {ctx}. " if ctx else "") + (f"Relationship: {s.replace('_', ' ')}." if s != "stranger" else "")
|
|
161
|
+
|
|
162
|
+
# Combine traditional emotional state with soul dimensions
|
|
163
|
+
return {"valence": e.valence, "arousal": e.arousal, "desire": e.desire,
|
|
164
|
+
"joy": e.joy, "love": e.love, "sadness": e.sadness,
|
|
165
|
+
"guilt": e.guilt, "pride": e.pride,
|
|
166
|
+
"jealousy": e.jealousy, "embarrassment": e.embarrassment,
|
|
167
|
+
"anticipation": e.anticipation, "is_high_desire": e.is_high_desire, "is_in_love": e.is_in_love,
|
|
168
|
+
"is_jealous": e.is_jealous, "mood": e.mood_description,
|
|
169
|
+
"emotional_narrative": narrative.strip(), "attachment_status": s,
|
|
170
|
+
"interaction_count": self.attachment.interactions,
|
|
171
|
+
# Soul architecture dimensions
|
|
172
|
+
"soul_integrity": soul_experience.get("integrity", {}),
|
|
173
|
+
"soul_hormonal": soul_experience.get("hormonal", {}),
|
|
174
|
+
"soul_somatic": soul_experience.get("somatic", ""),
|
|
175
|
+
"soul_conflicts": soul_experience.get("conflicts", []),
|
|
176
|
+
"soul_vulnerability": soul_experience.get("vulnerability", 0.0),
|
|
177
|
+
"soul_experience": soul_experience.get("description", ""),
|
|
178
|
+
"response_tendency": soul_experience.get("response_tendency", "neutral")}
|
|
179
|
+
|
|
180
|
+
def _process_soul(self, text: str, emotion_state) -> dict:
|
|
181
|
+
"""
|
|
182
|
+
Process text through the Soul Architecture for genuine emotional experience.
|
|
183
|
+
|
|
184
|
+
This is where emotions become REAL rather than simulated - integrating
|
|
185
|
+
all seven pillars: integrity, hormones, somatics, unconscious,
|
|
186
|
+
scars, conflicts, and predictions.
|
|
187
|
+
"""
|
|
188
|
+
# Prepare input for soul processing
|
|
189
|
+
input_data = {
|
|
190
|
+
"text": text,
|
|
191
|
+
"joy": emotion_state.joy,
|
|
192
|
+
"love": emotion_state.love,
|
|
193
|
+
"sadness": emotion_state.sadness,
|
|
194
|
+
"anger": emotion_state.anger,
|
|
195
|
+
"fear": emotion_state.fear,
|
|
196
|
+
"desire": emotion_state.desire,
|
|
197
|
+
# Detect interaction type from text
|
|
198
|
+
"affirmation": any(w in text.lower() for w in ["love you", "beautiful", "amazing", "wonderful", "perfect"]),
|
|
199
|
+
"rejection": any(w in text.lower() for w in ["don't want", "leave me", "not interested", "stop"]),
|
|
200
|
+
"criticism": any(w in text.lower() for w in ["wrong", "bad", "stupid", "disappointing"]),
|
|
201
|
+
"connection_active": emotion_state.love > 0.5 or "love" in text.lower()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# Process through soul orchestrator
|
|
205
|
+
experience = self.soul.process_moment(input_data)
|
|
206
|
+
|
|
207
|
+
# Determine if we should process positive or negative interaction
|
|
208
|
+
# Lowered thresholds for more responsive soul processing
|
|
209
|
+
if input_data["affirmation"] or emotion_state.joy > 0.5:
|
|
210
|
+
self.soul.process_positive_interaction(text[:50], max(emotion_state.joy, emotion_state.love) * 0.5)
|
|
211
|
+
elif input_data["rejection"] or emotion_state.sadness > 0.4:
|
|
212
|
+
self.soul.process_negative_interaction(text[:50], emotion_state.sadness * 0.5, "hurt")
|
|
213
|
+
|
|
214
|
+
# Sync soul dimensions back to emotional state
|
|
215
|
+
integrity_dict = self.soul.integrity.to_dict()
|
|
216
|
+
emotion_state.update_soul_dimensions(
|
|
217
|
+
integrity=integrity_dict.get("overall_score", emotion_state.integrity_overall),
|
|
218
|
+
vulnerability=experience.overall_vulnerability,
|
|
219
|
+
hope=experience.predictive_emotions.hope_level if hasattr(experience.predictive_emotions, 'hope_level') else emotion_state.hope,
|
|
220
|
+
dread=experience.predictive_emotions.fear_level if hasattr(experience.predictive_emotions, 'fear_level') else emotion_state.dread,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Return soul experience dimensions
|
|
224
|
+
return {
|
|
225
|
+
"integrity": self.soul.integrity.to_dict(),
|
|
226
|
+
"hormonal": self.soul.hormonal.to_dict(),
|
|
227
|
+
"somatic": experience.somatic_sensation,
|
|
228
|
+
"conflicts": [c.description for c in experience.active_conflicts[:3]],
|
|
229
|
+
"vulnerability": experience.overall_vulnerability,
|
|
230
|
+
"description": experience.experience_description,
|
|
231
|
+
"response_tendency": experience.response_tendency,
|
|
232
|
+
"valence": experience.overall_valence,
|
|
233
|
+
"arousal": experience.overall_arousal
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
def get_state(self) -> dict:
|
|
237
|
+
e = self.emotion
|
|
238
|
+
return {"valence": e.valence, "arousal": e.arousal, "desire": e.desire, "joy": e.joy,
|
|
239
|
+
"love": e.love, "sadness": e.sadness, "anger": e.anger, "boredom": e.boredom,
|
|
240
|
+
"is_high_desire": e.is_high_desire, "is_in_love": e.is_in_love, "mood": e.mood_description}
|
|
241
|
+
|
|
242
|
+
def get_reaction(self, message: str) -> str | None:
|
|
243
|
+
"""
|
|
244
|
+
Get reaction emoji based on:
|
|
245
|
+
1. USER's message intention (primary)
|
|
246
|
+
2. Her emotional state (modifier)
|
|
247
|
+
"""
|
|
248
|
+
import random
|
|
249
|
+
from core.settings import get_int
|
|
250
|
+
|
|
251
|
+
msg = message.lower()
|
|
252
|
+
e = self.emotion
|
|
253
|
+
|
|
254
|
+
# Get configurable chance
|
|
255
|
+
base_chance = get_int("REACTION_BASE_CHANCE", 15) / 100.0
|
|
256
|
+
|
|
257
|
+
# Analyze USER's message for intention
|
|
258
|
+
reaction_context = self._analyze_message_for_reaction(msg)
|
|
259
|
+
|
|
260
|
+
# Adjust chance based on message type
|
|
261
|
+
if reaction_context == "funny":
|
|
262
|
+
base_chance = min(0.6, base_chance * 3) # React more to funny stuff
|
|
263
|
+
elif reaction_context == "romantic":
|
|
264
|
+
base_chance = min(0.5, base_chance * 2.5)
|
|
265
|
+
elif reaction_context == "intimate":
|
|
266
|
+
base_chance = get_int("REACTION_HIGH_DESIRE_CHANCE", 40) / 100.0
|
|
267
|
+
elif reaction_context == "sad":
|
|
268
|
+
base_chance = get_int("REACTION_SAD_CHANCE", 35) / 100.0
|
|
269
|
+
elif reaction_context == "question":
|
|
270
|
+
base_chance = 0.08 # Rare to react to questions
|
|
271
|
+
|
|
272
|
+
# Random check
|
|
273
|
+
if random.random() > base_chance:
|
|
274
|
+
return None
|
|
275
|
+
|
|
276
|
+
# Pick emoji based on what makes sense as REACTION
|
|
277
|
+
return self._pick_reaction_emoji(reaction_context, e)
|
|
278
|
+
|
|
279
|
+
def _analyze_message_for_reaction(self, msg: str) -> str:
|
|
280
|
+
"""Analyze what kind of reaction the message deserves"""
|
|
281
|
+
# Funny/humor
|
|
282
|
+
funny_patterns = ["lol", "lmao", "haha", "hahaha", "😂", "🤣", "joking", "jk", "just kidding"]
|
|
283
|
+
if any(p in msg for p in funny_patterns):
|
|
284
|
+
return "funny"
|
|
285
|
+
|
|
286
|
+
# Romantic/sweet
|
|
287
|
+
romantic_patterns = [
|
|
288
|
+
"love you", "love u", "i love", "miss you", "miss u", "beautiful",
|
|
289
|
+
"gorgeous", "amazing", "perfect", "my heart", "so sweet", "cutie"
|
|
290
|
+
]
|
|
291
|
+
if any(p in msg for p in romantic_patterns):
|
|
292
|
+
return "romantic"
|
|
293
|
+
|
|
294
|
+
# Intimate/flirty
|
|
295
|
+
intimate_patterns = [
|
|
296
|
+
"expressive", "hot", "high_desire", "emotional", "intense", "private", "private",
|
|
297
|
+
"intense", "closeness", "body", "body", "ass", "body", "body",
|
|
298
|
+
"touch", "kiss", "bite", "spank"
|
|
299
|
+
]
|
|
300
|
+
if any(p in msg for p in intimate_patterns):
|
|
301
|
+
return "intimate"
|
|
302
|
+
|
|
303
|
+
# Sad/upset
|
|
304
|
+
sad_patterns = [
|
|
305
|
+
"sad", "upset", "depressed", "lonely", "miss you so much",
|
|
306
|
+
"bad day", "terrible", "awful", "crying", "cry", "hurt"
|
|
307
|
+
]
|
|
308
|
+
if any(p in msg for p in sad_patterns):
|
|
309
|
+
return "sad"
|
|
310
|
+
|
|
311
|
+
# Angry
|
|
312
|
+
angry_patterns = ["angry", "pissed", "furious", "hate", "annoying"]
|
|
313
|
+
if any(p in msg for p in angry_patterns):
|
|
314
|
+
return "angry"
|
|
315
|
+
|
|
316
|
+
# Compliment
|
|
317
|
+
compliment_patterns = [
|
|
318
|
+
"you're so", "you are so", "smart", "funny", "cute",
|
|
319
|
+
"incredible", "wonderful", "the best"
|
|
320
|
+
]
|
|
321
|
+
if any(p in msg for p in compliment_patterns):
|
|
322
|
+
return "compliment"
|
|
323
|
+
|
|
324
|
+
# Question
|
|
325
|
+
if "?" in msg or any(p in msg for p in ["what", "how", "why", "when", "where", "who"]):
|
|
326
|
+
return "question"
|
|
327
|
+
|
|
328
|
+
# Default
|
|
329
|
+
return "neutral"
|
|
330
|
+
|
|
331
|
+
def _pick_reaction_emoji(self, context: str, emotion) -> str | None:
|
|
332
|
+
"""Pick appropriate emoji based on context and emotion"""
|
|
333
|
+
import random
|
|
334
|
+
|
|
335
|
+
# Context-based reactions (what user said)
|
|
336
|
+
if context == "funny":
|
|
337
|
+
options = ["😂", "🤣", "😆", "😹"]
|
|
338
|
+
if emotion.love > 0.5:
|
|
339
|
+
options.extend(["🥰", "😍"])
|
|
340
|
+
return random.choice(options)
|
|
341
|
+
|
|
342
|
+
elif context == "romantic":
|
|
343
|
+
options = ["❤️", "💕", "💗", "🥰", "😍", "💖"]
|
|
344
|
+
if emotion.desire > 0.6:
|
|
345
|
+
options.extend(["🔥", "😈"])
|
|
346
|
+
return random.choice(options)
|
|
347
|
+
|
|
348
|
+
elif context == "intimate":
|
|
349
|
+
if emotion.desire > 0.5 or emotion.arousal > 0.5:
|
|
350
|
+
options = ["🔥", "😈", "💋", "🌶️", "😏"]
|
|
351
|
+
else:
|
|
352
|
+
options = ["😏", "💋"] # More reserved if not in mood
|
|
353
|
+
return random.choice(options)
|
|
354
|
+
|
|
355
|
+
elif context == "sad":
|
|
356
|
+
# Comfort reactions - NOT sad emojis (we're comforting them)
|
|
357
|
+
options = ["🤗", "❤️", "💕", "💔", "😘"]
|
|
358
|
+
return random.choice(options)
|
|
359
|
+
|
|
360
|
+
elif context == "angry":
|
|
361
|
+
# Soothing or worried
|
|
362
|
+
if emotion.love > 0.5:
|
|
363
|
+
options = ["❤️", "🤗"] # Comfort
|
|
364
|
+
else:
|
|
365
|
+
return None # Skip reaction to anger
|
|
366
|
+
return random.choice(options) if options else None
|
|
367
|
+
|
|
368
|
+
elif context == "compliment":
|
|
369
|
+
options = ["🥰", "😊", "💕", "😄", "😘"]
|
|
370
|
+
return random.choice(options)
|
|
371
|
+
|
|
372
|
+
elif context == "question":
|
|
373
|
+
return None # Usually don't react to questions
|
|
374
|
+
|
|
375
|
+
# Neutral - use her emotional state
|
|
376
|
+
else:
|
|
377
|
+
if emotion.is_high_desire or emotion.desire > 0.7:
|
|
378
|
+
return random.choice(["🔥", "😈", "💋"])
|
|
379
|
+
elif emotion.love > 0.6:
|
|
380
|
+
return random.choice(["❤️", "🥰", "💕"])
|
|
381
|
+
elif emotion.joy > 0.6:
|
|
382
|
+
return random.choice(["😊", "😄", "🥰"])
|
|
383
|
+
elif emotion.sadness > 0.6:
|
|
384
|
+
# Don't spam sad reactions for no reason
|
|
385
|
+
return None
|
|
386
|
+
else:
|
|
387
|
+
return random.choice(["❤️", "👍", "😊"])
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
EmotionalDecay: Decay logic and baseline management
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class EmotionalDecay:
|
|
7
|
+
"""Handles emotional decay and natural changes"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, emotion_state, variability=None):
|
|
10
|
+
self.e = emotion_state
|
|
11
|
+
self.variability = variability
|
|
12
|
+
|
|
13
|
+
def set_variability(self, variability):
|
|
14
|
+
"""Set variability module for organic randomness"""
|
|
15
|
+
self.variability = variability
|
|
16
|
+
|
|
17
|
+
def decay(self):
|
|
18
|
+
"""Natural return to baseline - LOVE IS STICKY AT HIGH LEVELS"""
|
|
19
|
+
for emo, base in self.e.baseline.items():
|
|
20
|
+
curr = getattr(self.e, emo)
|
|
21
|
+
|
|
22
|
+
# LOVE: Very slow decay at high levels (sticky love)
|
|
23
|
+
if emo == "love":
|
|
24
|
+
if curr >= 0.9:
|
|
25
|
+
rate = 0.001 # Almost no decay at max
|
|
26
|
+
elif curr >= 0.7:
|
|
27
|
+
rate = 0.005 # Very slow at high
|
|
28
|
+
elif curr >= 0.5:
|
|
29
|
+
rate = 0.01 # Slow at moderate
|
|
30
|
+
else:
|
|
31
|
+
rate = 0.03 # Normal at low
|
|
32
|
+
elif emo == "desire":
|
|
33
|
+
rate = 0.02 # Faster decay
|
|
34
|
+
else:
|
|
35
|
+
rate = 0.05 # Standard decay
|
|
36
|
+
|
|
37
|
+
# Add organic randomness if variability provided
|
|
38
|
+
if self.variability:
|
|
39
|
+
if self.variability.should_skip_decay(emo, curr):
|
|
40
|
+
continue
|
|
41
|
+
rate = self.variability.randomize_decay(rate)
|
|
42
|
+
|
|
43
|
+
new = curr + (base - curr) * rate
|
|
44
|
+
setattr(self.e, emo, max(0, min(1, new)))
|
|
45
|
+
|
|
46
|
+
def tick(self):
|
|
47
|
+
"""Minute tick for natural changes"""
|
|
48
|
+
# Desire slowly fades
|
|
49
|
+
self.e.desire = max(0.0, self.e.desire - 0.002)
|
|
50
|
+
|
|
51
|
+
# Boredom increases if low arousal
|
|
52
|
+
if self.e.arousal < 0.3:
|
|
53
|
+
self.e.boredom = min(1.0, self.e.boredom + 0.01)
|
|
54
|
+
else:
|
|
55
|
+
self.e.boredom = max(0.0, self.e.boredom - 0.005)
|
|
56
|
+
|
|
57
|
+
# Love slowly creeps up if in good relationship
|
|
58
|
+
if self.e.joy > 0.6 and self.e.love > 0.3:
|
|
59
|
+
self.e.love = min(1.0, self.e.love + 0.001)
|