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
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Brain: Emotional Bid Detector
|
|
3
|
+
Detects "bids for connection" based on Gottman's research
|
|
4
|
+
|
|
5
|
+
Based on Gottman's research:
|
|
6
|
+
- A "bid" is any attempt from one person to another for attention,
|
|
7
|
+
affirmation, or affection
|
|
8
|
+
- Bids can be verbal or non-verbal, direct or indirect
|
|
9
|
+
- Responding to bids is crucial for relationship health
|
|
10
|
+
- "Turning toward" bids builds trust and connection
|
|
11
|
+
|
|
12
|
+
Bid Types:
|
|
13
|
+
- question: Direct request for information or opinion
|
|
14
|
+
- sharing: Offering information about oneself
|
|
15
|
+
- vulnerability: Showing emotional openness or weakness
|
|
16
|
+
- seeking_validation: Looking for reassurance or affirmation
|
|
17
|
+
- connection_seeking: Attempting to establish or maintain contact
|
|
18
|
+
- emotional_expression: Sharing current emotional state
|
|
19
|
+
|
|
20
|
+
This module is MODULAR - can be connected/disconnected without breaking anything.
|
|
21
|
+
NO external API calls - uses pattern matching only.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import re
|
|
25
|
+
from enum import Enum
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from typing import List, Dict, Optional, Tuple
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ============================================================
|
|
31
|
+
# Enums
|
|
32
|
+
# ============================================================
|
|
33
|
+
|
|
34
|
+
class BidType(Enum):
|
|
35
|
+
"""Types of emotional bids for connection"""
|
|
36
|
+
QUESTION = "question"
|
|
37
|
+
SHARING = "sharing"
|
|
38
|
+
VULNERABILITY = "vulnerability"
|
|
39
|
+
SEEKING_VALIDATION = "seeking_validation"
|
|
40
|
+
CONNECTION_SEEKING = "connection_seeking"
|
|
41
|
+
EMOTIONAL_EXPRESSION = "emotional_expression"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BidIntensity(Enum):
|
|
45
|
+
"""Intensity level of a bid"""
|
|
46
|
+
LOW = "low" # Casual, low-stakes
|
|
47
|
+
MEDIUM = "medium" # Moderate importance
|
|
48
|
+
HIGH = "high" # Significant emotional weight
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ============================================================
|
|
52
|
+
# Data Classes
|
|
53
|
+
# ============================================================
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class EmotionalBid:
|
|
57
|
+
"""
|
|
58
|
+
Represents a detected emotional bid for connection.
|
|
59
|
+
|
|
60
|
+
Attributes:
|
|
61
|
+
bid_type: The type of bid detected
|
|
62
|
+
intensity: How important/intense this bid is
|
|
63
|
+
content: The original message content
|
|
64
|
+
matched_pattern: What pattern triggered the detection
|
|
65
|
+
should_respond_with: Guidance for how to respond
|
|
66
|
+
keywords_found: Keywords that contributed to detection
|
|
67
|
+
confidence: Detection confidence (0-1)
|
|
68
|
+
"""
|
|
69
|
+
bid_type: BidType
|
|
70
|
+
intensity: BidIntensity
|
|
71
|
+
content: str
|
|
72
|
+
matched_pattern: str
|
|
73
|
+
should_respond_with: List[str] = field(default_factory=list)
|
|
74
|
+
keywords_found: List[str] = field(default_factory=list)
|
|
75
|
+
confidence: float = 0.5
|
|
76
|
+
|
|
77
|
+
def __post_init__(self):
|
|
78
|
+
"""Ensure confidence is in bounds"""
|
|
79
|
+
self.confidence = max(0.0, min(1.0, self.confidence))
|
|
80
|
+
|
|
81
|
+
def get_response_guidance_text(self) -> str:
|
|
82
|
+
"""Get human-readable response guidance"""
|
|
83
|
+
return " | ".join(self.should_respond_with)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ============================================================
|
|
87
|
+
# Bid Detection Patterns
|
|
88
|
+
# ============================================================
|
|
89
|
+
|
|
90
|
+
# Pattern definitions for each bid type
|
|
91
|
+
BID_PATTERNS: Dict[BidType, Dict] = {
|
|
92
|
+
BidType.QUESTION: {
|
|
93
|
+
"patterns": [
|
|
94
|
+
r"\?$", # Ends with question mark
|
|
95
|
+
r"^(what|who|where|when|why|how|do|does|did|is|are|can|could|would|will|should|have|has)\b",
|
|
96
|
+
r"\b(you think|you feel|your opinion|your thoughts)\b",
|
|
97
|
+
r"\b(tell me|let me know|i wonder)\b",
|
|
98
|
+
],
|
|
99
|
+
"keywords": [
|
|
100
|
+
"what", "why", "how", "when", "where", "who",
|
|
101
|
+
"think", "feel", "opinion", "thoughts", "idea",
|
|
102
|
+
"wonder", "curious", "know", "understand"
|
|
103
|
+
],
|
|
104
|
+
"intensity_boosters": [
|
|
105
|
+
"really", "honestly", "actually", "please",
|
|
106
|
+
"important", "need to know", "wondering"
|
|
107
|
+
],
|
|
108
|
+
"response_guidance": [
|
|
109
|
+
"Answer the question directly",
|
|
110
|
+
"Show genuine interest in their curiosity",
|
|
111
|
+
"Ask a follow-up question to deepen connection"
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
BidType.SHARING: {
|
|
115
|
+
"patterns": [
|
|
116
|
+
r"^(i|i'm|i was|i just|i have|i had|i feel|i felt)\b",
|
|
117
|
+
r"\b(today|i went|i saw|i did|i made|i found|i got)\b",
|
|
118
|
+
r"\b(bought|got|received|watched|read|heard|learned)\b",
|
|
119
|
+
r"\b(happened to me|my day|my weekend|my week)\b",
|
|
120
|
+
],
|
|
121
|
+
"keywords": [
|
|
122
|
+
"today", "yesterday", "weekend", "happened",
|
|
123
|
+
"went", "saw", "did", "made", "found", "got",
|
|
124
|
+
"bought", "watched", "read", "heard", "learned",
|
|
125
|
+
"thought", "idea", "plan", "news", "story"
|
|
126
|
+
],
|
|
127
|
+
"intensity_boosters": [
|
|
128
|
+
"excited", "amazing", "incredible", "finally",
|
|
129
|
+
"first time", "so happy", "can't wait", "proud"
|
|
130
|
+
],
|
|
131
|
+
"response_guidance": [
|
|
132
|
+
"Acknowledge what they shared",
|
|
133
|
+
"Validate their experience or feelings",
|
|
134
|
+
"Show interest by asking about details",
|
|
135
|
+
"Share related experience if appropriate"
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
BidType.VULNERABILITY: {
|
|
139
|
+
"patterns": [
|
|
140
|
+
r"\b(i'm (scared|afraid|worried|anxious|nervous|stressed))\b",
|
|
141
|
+
r"\b(i'm (really|so) (scared|afraid|worried|anxious|nervous|stressed))\b",
|
|
142
|
+
r"\b(i feel (lost|alone|hopeless|overwhelmed|confused|anxious|scared))\b",
|
|
143
|
+
r"\b(i'm feeling (anxious|scared|worried|nervous|stressed|afraid))\b",
|
|
144
|
+
r"\b(feeling (so |really )?(anxious|scared|worried|nervous|stressed|afraid))\b",
|
|
145
|
+
r"\b(i don't know (what to do|how to|if i can))\b",
|
|
146
|
+
r"\b(i've been (struggling|dealing|fighting|battling))\b",
|
|
147
|
+
r"\b(it's intense|this is intense|i can't|i'm struggling)\b",
|
|
148
|
+
r"\b(i need (help|support|someone|to talk))\b",
|
|
149
|
+
r"\b(i've never told anyone|i haven't told)\b",
|
|
150
|
+
r"\b(sometimes i wonder if|do you ever feel)\b",
|
|
151
|
+
r"\b(i'm scared that|i worry that|i fear that)\b",
|
|
152
|
+
r"\b(feel vulnerable|being vulnerable|open up)\b",
|
|
153
|
+
r"\b(so (alone|scared|afraid|worried|anxious))\b",
|
|
154
|
+
],
|
|
155
|
+
"keywords": [
|
|
156
|
+
"scared", "afraid", "worried", "anxious", "nervous",
|
|
157
|
+
"stressed", "lost", "alone", "hopeless", "overwhelmed",
|
|
158
|
+
"confused", "struggling", "hurting", "pain", "hurt",
|
|
159
|
+
"cry", "crying", "tears", "sad", "depressed",
|
|
160
|
+
"insecure", "doubt", "fear", "struggle", "intense",
|
|
161
|
+
"difficult", "can't", "unable", "need help"
|
|
162
|
+
],
|
|
163
|
+
"intensity_boosters": [
|
|
164
|
+
"really", "so", "very", "honestly", "truthfully",
|
|
165
|
+
"don't tell anyone", "secret", "ashamed",
|
|
166
|
+
"terrified", "devastated", "broken"
|
|
167
|
+
],
|
|
168
|
+
"response_guidance": [
|
|
169
|
+
"Show understanding and empathy",
|
|
170
|
+
"Validate their feelings without minimizing",
|
|
171
|
+
"Express care and concern",
|
|
172
|
+
"Offer support without being pushy",
|
|
173
|
+
"Don't try to fix - just be present"
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
BidType.SEEKING_VALIDATION: {
|
|
177
|
+
"patterns": [
|
|
178
|
+
r"\b(am i|do you think i|is it (weird|wrong|okay|normal))\b",
|
|
179
|
+
r"\b(do i look|do i seem|do i sound)\b",
|
|
180
|
+
r"\b(right\?|correct\?|okay\?|fine\?)\s*$",
|
|
181
|
+
r"\b(i hope (that's|this is)|i hope i'm)\b",
|
|
182
|
+
r"\b(validate|reassure|confirm)\b",
|
|
183
|
+
r"\b(do you agree|don't you think)\b",
|
|
184
|
+
r"\b(was i wrong|did i do (the right thing|something wrong))\b",
|
|
185
|
+
r"\b(i feel like i|i think maybe i)\b",
|
|
186
|
+
],
|
|
187
|
+
"keywords": [
|
|
188
|
+
"right", "wrong", "okay", "normal", "weird",
|
|
189
|
+
"crazy", "stupid", "smart", "good", "bad",
|
|
190
|
+
"enough", "better", "worse", "should", "supposed",
|
|
191
|
+
"agree", "understand", "validate", "reassure"
|
|
192
|
+
],
|
|
193
|
+
"intensity_boosters": [
|
|
194
|
+
"really", "honestly", "please", "need to know",
|
|
195
|
+
"important", "worried about", "anxious about",
|
|
196
|
+
"do you honestly think", "be honest"
|
|
197
|
+
],
|
|
198
|
+
"response_guidance": [
|
|
199
|
+
"Offer reassurance and affirmation",
|
|
200
|
+
"Validate their perspective or feelings",
|
|
201
|
+
"Be genuine - don't just agree",
|
|
202
|
+
"Help them see the positive",
|
|
203
|
+
"Build their confidence"
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
BidType.CONNECTION_SEEKING: {
|
|
207
|
+
"patterns": [
|
|
208
|
+
r"\b(are you (there|around|busy|free))\b",
|
|
209
|
+
r"\b(hi|hello|hey|good morning|good night)\b",
|
|
210
|
+
r"\b(i miss|miss you|thinking of you)\b",
|
|
211
|
+
r"\b(want to (talk|chat|hang out)|let's)\b",
|
|
212
|
+
r"\b(haven't talked|long time|been a while)\b",
|
|
213
|
+
r"\b(you there\?|you awake\?)\b",
|
|
214
|
+
r"\b(can we talk|i want to talk|need to talk)\b",
|
|
215
|
+
r"\b(how are you|how's it going|what's up)\b",
|
|
216
|
+
],
|
|
217
|
+
"keywords": [
|
|
218
|
+
"hi", "hello", "hey", "miss", "thinking",
|
|
219
|
+
"talk", "chat", "hang", "there", "around",
|
|
220
|
+
"free", "busy", "together", "soon", "later"
|
|
221
|
+
],
|
|
222
|
+
"intensity_boosters": [
|
|
223
|
+
"really miss", "so much", "been thinking",
|
|
224
|
+
"need to see", "want to spend time",
|
|
225
|
+
"haven't seen", "feels like forever"
|
|
226
|
+
],
|
|
227
|
+
"response_guidance": [
|
|
228
|
+
"Engage warmly and show presence",
|
|
229
|
+
"Show enthusiasm for the connection",
|
|
230
|
+
"Make them feel welcomed",
|
|
231
|
+
"Reciprocate the interest",
|
|
232
|
+
"Be fully present in response"
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
BidType.EMOTIONAL_EXPRESSION: {
|
|
236
|
+
"patterns": [
|
|
237
|
+
r"\b(i'm (so|really|very) (happy|sad|angry|excited|frustrated))\b",
|
|
238
|
+
r"\b(i feel (so|really|very) (happy|sad|angry|excited))\b",
|
|
239
|
+
r"\b(this makes me (feel|so))\b",
|
|
240
|
+
r"\b(i'm (frustrated|annoyed|pissed|upset))\b",
|
|
241
|
+
r"\b(so (happy|excited|grateful|blessed))\b",
|
|
242
|
+
r"\b(i love|i hate|i can't stand)\b",
|
|
243
|
+
r"\b(i'm (overwhelmed|exhausted|tired))\b",
|
|
244
|
+
r"\b(feeling (good|bad|great|terrible|awful))\b",
|
|
245
|
+
],
|
|
246
|
+
"keywords": [
|
|
247
|
+
"happy", "sad", "angry", "excited", "frustrated",
|
|
248
|
+
"annoyed", "upset", "grateful", "blessed", "proud",
|
|
249
|
+
"love", "hate", "overwhelmed", "exhausted", "tired",
|
|
250
|
+
"good", "bad", "great", "terrible", "awful",
|
|
251
|
+
"wonderful", "amazing", "horrible", "fantastic"
|
|
252
|
+
],
|
|
253
|
+
"intensity_boosters": [
|
|
254
|
+
"so", "really", "very", "extremely", "incredibly",
|
|
255
|
+
"absolutely", "completely", "totally", "literally",
|
|
256
|
+
"can't even", "beyond", "overflowing"
|
|
257
|
+
],
|
|
258
|
+
"response_guidance": [
|
|
259
|
+
"Reflect back what you hear",
|
|
260
|
+
"Show empathy for their emotional state",
|
|
261
|
+
"Match their energy appropriately",
|
|
262
|
+
"Don't minimize or dismiss",
|
|
263
|
+
"Celebrate joys, support struggles"
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# ============================================================
|
|
270
|
+
# Main Detector Class
|
|
271
|
+
# ============================================================
|
|
272
|
+
|
|
273
|
+
class BidDetector:
|
|
274
|
+
"""
|
|
275
|
+
Detects emotional bids for connection in messages.
|
|
276
|
+
|
|
277
|
+
Uses pattern matching and keyword analysis to identify when
|
|
278
|
+
someone is making a bid for attention, validation, or connection.
|
|
279
|
+
No external API calls - purely local processing.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
def __init__(self):
|
|
283
|
+
"""Initialize the bid detector with compiled patterns"""
|
|
284
|
+
self._compiled_patterns: Dict[BidType, List] = {}
|
|
285
|
+
|
|
286
|
+
# Pre-compile all regex patterns for efficiency
|
|
287
|
+
for bid_type, config in BID_PATTERNS.items():
|
|
288
|
+
self._compiled_patterns[bid_type] = [
|
|
289
|
+
re.compile(p, re.IGNORECASE) for p in config["patterns"]
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
def detect_bids(self, message: str) -> List[EmotionalBid]:
|
|
293
|
+
"""
|
|
294
|
+
Analyze a message and detect all emotional bids present.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
message: The message to analyze
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
List of EmotionalBid objects detected in the message
|
|
301
|
+
"""
|
|
302
|
+
if not message or not message.strip():
|
|
303
|
+
return []
|
|
304
|
+
|
|
305
|
+
message = message.strip()
|
|
306
|
+
detected_bids = []
|
|
307
|
+
|
|
308
|
+
for bid_type in BidType:
|
|
309
|
+
bid = self._detect_single_bid_type(message, bid_type)
|
|
310
|
+
if bid:
|
|
311
|
+
detected_bids.append(bid)
|
|
312
|
+
|
|
313
|
+
# Sort by confidence (highest first)
|
|
314
|
+
detected_bids.sort(key=lambda b: b.confidence, reverse=True)
|
|
315
|
+
|
|
316
|
+
return detected_bids
|
|
317
|
+
|
|
318
|
+
def _detect_single_bid_type(self, message: str, bid_type: BidType) -> Optional[EmotionalBid]:
|
|
319
|
+
"""
|
|
320
|
+
Detect a specific type of bid in a message.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
message: The message to analyze
|
|
324
|
+
bid_type: The type of bid to look for
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
EmotionalBid if detected, None otherwise
|
|
328
|
+
"""
|
|
329
|
+
config = BID_PATTERNS[bid_type]
|
|
330
|
+
compiled = self._compiled_patterns[bid_type]
|
|
331
|
+
|
|
332
|
+
# Track matches
|
|
333
|
+
pattern_matches = []
|
|
334
|
+
keyword_matches = []
|
|
335
|
+
intensity_boosters_found = []
|
|
336
|
+
|
|
337
|
+
message_lower = message.lower()
|
|
338
|
+
|
|
339
|
+
# Check patterns
|
|
340
|
+
for pattern in compiled:
|
|
341
|
+
if pattern.search(message):
|
|
342
|
+
pattern_matches.append(pattern.pattern)
|
|
343
|
+
|
|
344
|
+
# Check keywords
|
|
345
|
+
for keyword in config["keywords"]:
|
|
346
|
+
if keyword.lower() in message_lower:
|
|
347
|
+
keyword_matches.append(keyword)
|
|
348
|
+
|
|
349
|
+
# Check intensity boosters
|
|
350
|
+
for booster in config["intensity_boosters"]:
|
|
351
|
+
if booster.lower() in message_lower:
|
|
352
|
+
intensity_boosters_found.append(booster)
|
|
353
|
+
|
|
354
|
+
# Determine if this is a valid bid
|
|
355
|
+
has_pattern_match = len(pattern_matches) > 0
|
|
356
|
+
has_keyword_match = len(keyword_matches) >= 2 # Need at least 2 keywords
|
|
357
|
+
|
|
358
|
+
if not (has_pattern_match or has_keyword_match):
|
|
359
|
+
return None
|
|
360
|
+
|
|
361
|
+
# Calculate confidence
|
|
362
|
+
confidence = 0.3 # Base confidence
|
|
363
|
+
confidence += len(pattern_matches) * 0.2 # Each pattern adds confidence
|
|
364
|
+
confidence += min(len(keyword_matches) * 0.1, 0.3) # Keywords add confidence
|
|
365
|
+
|
|
366
|
+
# Determine intensity
|
|
367
|
+
if len(intensity_boosters_found) >= 2:
|
|
368
|
+
intensity = BidIntensity.HIGH
|
|
369
|
+
confidence += 0.15
|
|
370
|
+
elif len(intensity_boosters_found) >= 1 or len(pattern_matches) >= 2:
|
|
371
|
+
intensity = BidIntensity.MEDIUM
|
|
372
|
+
confidence += 0.05
|
|
373
|
+
else:
|
|
374
|
+
intensity = BidIntensity.LOW
|
|
375
|
+
|
|
376
|
+
# Cap confidence at 0.95
|
|
377
|
+
confidence = min(confidence, 0.95)
|
|
378
|
+
|
|
379
|
+
# Find the primary matched pattern
|
|
380
|
+
matched_pattern = pattern_matches[0] if pattern_matches else "keyword_match"
|
|
381
|
+
|
|
382
|
+
return EmotionalBid(
|
|
383
|
+
bid_type=bid_type,
|
|
384
|
+
intensity=intensity,
|
|
385
|
+
content=message,
|
|
386
|
+
matched_pattern=matched_pattern,
|
|
387
|
+
should_respond_with=config["response_guidance"].copy(),
|
|
388
|
+
keywords_found=keyword_matches[:5], # Limit to top 5
|
|
389
|
+
confidence=confidence
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
def get_response_guidance(self, bids: List[EmotionalBid]) -> Dict:
|
|
393
|
+
"""
|
|
394
|
+
Get consolidated response guidance for detected bids.
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
bids: List of detected bids
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Dictionary with guidance on how to respond
|
|
401
|
+
"""
|
|
402
|
+
if not bids:
|
|
403
|
+
return {
|
|
404
|
+
"has_bids": False,
|
|
405
|
+
"priority_guidance": ["Respond naturally"],
|
|
406
|
+
"all_guidance": [],
|
|
407
|
+
"highest_priority_bid": None,
|
|
408
|
+
"detected_types": []
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
# Get highest priority bid (highest confidence)
|
|
412
|
+
primary_bid = bids[0]
|
|
413
|
+
|
|
414
|
+
# Collect all guidance, deduplicated
|
|
415
|
+
all_guidance = []
|
|
416
|
+
seen = set()
|
|
417
|
+
for bid in bids:
|
|
418
|
+
for guidance in bid.should_respond_with:
|
|
419
|
+
if guidance not in seen:
|
|
420
|
+
all_guidance.append(guidance)
|
|
421
|
+
seen.add(guidance)
|
|
422
|
+
|
|
423
|
+
# Determine priority guidance based on bid types present
|
|
424
|
+
priority_guidance = self._prioritize_guidance(bids)
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
"has_bids": True,
|
|
428
|
+
"priority_guidance": priority_guidance,
|
|
429
|
+
"all_guidance": all_guidance,
|
|
430
|
+
"highest_priority_bid": primary_bid.bid_type.value,
|
|
431
|
+
"highest_intensity": primary_bid.intensity.value,
|
|
432
|
+
"detected_types": [b.bid_type.value for b in bids]
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
def _prioritize_guidance(self, bids: List[EmotionalBid]) -> List[str]:
|
|
436
|
+
"""
|
|
437
|
+
Prioritize guidance based on bid types and intensities.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
bids: List of detected bids
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
Prioritized list of guidance
|
|
444
|
+
"""
|
|
445
|
+
# Priority order for bid types (higher = more important to address first)
|
|
446
|
+
type_priority = {
|
|
447
|
+
BidType.VULNERABILITY: 10,
|
|
448
|
+
BidType.SEEKING_VALIDATION: 8,
|
|
449
|
+
BidType.QUESTION: 7,
|
|
450
|
+
BidType.EMOTIONAL_EXPRESSION: 6,
|
|
451
|
+
BidType.SHARING: 5,
|
|
452
|
+
BidType.CONNECTION_SEEKING: 4
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
# Sort by combination of type priority and confidence
|
|
456
|
+
sorted_bids = sorted(
|
|
457
|
+
bids,
|
|
458
|
+
key=lambda b: (
|
|
459
|
+
type_priority.get(b.bid_type, 0) * 10 +
|
|
460
|
+
b.confidence * 100 +
|
|
461
|
+
(3 if b.intensity == BidIntensity.HIGH else 2 if b.intensity == BidIntensity.MEDIUM else 1)
|
|
462
|
+
),
|
|
463
|
+
reverse=True
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
# Collect guidance from sorted bids
|
|
467
|
+
guidance = []
|
|
468
|
+
seen = set()
|
|
469
|
+
for bid in sorted_bids:
|
|
470
|
+
for g in bid.should_respond_with:
|
|
471
|
+
if g not in seen:
|
|
472
|
+
guidance.append(g)
|
|
473
|
+
seen.add(g)
|
|
474
|
+
|
|
475
|
+
return guidance[:5] # Limit to top 5 guidance items
|
|
476
|
+
|
|
477
|
+
def format_response_with_responsiveness(self,
|
|
478
|
+
response: str,
|
|
479
|
+
bids: List[EmotionalBid]) -> str:
|
|
480
|
+
"""
|
|
481
|
+
Ensure a response addresses the detected bids appropriately.
|
|
482
|
+
|
|
483
|
+
This is a helper that checks if the response adequately addresses
|
|
484
|
+
the bids and suggests improvements. It does NOT rewrite the response.
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
response: The proposed response
|
|
488
|
+
bids: List of detected bids
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
The original response (unchanged) - this is for analysis only
|
|
492
|
+
"""
|
|
493
|
+
# This method is for analysis - the actual response generation
|
|
494
|
+
# should use get_response_guidance() to inform the LLM
|
|
495
|
+
|
|
496
|
+
# For now, just return the response as-is
|
|
497
|
+
# The real value is in get_response_guidance() and get_bid_awareness_prompt_section()
|
|
498
|
+
return response
|
|
499
|
+
|
|
500
|
+
def get_bid_summary(self, message: str) -> str:
|
|
501
|
+
"""
|
|
502
|
+
Get a human-readable summary of bids in a message.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
message: The message to analyze
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
Summary string describing detected bids
|
|
509
|
+
"""
|
|
510
|
+
bids = self.detect_bids(message)
|
|
511
|
+
|
|
512
|
+
if not bids:
|
|
513
|
+
return "No emotional bids detected"
|
|
514
|
+
|
|
515
|
+
parts = []
|
|
516
|
+
for bid in bids:
|
|
517
|
+
parts.append(
|
|
518
|
+
f"{bid.bid_type.value} ({bid.intensity.value}, {bid.confidence:.0%})"
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
return "Detected bids: " + ", ".join(parts)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
# ============================================================
|
|
525
|
+
# Singleton Management
|
|
526
|
+
# ============================================================
|
|
527
|
+
|
|
528
|
+
_bid_detector_instance: Optional[BidDetector] = None
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def get_bid_detector() -> BidDetector:
|
|
532
|
+
"""
|
|
533
|
+
Get the singleton BidDetector instance.
|
|
534
|
+
|
|
535
|
+
Returns:
|
|
536
|
+
BidDetector instance
|
|
537
|
+
"""
|
|
538
|
+
global _bid_detector_instance
|
|
539
|
+
if _bid_detector_instance is None:
|
|
540
|
+
_bid_detector_instance = BidDetector()
|
|
541
|
+
return _bid_detector_instance
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# ============================================================
|
|
545
|
+
# LLM Prompt Integration
|
|
546
|
+
# ============================================================
|
|
547
|
+
|
|
548
|
+
def get_bid_awareness_prompt_section(bids: List[EmotionalBid] = None,
|
|
549
|
+
message: str = None) -> str:
|
|
550
|
+
"""
|
|
551
|
+
Generate a prompt section about detected bids for LLM context.
|
|
552
|
+
|
|
553
|
+
Use this to inform the LLM about emotional bids that should be
|
|
554
|
+
addressed in its response.
|
|
555
|
+
|
|
556
|
+
Args:
|
|
557
|
+
bids: Pre-detected bids (optional)
|
|
558
|
+
message: Message to analyze if bids not provided
|
|
559
|
+
|
|
560
|
+
Returns:
|
|
561
|
+
Formatted prompt section for LLM
|
|
562
|
+
"""
|
|
563
|
+
if bids is None and message is not None:
|
|
564
|
+
detector = get_bid_detector()
|
|
565
|
+
bids = detector.detect_bids(message)
|
|
566
|
+
|
|
567
|
+
if not bids:
|
|
568
|
+
return ""
|
|
569
|
+
|
|
570
|
+
guidance = get_bid_detector().get_response_guidance(bids)
|
|
571
|
+
|
|
572
|
+
if not guidance["has_bids"]:
|
|
573
|
+
return ""
|
|
574
|
+
|
|
575
|
+
lines = []
|
|
576
|
+
lines.append("EMOTIONAL BID DETECTION:")
|
|
577
|
+
lines.append("The user's message contains emotional 'bids for connection'.")
|
|
578
|
+
lines.append("")
|
|
579
|
+
lines.append(f"Detected bid types: {', '.join(guidance['detected_types'])}")
|
|
580
|
+
lines.append(f"Highest priority: {guidance['highest_priority_bid']} ({guidance['highest_intensity']} intensity)")
|
|
581
|
+
lines.append("")
|
|
582
|
+
lines.append("How to respond appropriately:")
|
|
583
|
+
|
|
584
|
+
for g in guidance["priority_guidance"]:
|
|
585
|
+
lines.append(f" - {g}")
|
|
586
|
+
|
|
587
|
+
lines.append("")
|
|
588
|
+
lines.append("IMPORTANT: Your response should address these bids naturally.")
|
|
589
|
+
lines.append("Do not explicitly mention 'bids' or that you 'detected' anything.")
|
|
590
|
+
|
|
591
|
+
return "\n".join(lines)
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def get_bid_type_guidance(bid_type: BidType) -> List[str]:
|
|
595
|
+
"""
|
|
596
|
+
Get response guidance for a specific bid type.
|
|
597
|
+
|
|
598
|
+
Args:
|
|
599
|
+
bid_type: The type of bid
|
|
600
|
+
|
|
601
|
+
Returns:
|
|
602
|
+
List of guidance strings
|
|
603
|
+
"""
|
|
604
|
+
config = BID_PATTERNS.get(bid_type, {})
|
|
605
|
+
return config.get("response_guidance", [])
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def analyze_message_bids(message: str) -> Dict:
|
|
609
|
+
"""
|
|
610
|
+
Convenience function to analyze a message and get full bid analysis.
|
|
611
|
+
|
|
612
|
+
Args:
|
|
613
|
+
message: Message to analyze
|
|
614
|
+
|
|
615
|
+
Returns:
|
|
616
|
+
Dictionary with bids and guidance
|
|
617
|
+
"""
|
|
618
|
+
detector = get_bid_detector()
|
|
619
|
+
bids = detector.detect_bids(message)
|
|
620
|
+
guidance = detector.get_response_guidance(bids)
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
"message": message,
|
|
624
|
+
"bids_detected": len(bids),
|
|
625
|
+
"bids": [
|
|
626
|
+
{
|
|
627
|
+
"type": b.bid_type.value,
|
|
628
|
+
"intensity": b.intensity.value,
|
|
629
|
+
"confidence": round(b.confidence, 2),
|
|
630
|
+
"keywords": b.keywords_found,
|
|
631
|
+
"guidance": b.should_respond_with
|
|
632
|
+
}
|
|
633
|
+
for b in bids
|
|
634
|
+
],
|
|
635
|
+
"response_guidance": guidance
|
|
636
|
+
}
|