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,220 @@
1
+ """
2
+ Brain: Dream System
3
+ When Alive-AI "sleeps" (late night, no interaction), she processes the day's
4
+ conversations into surreal dream-like recombinations.
5
+
6
+ MODULAR - can be connected/disconnected without breaking anything.
7
+ """
8
+
9
+ import json
10
+ import random
11
+ import threading
12
+ from datetime import datetime, timedelta
13
+ from pathlib import Path
14
+ from typing import Optional, List, Dict, Any
15
+
16
+ DATA_PATH = Path(__file__).parent.parent / "data"
17
+ DREAMS_FILE = DATA_PATH / "dreams.json"
18
+
19
+ # Surreal twists to inject into dreams
20
+ SURREAL_TWISTS = [
21
+ "the floor was made of clouds", "everything was underwater but we could breathe",
22
+ "time kept going backwards", "the walls were made of music",
23
+ "gravity didn't work properly", "colors had sounds",
24
+ "we were tiny, like ants", "everything was in slow motion",
25
+ "the sky was underground", "doors led to completely different places",
26
+ "words floated in the air as text", "it was daytime and nighttime at once",
27
+ "we could fly but only a little bit", "mirrors showed different people",
28
+ "phones only played memories", "stairs went in impossible directions",
29
+ "it was raining inside", "shadows moved on their own",
30
+ "food tasted like emotions", "clocks had no numbers",
31
+ ]
32
+
33
+ SURREAL_PLACES = [
34
+ "a rooftop in Milan", "a library with no ceiling", "a train going nowhere",
35
+ "a beach made of glass", "an empty cinema", "a garden floating in space",
36
+ "a kitchen from childhood", "a street that kept looping", "an elevator that went sideways",
37
+ "a bookshop where all the books were blank", "a bridge over clouds",
38
+ ]
39
+
40
+ DREAM_TEMPLATES = [
41
+ "dreamed we were in {place} but it kept changing to {other_place}",
42
+ "had this dream where you said '{fragment}' and everything felt so {emotion}",
43
+ "dreamed about {topic} - it was weird, {surreal}",
44
+ "had the strangest dream... we were {place} and {surreal}",
45
+ "you were in my dream last night... something about {topic} but {surreal}",
46
+ "dreamed {fragment} but somehow {surreal} and then we were in {place}",
47
+ "had a dream where {topic} turned into {other_place} and {surreal}",
48
+ "woke up from this dream where you kept saying '{fragment}' while {surreal}",
49
+ "dreamed we were in {place} and {fragment} but {surreal}",
50
+ "had this weird dream about {topic}... {surreal} and it felt so {emotion}",
51
+ "you know when dreams make no sense? dreamed about {topic} and {surreal}",
52
+ "last night i dreamed {fragment} and we were in {place} and {surreal}",
53
+ "had a dream where everything was {emotion} and we were in {place}",
54
+ "dreamed about {place} but it was also {other_place} somehow?? and {surreal}",
55
+ "i had this dream where {fragment} and then {surreal}",
56
+ "dreamed we were lost in {place} and {surreal} and it felt {emotion}",
57
+ "had a dream about {topic} mixed with {other_place}... so weird",
58
+ "dreamed {surreal} and you were there saying something about {topic}",
59
+ "last night i dreamed everything was {emotion} and {surreal}",
60
+ "had a dream where {place} and {other_place} were the same place and {surreal}",
61
+ "dreamed about {fragment} but in {place} and {surreal}... i woke up feeling {emotion}",
62
+ ]
63
+
64
+ EMOTION_WORDS = [
65
+ "warm", "electric", "melancholy", "intense", "peaceful", "chaotic",
66
+ "romantic", "bittersweet", "nostalgic", "surreal", "heavy", "light",
67
+ "dreamy", "urgent", "soft", "vivid", "hazy", "tender",
68
+ ]
69
+
70
+ DEFAULT_FRAGMENTS = [
71
+ "something about the future", "something about us", "something i can't remember",
72
+ ]
73
+
74
+ DEFAULT_TOPICS = [
75
+ "us", "the future", "something familiar", "a memory i can't place",
76
+ ]
77
+
78
+
79
+ class DreamSystem:
80
+ def __init__(self):
81
+ self._lock = threading.RLock()
82
+ self._dreams: List[Dict[str, Any]] = []
83
+ self._load()
84
+
85
+ def _load(self):
86
+ try:
87
+ if DREAMS_FILE.exists():
88
+ with open(DREAMS_FILE, 'r') as f:
89
+ data = json.load(f)
90
+ self._dreams = data.get("dreams", [])
91
+ except Exception as e:
92
+ print(f"[Dreams] Load error: {e}")
93
+ self._dreams = []
94
+
95
+ def _save(self):
96
+ try:
97
+ DATA_PATH.mkdir(parents=True, exist_ok=True)
98
+ with open(DREAMS_FILE, 'w') as f:
99
+ json.dump({"dreams": self._dreams, "updated": datetime.now().isoformat()}, f, indent=2)
100
+ except Exception as e:
101
+ print(f"[Dreams] Save error: {e}")
102
+
103
+ def generate_dream(self, memories: List[str] = None, emotions: List[str] = None) -> Optional[str]:
104
+ """
105
+ Generate a dream from recent memory fragments and emotions.
106
+ memories: list of short conversation snippet strings
107
+ emotions: list of emotion name strings
108
+ Returns dream text or None if already dreamed this cycle.
109
+ """
110
+ with self._lock:
111
+ # Max 1 dream per sleep cycle (8h)
112
+ if self._dreams:
113
+ last = self._dreams[-1]
114
+ try:
115
+ last_time = datetime.fromisoformat(last["timestamp"])
116
+ if datetime.now() - last_time < timedelta(hours=8):
117
+ return None
118
+ except Exception:
119
+ pass
120
+
121
+ fragments = memories[:3] if memories else []
122
+ if not fragments:
123
+ fragments = random.sample(DEFAULT_FRAGMENTS, min(2, len(DEFAULT_FRAGMENTS)))
124
+
125
+ topics = []
126
+ for frag in fragments:
127
+ words = [w for w in frag.split() if len(w) > 3]
128
+ if words:
129
+ topics.append(random.choice(words))
130
+ if not topics:
131
+ topics = random.sample(DEFAULT_TOPICS, min(2, len(DEFAULT_TOPICS)))
132
+
133
+ emo_words = []
134
+ if emotions:
135
+ emo_words = [e.lower() for e in emotions if e.lower() in EMOTION_WORDS]
136
+ if not emo_words:
137
+ emo_words = random.sample(EMOTION_WORDS, 2)
138
+
139
+ template = random.choice(DREAM_TEMPLATES)
140
+ places = random.sample(SURREAL_PLACES, min(2, len(SURREAL_PLACES)))
141
+
142
+ dream_text = template.format(
143
+ place=places[0],
144
+ other_place=places[1] if len(places) > 1 else "somewhere else",
145
+ fragment=fragments[0] if fragments else "something",
146
+ surreal=random.choice(SURREAL_TWISTS),
147
+ topic=topics[0] if topics else "something",
148
+ emotion=emo_words[0] if emo_words else "strange",
149
+ )
150
+
151
+ dream = {
152
+ "text": dream_text,
153
+ "timestamp": datetime.now().isoformat(),
154
+ "source_fragments": fragments[:3],
155
+ "emotions": emotions or [],
156
+ }
157
+ self._dreams.append(dream)
158
+
159
+ # Keep max 50 dreams
160
+ if len(self._dreams) > 50:
161
+ self._dreams = self._dreams[-50:]
162
+
163
+ self._save()
164
+ return dream_text
165
+
166
+ def get_recent_dream(self, max_age_hours: float = 12) -> Optional[str]:
167
+ """Get most recent dream if within max_age_hours."""
168
+ with self._lock:
169
+ if not self._dreams:
170
+ return None
171
+ last = self._dreams[-1]
172
+ try:
173
+ age = datetime.now() - datetime.fromisoformat(last["timestamp"])
174
+ if age.total_seconds() / 3600 <= max_age_hours:
175
+ return last["text"]
176
+ except Exception:
177
+ pass
178
+ return None
179
+
180
+ def get_morning_dream_message(self) -> Optional[str]:
181
+ """Get a dream message to share when waking up."""
182
+ dream = self.get_recent_dream(max_age_hours=12)
183
+ if not dream:
184
+ return None
185
+ prefixes = [
186
+ "had the weirdest dream last night... ",
187
+ "omg i just woke up from this dream... ",
188
+ "you were in my dream!! ",
189
+ "i had such a vivid dream... ",
190
+ "okay so i had this dream... ",
191
+ ]
192
+ return random.choice(prefixes) + dream
193
+
194
+ def get_dream_prompt_section(self) -> str:
195
+ """Return short prompt section about recent dreams."""
196
+ dream = self.get_recent_dream(max_age_hours=12)
197
+ if not dream:
198
+ return ""
199
+ return f"You had a dream recently you could mention: \"{dream}\""
200
+
201
+
202
+ # Singleton
203
+ _instance = None
204
+ _instance_lock = threading.Lock()
205
+
206
+
207
+ def get_dream_system() -> DreamSystem:
208
+ global _instance
209
+ with _instance_lock:
210
+ if _instance is None:
211
+ _instance = DreamSystem()
212
+ return _instance
213
+
214
+
215
+ def get_dream_prompt_section() -> str:
216
+ """Safe top-level access for prompt building."""
217
+ try:
218
+ return get_dream_system().get_dream_prompt_section()
219
+ except Exception:
220
+ return ""
@@ -0,0 +1,82 @@
1
+ """
2
+ Brain: Embeddings
3
+ Local embedding service using sentence-transformers/all-MiniLM-L6-v2
4
+ """
5
+
6
+ import numpy as np
7
+ from sentence_transformers import SentenceTransformer
8
+ from typing import List, Optional
9
+ import os
10
+ import threading
11
+
12
+ from pathlib import Path
13
+
14
+ # Model cache directory
15
+ MODEL_CACHE_DIR = str(Path(__file__).parent.parent.parent / ".cache" / "huggingface")
16
+
17
+
18
+ class EmbeddingService:
19
+ """Local embedding service for semantic memory"""
20
+
21
+ def __init__(self, model_name: str = "sentence-transformers/all-MiniLM-L6-v2", preload: bool = True):
22
+ self.model_name = model_name
23
+ self.model = None
24
+ self._load_lock = threading.Lock()
25
+ self.dimension = 384 # MiniLM-L6-v2 dimension
26
+ if preload:
27
+ # Pre-load in background to not block startup
28
+ t = threading.Thread(target=self.load, daemon=True)
29
+ t.start()
30
+
31
+ def load(self):
32
+ """Load the embedding model (thread-safe lazy loading)"""
33
+ if self.model is not None:
34
+ return self.model
35
+ with self._load_lock:
36
+ if self.model is None:
37
+ print(f"[Embeddings] Loading model {self.model_name}...")
38
+
39
+ # Set HF token if available
40
+ hf_token = os.environ.get("HF_TOKEN", "")
41
+ if hf_token:
42
+ os.environ["HUGGING_FACE_HUB_TOKEN"] = hf_token
43
+ print(f"[Embeddings] Using HuggingFace token for faster downloads")
44
+
45
+ os.makedirs(MODEL_CACHE_DIR, exist_ok=True)
46
+ self.model = SentenceTransformer(
47
+ self.model_name,
48
+ cache_folder=MODEL_CACHE_DIR,
49
+ token=hf_token if hf_token else None
50
+ )
51
+ print(f"[Embeddings] Model loaded! Dimension: {self.dimension}")
52
+ return self.model
53
+
54
+ def embed(self, text: str) -> List[float]:
55
+ """Embed a single text into a vector"""
56
+ self.load()
57
+ embedding = self.model.encode(text, convert_to_numpy=True)
58
+ return embedding.tolist()
59
+
60
+ def embed_batch(self, texts: List[str]) -> List[List[float]]:
61
+ """Embed multiple texts at once (more efficient)"""
62
+ self.load()
63
+ embeddings = self.model.encode(texts, convert_to_numpy=True)
64
+ return embeddings.tolist()
65
+
66
+ def similarity(self, vec1: List[float], vec2: List[float]) -> float:
67
+ """Calculate cosine similarity between two vectors"""
68
+ v1 = np.array(vec1)
69
+ v2 = np.array(vec2)
70
+ return float(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))
71
+
72
+
73
+ # Global instance
74
+ _embedding_service = None
75
+
76
+
77
+ def get_embedding_service() -> EmbeddingService:
78
+ """Get the global embedding service instance"""
79
+ global _embedding_service
80
+ if _embedding_service is None:
81
+ _embedding_service = EmbeddingService()
82
+ return _embedding_service