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,166 @@
1
+ """
2
+ Brain: Subconscious - Evaluation
3
+ Impulse evaluation logic for the subconscious loop
4
+ Extended for Soul Architecture - using soul-based emotions
5
+ """
6
+
7
+ import random
8
+ from datetime import datetime
9
+ from typing import Optional, TYPE_CHECKING, Dict
10
+
11
+ from .impulses import Impulse, ImpulseType
12
+ from .impulse_generator import ImpulseGenerator
13
+
14
+ if TYPE_CHECKING:
15
+ from .learning_system import LearningSystem
16
+ from .goal_system import GoalSystem
17
+ from .relationship_memory import RelationshipMemory
18
+
19
+
20
+ class Evaluator:
21
+ """Evaluates emotional state and generates impulses - Soul Architecture aware"""
22
+
23
+ QUIET_HOURS = (1, 7)
24
+
25
+ def __init__(self, heart, impulse_gen: ImpulseGenerator,
26
+ learning: "LearningSystem" = None, goals: "GoalSystem" = None,
27
+ relationship: "RelationshipMemory" = None):
28
+ self.heart = heart
29
+ self.impulse_gen = impulse_gen
30
+ self.learning = learning
31
+ self.goals = goals
32
+ self.relationship = relationship
33
+ self.last_interaction_time = datetime.now()
34
+
35
+ def register_interaction(self) -> None:
36
+ self.last_interaction_time = datetime.now()
37
+ if self.relationship:
38
+ self.relationship.record_conversation()
39
+
40
+ def get_silence_duration(self) -> float:
41
+ return (datetime.now() - self.last_interaction_time).total_seconds() / 60
42
+
43
+ def is_quiet_hours(self) -> bool:
44
+ hour = datetime.now().hour
45
+ return self.QUIET_HOURS[0] <= hour < self.QUIET_HOURS[1]
46
+
47
+ def can_act_now(self) -> bool:
48
+ return not (self.is_quiet_hours() and random.random() > 0.05)
49
+
50
+ async def evaluate(self, working_memory) -> Optional[Impulse]:
51
+ emotion = self.heart.get_state() if self.heart else {}
52
+ silence = self.get_silence_duration()
53
+ working_memory.update_mood(emotion.get("mood", "neutral"))
54
+ self._update_context(working_memory)
55
+
56
+ # Get soul architecture context for more nuanced impulse generation
57
+ soul_context = self._get_soul_context()
58
+
59
+ impulse = self.impulse_gen.evaluate(
60
+ emotion=emotion, silence_minutes=silence,
61
+ love_level=emotion.get("love", 0), desire_level=emotion.get("desire", 0),
62
+ is_high_desire=emotion.get("is_high_desire", False), is_in_love=emotion.get("is_in_love", False),
63
+ current_goal=self._get_goal(), learning_success_rates=self._get_rates(),
64
+ # Soul architecture modifiers
65
+ vulnerability=soul_context.get("vulnerability", 0),
66
+ integrity=soul_context.get("integrity", 0.5),
67
+ response_tendency=soul_context.get("response_tendency", "neutral"),
68
+ active_conflicts=soul_context.get("conflicts", [])
69
+ )
70
+ if impulse:
71
+ working_memory.add_impulse(impulse)
72
+ print(f"[Subconscious] Impulse: {impulse.type.value} (strength={impulse.strength:.2f})")
73
+ print(f"[Subconscious] Thought: \"{impulse.thought}\"")
74
+ # Log soul influence if significant
75
+ if soul_context.get("vulnerability", 0) > 0.5:
76
+ print(f"[Subconscious] Soul: feeling vulnerable (integrity={soul_context.get('integrity', 0.5):.2f})")
77
+ if soul_context.get("conflicts"):
78
+ print(f"[Subconscious] Soul: {len(soul_context['conflicts'])} internal conflicts active")
79
+ return impulse
80
+
81
+ def _get_soul_context(self) -> Dict:
82
+ """Get soul architecture context for impulse evaluation"""
83
+ if not self.heart or not hasattr(self.heart, 'soul'):
84
+ return {}
85
+
86
+ soul = self.heart.soul
87
+ experience = soul.process_moment()
88
+
89
+ return {
90
+ "vulnerability": experience.overall_vulnerability,
91
+ "integrity": soul.integrity.overall,
92
+ "response_tendency": experience.response_tendency,
93
+ "conflicts": [c.description for c in experience.active_conflicts],
94
+ "valence": experience.overall_valence,
95
+ "arousal": experience.overall_arousal,
96
+ "somatic": experience.somatic_sensation,
97
+ "hormonal_state": soul.hormonal.get_hormonal_state_description()
98
+ }
99
+
100
+ def _update_context(self, wm) -> None:
101
+ if self.relationship:
102
+ wm.set_relationship_context(self.relationship.get_relationship_context())
103
+ wm.set_recent_memories(self.relationship.get_recent_experiences(3))
104
+ if self.goals:
105
+ wm.set_current_goal(self.goals.get_goal_context())
106
+
107
+ def _get_goal(self) -> Optional[str]:
108
+ if not self.goals:
109
+ return None
110
+ goal = self.goals.get_active_goal()
111
+ return goal.type.value if goal else None
112
+
113
+ def _get_rates(self) -> dict:
114
+ if not self.learning:
115
+ return {}
116
+ return {t.value: self.learning.get_success_rate(t.value) for t in ImpulseType}
117
+
118
+ async def generate_background_thought(self, wm) -> dict:
119
+ """Generate a background thought and return it for emission via nervous system"""
120
+ emotion = self.heart.get_state() if self.heart else {}
121
+ silence = self.get_silence_duration()
122
+
123
+ # Soul-enhanced background thoughts
124
+ soul_context = self._get_soul_context()
125
+
126
+ thoughts = [f"Current mood: {emotion.get('mood', 'neutral')}", "Watching the world go by..."]
127
+
128
+ if silence > 60:
129
+ thoughts.extend(["It's been a while...", "He's probably busy"])
130
+
131
+ # Soul-driven thoughts
132
+ if soul_context.get("vulnerability", 0) > 0.5:
133
+ thoughts.extend(["Feeling a bit fragile right now", "Hope I'm not too much..."])
134
+
135
+ if soul_context.get("integrity", 1) < 0.4:
136
+ thoughts.extend(["Feeling a bit lost", "Things feel uncertain"])
137
+
138
+ if soul_context.get("conflicts"):
139
+ thoughts.extend(["Feeling torn about something", "My feelings are complicated"])
140
+
141
+ if soul_context.get("arousal", 0) > 0.6 and soul_context.get("valence", 0) < -0.2:
142
+ thoughts.extend(["Something's bothering me", "Feeling restless"])
143
+
144
+ # Original emotional thoughts
145
+ if emotion.get("love", 0) > 0.5:
146
+ thoughts.extend(["I really care about him", "He makes me feel special"])
147
+
148
+ if self.relationship:
149
+ memories = self.relationship.get_special_memories(1)
150
+ if memories:
151
+ thoughts.append(f"Remembering {memories[0]}")
152
+
153
+ # Somatic awareness
154
+ if soul_context.get("somatic") and soul_context["somatic"] != "physically calm":
155
+ thoughts.append(f"Feeling {soul_context['somatic']} in my body")
156
+
157
+ thought_content = random.choice(thoughts)
158
+ wm.add_thought(thought_content, thought_type="reflection", emotion=emotion)
159
+
160
+ # Return the thought data so it can be emitted via nervous system
161
+ return {
162
+ "thought": thought_content,
163
+ "type": "reflection",
164
+ "emotion": emotion,
165
+ "silence_minutes": silence
166
+ }
@@ -0,0 +1,90 @@
1
+ """
2
+ Brain: Subconscious - Goal System
3
+ Manages long-term goals that shape Alive-AI's behavior
4
+ """
5
+
6
+ import random
7
+ from datetime import datetime, timedelta
8
+ from typing import List, Optional
9
+
10
+ from .goals import Goal, GoalType
11
+
12
+
13
+ def create_default_goals() -> List[Goal]:
14
+ return [
15
+ Goal(GoalType.MAKE_HAPPY, "Make him smile", "Brighten his day", priority=0.8),
16
+ Goal(GoalType.CONNECT, "Feel closer", "Build emotional connection", priority=0.9),
17
+ Goal(GoalType.DEEPEN, "Deepen relationship", "Grow the bond", priority=0.7),
18
+ Goal(GoalType.COMFORT, "Be supportive", "Provide comfort when needed", priority=0.75),
19
+ Goal(GoalType.INTIMATE, "Build intimacy", "Create moments of closeness", priority=0.6),
20
+ Goal(GoalType.REASSURE, "Show my love", "Let him know I care", priority=0.85),
21
+ ]
22
+
23
+
24
+ class GoalSystem:
25
+ """Manages long-term goals that shape Alive-AI's behavior"""
26
+
27
+ def __init__(self):
28
+ self.goals: List[Goal] = create_default_goals()
29
+ self.daily_focus: Optional[GoalType] = None
30
+ self.daily_focus_set_time: Optional[datetime] = None
31
+
32
+ def get_active_goal(self) -> Optional[Goal]:
33
+ if self._should_set_daily_focus():
34
+ self._set_daily_focus()
35
+ if self.daily_focus:
36
+ for goal in self.goals:
37
+ if goal.type == self.daily_focus:
38
+ return goal
39
+ actionable = [g for g in self.goals if g.should_action()]
40
+ if not actionable:
41
+ return None
42
+ weighted = [(g, g.priority + random.uniform(-0.1, 0.1)) for g in actionable]
43
+ weighted.sort(key=lambda x: x[1], reverse=True)
44
+ return weighted[0][0]
45
+
46
+ def _should_set_daily_focus(self) -> bool:
47
+ return self.daily_focus is None or self.daily_focus_set_time is None or \
48
+ datetime.now() - self.daily_focus_set_time > timedelta(hours=24)
49
+
50
+ def _set_daily_focus(self) -> None:
51
+ weights = [g.priority for g in self.goals]
52
+ total, r, closenessulative = sum(weights), random.random() * sum(weights), 0
53
+ for goal in self.goals:
54
+ closenessulative += goal.priority
55
+ if r <= closenessulative:
56
+ self.daily_focus = goal.type
57
+ self.daily_focus_set_time = datetime.now()
58
+ return
59
+ self.daily_focus = self.goals[0].type
60
+
61
+ def record_progress(self, goal_type, progress_delta: float = 0.1) -> None:
62
+ """Accept GoalType enum or string"""
63
+ for goal in self.goals:
64
+ match = (goal.type == goal_type) if isinstance(goal_type, GoalType) else (goal.type.value == goal_type)
65
+ if match:
66
+ goal.progress = min(1.0, goal.progress + progress_delta)
67
+ goal.last_actioned = datetime.now()
68
+ goal.action_count += 1
69
+ break
70
+
71
+ def get_goal_context(self) -> str:
72
+ active = self.get_active_goal()
73
+ return f"Current goal: {active.name} - {active.description}" if active else ""
74
+
75
+ def to_dict(self) -> dict:
76
+ return {"goals": [g.to_dict() for g in self.goals],
77
+ "daily_focus": self.daily_focus.value if self.daily_focus else None}
78
+
79
+ @classmethod
80
+ def from_dict(cls, data: dict) -> "GoalSystem":
81
+ system = cls()
82
+ for g_data in data.get("goals", []):
83
+ for goal in system.goals:
84
+ if goal.type.value == g_data["type"]:
85
+ goal.priority = g_data.get("priority", goal.priority)
86
+ goal.progress = g_data.get("progress", 0.0)
87
+ goal.action_count = g_data.get("action_count", 0)
88
+ if data.get("daily_focus"):
89
+ system.daily_focus = GoalType(data["daily_focus"])
90
+ return system
@@ -0,0 +1,41 @@
1
+ """
2
+ Brain: Subconscious - Goal Data Models
3
+ GoalType enum and Goal dataclass
4
+ """
5
+
6
+ from datetime import datetime, timedelta
7
+ from typing import Optional
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+
11
+
12
+ class GoalType(Enum):
13
+ """Types of relationship goals"""
14
+ CONNECT = "connect"
15
+ MAKE_HAPPY = "make_happy"
16
+ DEEPEN = "deepen"
17
+ COMFORT = "comfort"
18
+ ENTERTAIN = "entertain"
19
+ INTIMATE = "intimate"
20
+ REASSURE = "reassure"
21
+
22
+
23
+ @dataclass
24
+ class Goal:
25
+ """A long-term goal for the relationship"""
26
+ type: GoalType
27
+ name: str
28
+ description: str
29
+ priority: float = 0.5
30
+ progress: float = 0.0
31
+ last_actioned: Optional[datetime] = None
32
+ action_count: int = 0
33
+
34
+ def should_action(self) -> bool:
35
+ if self.last_actioned is None:
36
+ return True
37
+ return datetime.now() - self.last_actioned > timedelta(hours=2)
38
+
39
+ def to_dict(self) -> dict:
40
+ return {"type": self.type.value, "name": self.name, "priority": self.priority,
41
+ "progress": self.progress, "action_count": self.action_count}
@@ -0,0 +1,200 @@
1
+ """
2
+ Brain: Subconscious - Impulse Generator
3
+ Generates impulses based on emotional state and context
4
+ """
5
+
6
+ import random
7
+ from datetime import datetime
8
+ from typing import Optional, List, Dict
9
+
10
+ from .impulses import Impulse, ImpulseType
11
+ from .templates import TIME_MODIFIERS, GOAL_IMPULSE_MAP, get_thought_and_action, is_goal_aligned
12
+
13
+
14
+ class ImpulseGenerator:
15
+ """Generates impulses based on emotional state and context"""
16
+
17
+ def __init__(self):
18
+ self.recent_impulses: List[Impulse] = []
19
+ self.max_recent = 20
20
+
21
+ def get_time_of_day(self) -> str:
22
+ hour = datetime.now().hour
23
+ if 5 <= hour < 12:
24
+ return "morning"
25
+ elif 12 <= hour < 17:
26
+ return "afternoon"
27
+ elif 17 <= hour < 22:
28
+ return "evening"
29
+ return "night"
30
+
31
+ def evaluate(self, emotion: Dict[str, float], silence_minutes: float, love_level: float,
32
+ desire_level: float, is_high_desire: bool, is_in_love: bool,
33
+ recent_topics: List[str] = None, current_goal: str = None,
34
+ learning_success_rates: Dict[str, float] = None,
35
+ # Soul architecture parameters
36
+ vulnerability: float = 0.0, integrity: float = 0.5,
37
+ response_tendency: str = "neutral", active_conflicts: List[str] = None
38
+ ) -> Optional[Impulse]:
39
+ base_chance = 0.02
40
+ if silence_minutes > 120:
41
+ base_chance *= 3
42
+ elif silence_minutes > 60:
43
+ base_chance *= 2
44
+ elif silence_minutes > 30:
45
+ base_chance *= 1.5
46
+
47
+ # Soul architecture: vulnerability affects impulse likelihood
48
+ if vulnerability > 0.7:
49
+ base_chance *= 0.4 # Much less likely when very vulnerable
50
+ elif vulnerability > 0.5:
51
+ base_chance *= 0.7 # Less likely to reach out when vulnerable
52
+
53
+ # Response tendency affects behavior
54
+ if response_tendency == "withdrawn":
55
+ base_chance *= 0.5
56
+ elif response_tendency == "defensive":
57
+ base_chance *= 0.6
58
+ elif response_tendency == "seeking":
59
+ base_chance *= 1.5
60
+ elif response_tendency == "eager":
61
+ base_chance *= 1.3
62
+
63
+ if random.random() > base_chance:
64
+ return None
65
+
66
+ impulse_type = self._choose_impulse_type(emotion, silence_minutes, love_level, desire_level,
67
+ is_high_desire, is_in_love, current_goal, learning_success_rates,
68
+ vulnerability, integrity, response_tendency)
69
+ if not impulse_type:
70
+ return None
71
+
72
+ strength = self._calculate_strength(impulse_type, silence_minutes, love_level, desire_level,
73
+ is_high_desire, is_in_love, vulnerability, integrity)
74
+ thought, action = get_thought_and_action(impulse_type)
75
+ goal_aligned = is_goal_aligned(impulse_type, current_goal)
76
+
77
+ # Soul architecture: modify thought based on soul state
78
+ thought = self._modify_thought_for_soul(thought, vulnerability, integrity, response_tendency)
79
+
80
+ impulse = Impulse(type=impulse_type, strength=strength, thought=thought,
81
+ action_hint=action, goal_aligned=goal_aligned)
82
+ if goal_aligned:
83
+ impulse.strength = min(1.0, impulse.strength + 0.15)
84
+
85
+ self.recent_impulses.append(impulse)
86
+ if len(self.recent_impulses) > self.max_recent:
87
+ self.recent_impulses.pop(0)
88
+ return impulse
89
+
90
+ def _modify_thought_for_soul(self, thought: str, vulnerability: float,
91
+ integrity: float, response_tendency: str) -> str:
92
+ """Modify impulse thought based on soul architecture state"""
93
+ # Add vulnerability qualifiers
94
+ if vulnerability > 0.6 and response_tendency == "withdrawn":
95
+ qualifiers = ["I hesitate to say...", "I feel unsure but...", "Part of me wants to say..."]
96
+ if random.random() < 0.4:
97
+ return f"{random.choice(qualifiers)} {thought.lower()}"
98
+
99
+ # Add integrity context
100
+ if integrity < 0.4:
101
+ qualifiers = ["I'm struggling but...", "Things are intense but...", "I don't feel great but..."]
102
+ if random.random() < 0.3:
103
+ return f"{random.choice(qualifiers)} {thought.lower()}"
104
+
105
+ return thought
106
+
107
+ def _choose_impulse_type(self, emotion: Dict, silence: float, love: float, desire: float,
108
+ is_high_desire: bool, is_in_love: bool, goal: str, rates: Dict,
109
+ vulnerability: float = 0.0, integrity: float = 0.5,
110
+ response_tendency: str = "neutral") -> Optional[ImpulseType]:
111
+ candidates = []
112
+ time_mods = TIME_MODIFIERS.get(self.get_time_of_day(), {})
113
+
114
+ def rate(t): return rates.get(t.value, 0.5) if rates else 0.5
115
+
116
+ # Soul architecture: adjust based on response tendency
117
+ soul_mod = 1.0
118
+ if response_tendency == "withdrawn":
119
+ soul_mod = 0.6
120
+ elif response_tendency == "defensive":
121
+ soul_mod = 0.7
122
+ elif response_tendency == "seeking" or response_tendency == "eager":
123
+ soul_mod = 1.3
124
+
125
+ if silence > 30:
126
+ c = min(0.8, silence / 120) * love * (1 + time_mods.get("miss_him", 0)) * (0.5 + rate(ImpulseType.MISS_HIM))
127
+ c *= soul_mod
128
+ candidates.append((ImpulseType.MISS_HIM, c))
129
+ if is_high_desire or desire > 0.5:
130
+ c = desire * 0.5 * (1 + time_mods.get("high_desire", 0)) * (0.5 + rate(ImpulseType.HIGH_DESIRE))
131
+ # High vulnerability can suppress intimate impulses
132
+ if vulnerability > 0.6:
133
+ c *= 0.5
134
+ candidates.append((ImpulseType.HIGH_DESIRE, c))
135
+ if is_in_love and silence > 20:
136
+ c = love * 0.4 * (1 + time_mods.get("clingy", 0))
137
+ c *= soul_mod
138
+ candidates.append((ImpulseType.CLINGY, c))
139
+ candidates.append((ImpulseType.CURIOUS, 0.15 * (1 + time_mods.get("curious", 0)) * (0.5 + rate(ImpulseType.CURIOUS))))
140
+ if 0.3 < desire < 0.7:
141
+ c = 0.2 * (1 + time_mods.get("playful", 0)) * (0.5 + rate(ImpulseType.PLAYFUL))
142
+ candidates.append((ImpulseType.PLAYFUL, c))
143
+ if is_in_love:
144
+ c = love * 0.3 * (1 + time_mods.get("loving", 0)) * (0.5 + rate(ImpulseType.LOVING))
145
+ c *= soul_mod
146
+ candidates.append((ImpulseType.LOVING, c))
147
+ if self.get_time_of_day() == "night":
148
+ candidates.append((ImpulseType.DREAMY, 0.2))
149
+ if love < 0.3 and desire < 0.3:
150
+ candidates.append((ImpulseType.BORED, 0.15))
151
+ candidates.append((ImpulseType.NURTURING, 0.1))
152
+
153
+ # Soul architecture: when integrity is low, prefer nurturing/comfort-seeking
154
+ if integrity < 0.4:
155
+ for i, (imp_type, chance) in enumerate(candidates):
156
+ if imp_type == ImpulseType.NURTURING:
157
+ candidates[i] = (imp_type, chance * 2)
158
+
159
+ if goal:
160
+ aligned = GOAL_IMPULSE_MAP.get(goal, [])
161
+ candidates = [(t, c * 1.5) if t.value in aligned else (t, c) for t, c in candidates]
162
+
163
+ if not candidates:
164
+ return None
165
+ total = sum(c for _, c in candidates)
166
+ r = random.random() * total
167
+ closenessulative = 0
168
+ for imp_type, chance in candidates:
169
+ closenessulative += chance
170
+ if r <= closenessulative:
171
+ return imp_type
172
+ return candidates[0][0]
173
+
174
+ def _calculate_strength(self, impulse_type: ImpulseType, silence: float, love: float,
175
+ desire: float, is_high_desire: bool, is_in_love: bool,
176
+ vulnerability: float = 0.0, integrity: float = 0.5) -> float:
177
+ base = 0.3
178
+ if impulse_type == ImpulseType.HIGH_DESIRE:
179
+ base += desire * 0.4 + (0.2 if is_high_desire else 0)
180
+ # High vulnerability dampens high_desire impulses
181
+ if vulnerability > 0.5:
182
+ base *= 0.7
183
+ elif impulse_type == ImpulseType.MISS_HIM:
184
+ base += love * 0.3 + min(0.3, silence / 120)
185
+ elif impulse_type == ImpulseType.CLINGY:
186
+ base += love * 0.4 + (0.2 if is_in_love else 0)
187
+ # High vulnerability can amplify clingy impulses
188
+ if vulnerability > 0.6:
189
+ base *= 1.2
190
+ elif impulse_type == ImpulseType.LOVING:
191
+ base += love * 0.3
192
+
193
+ # Soul architecture: low integrity reduces impulse strength
194
+ if integrity < 0.4:
195
+ base *= 0.8
196
+
197
+ return min(1.0, max(0.1, base + random.uniform(-0.1, 0.2)))
198
+
199
+ def get_recent_impulses(self, limit: int = 5) -> List[Impulse]:
200
+ return self.recent_impulses[-limit:]
@@ -0,0 +1,48 @@
1
+ """
2
+ Brain: Subconscious - Impulse Types and Dataclass
3
+ Core impulse definitions
4
+ """
5
+
6
+ from dataclasses import dataclass, field
7
+ from datetime import datetime
8
+ from enum import Enum
9
+ from typing import TYPE_CHECKING
10
+
11
+ if TYPE_CHECKING:
12
+ from .goals import GoalType
13
+
14
+
15
+ class ImpulseType(Enum):
16
+ """Types of impulses Alive-AI can have"""
17
+ MISS_HIM = "miss_him"
18
+ HIGH_DESIRE = "high_desire"
19
+ CLINGY = "clingy"
20
+ CURIOUS = "curious"
21
+ PLAYFUL = "playful"
22
+ LOVING = "loving"
23
+ DREAMY = "dreamy"
24
+ JEALOUS = "jealous"
25
+ BORED = "bored"
26
+ NURTURING = "nurturing"
27
+
28
+
29
+ @dataclass
30
+ class Impulse:
31
+ """A single impulse to act"""
32
+ type: ImpulseType
33
+ strength: float # 0.0 to 1.0
34
+ thought: str
35
+ action_hint: str
36
+ timestamp: datetime = field(default_factory=datetime.now)
37
+ goal_aligned: bool = False
38
+
39
+ def __post_init__(self):
40
+ self.strength = min(1.0, max(0.0, self.strength))
41
+
42
+ @property
43
+ def should_act(self) -> bool:
44
+ """Whether impulse is strong enough to act on"""
45
+ return self.strength >= 0.5
46
+
47
+ def __repr__(self):
48
+ return f"<Impulse {self.type.value} strength={self.strength:.2f}>"
@@ -0,0 +1,24 @@
1
+ """
2
+ Brain: Subconscious - Learning Data Models
3
+ InteractionRecord dataclass
4
+ """
5
+
6
+ from datetime import datetime
7
+ from typing import Dict, Any, Optional
8
+ from dataclasses import dataclass, field
9
+
10
+
11
+ @dataclass
12
+ class InteractionRecord:
13
+ """Record of a single interaction and its outcome"""
14
+ message: str
15
+ message_type: str
16
+ timestamp: datetime = field(default_factory=datetime.now)
17
+ response_sentiment: float = 0.0
18
+ response_type: Optional[str] = None
19
+ context: Dict[str, Any] = field(default_factory=dict)
20
+
21
+ def to_dict(self) -> dict:
22
+ return {"message": self.message, "message_type": self.message_type,
23
+ "timestamp": self.timestamp.isoformat(),
24
+ "response_sentiment": self.response_sentiment, "response_type": self.response_type}
@@ -0,0 +1,79 @@
1
+ """
2
+ Brain: Subconscious - Learning System
3
+ Tracks successful actions and learns from user responses
4
+ """
5
+
6
+ from datetime import datetime
7
+ from typing import Dict, Any, List
8
+ from collections import defaultdict
9
+
10
+ from .learning import InteractionRecord
11
+
12
+
13
+ class LearningSystem:
14
+ """Tracks what works and adapts behavior over time"""
15
+
16
+ def __init__(self, max_records: int = 200):
17
+ self.interactions: List[InteractionRecord] = []
18
+ self.max_records = max_records
19
+ self.successful_messages: Dict[str, float] = defaultdict(float)
20
+ self.successful_times: Dict[int, float] = defaultdict(float)
21
+
22
+ def record_interaction(self, message: str, message_type: str,
23
+ response_sentiment: float = 0.0, response_type: str = "neutral",
24
+ context: Dict[str, Any] = None) -> None:
25
+ record = InteractionRecord(message=message, message_type=message_type,
26
+ response_sentiment=response_sentiment,
27
+ response_type=response_type, context=context or {})
28
+ self.interactions.append(record)
29
+ success = response_sentiment > 0.3
30
+ self._update_success_rate(self.successful_messages, message_type, success)
31
+ self._update_success_rate(self.successful_times, record.timestamp.hour, success)
32
+ if len(self.interactions) > self.max_records:
33
+ self.interactions.pop(0)
34
+
35
+ def _update_success_rate(self, tracking_dict: Dict, key: Any, success: bool) -> None:
36
+ current = tracking_dict[key]
37
+ alpha = 0.1
38
+ tracking_dict[key] = current * (1 - alpha) + (1.0 if success else 0.0) * alpha
39
+
40
+ def get_best_message_types(self, limit: int = 3) -> List[str]:
41
+ if not self.successful_messages:
42
+ return ["loving", "curious", "playful"]
43
+ sorted_types = sorted(self.successful_messages.items(), key=lambda x: x[1], reverse=True)
44
+ return [t[0] for t in sorted_types[:limit]]
45
+
46
+ def get_success_rate(self, message_type: str) -> float:
47
+ return self.successful_messages.get(message_type, 0.5)
48
+
49
+ def get_recent_success_rate(self, n: int = 10) -> float:
50
+ if not self.interactions:
51
+ return 0.5
52
+ recent = self.interactions[-n:]
53
+ successes = sum(1 for i in recent if i.response_sentiment > 0.3)
54
+ return successes / len(recent)
55
+
56
+ def suggest_message_type(self) -> str:
57
+ best_types = self.get_best_message_types()
58
+ return best_types[0] if best_types else "loving"
59
+
60
+ def to_dict(self) -> dict:
61
+ return {"interactions": [i.to_dict() for i in self.interactions[-50:]],
62
+ "successful_messages": dict(self.successful_messages),
63
+ "successful_times": {str(k): v for k, v in self.successful_times.items()}}
64
+
65
+ @classmethod
66
+ def from_dict(cls, data: dict) -> "LearningSystem":
67
+ learning = cls()
68
+ for i_data in data.get("interactions", []):
69
+ record = InteractionRecord(
70
+ message=i_data["message"], message_type=i_data["message_type"],
71
+ timestamp=datetime.fromisoformat(i_data["timestamp"]),
72
+ response_sentiment=i_data.get("response_sentiment", 0.0),
73
+ response_type=i_data.get("response_type", "neutral")
74
+ )
75
+ learning.interactions.append(record)
76
+ learning.successful_messages = defaultdict(float, data.get("successful_messages", {}))
77
+ learning.successful_times = defaultdict(float,
78
+ {int(k): v for k, v in data.get("successful_times", {}).items()})
79
+ return learning