-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdelulu_detective.py
More file actions
391 lines (335 loc) · 15 KB
/
Copy pathdelulu_detective.py
File metadata and controls
391 lines (335 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
"""
deluluisnotthesolulu -Main Orchestrator
Analyzes texting patterns like game tape. Calls a local LLM via Ollama.
"""
import json
import sys
import time
import urllib.request
import urllib.error
import io
# Force UTF-8 output on Windows
if sys.stdout.encoding != "utf-8":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
from data_engine import (
get_vibe_trend,
get_daily_summary,
get_confidence_score,
get_correlation_highlights,
get_recent_messages,
)
from pattern_detector import (
word_impact_analysis,
ghost_triggers,
response_time_patterns,
her_enthusiasm_triggers,
time_of_day_sweet_spot,
)
# ---------------------------------------------------------------------------
# ASCII art & display helpers
# ---------------------------------------------------------------------------
HEADER = r"""
╔══════════════════════════════════════════════════════════════════╗
║ ║
║ ██████╗ ███████╗██╗ ██╗ ██╗██╗ ██╗ ██╗ ║
║ ██╔══██╗██╔════╝██║ ██║ ██║██║ ██║ ██║ ║
║ ██║ ██║█████╗ ██║ ██║ ██║██║ ██║ ██║ ║
║ ██║ ██║██╔══╝ ██║ ██║ ██║██║ ██║ ██║ ║
║ ██████╔╝███████╗███████╗╚██████╔╝███████╗╚██████╔╝ ║
║ ╚═════╝ ╚══════╝╚══════╝ ╚═════╝ ╚══════╝ ╚═════╝ ║
║ ║
║ ██████╗ ███████╗████████╗███████╗ ██████╗████████╗██╗██╗ ██╗███████╗ ║
║ ██╔══██╗██╔════╝╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝██║██║ ██║██╔════╝ ║
║ ██║ ██║█████╗ ██║ █████╗ ██║ ██║ ██║██║ ██║█████╗ ║
║ ██║ ██║██╔══╝ ██║ ██╔══╝ ██║ ██║ ██║╚██╗ ██╔╝██╔══╝ ║
║ ██████╔╝███████╗ ██║ ███████╗╚██████╗ ██║ ██║ ╚████╔╝ ███████╗ ║
║ ╚═════╝ ╚══════╝ ╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═══╝ ╚══════╝ ║
║ ║
║ "past vibe performance is not indicative ║
║ of future results" ║
║ ║
╚══════════════════════════════════════════════════════════════════╝
"""
DISCLAIMER = """
================================================================
DISCLAIMER: this is not financial advice. past vibe performance
is not indicative of future results. deluluisnotthesolulu is not
a licensed therapist, relationship counselor, or functioning adult.
================================================================
"""
SYSTEM_PROMPT = (
"You are deluluisnotthesolulu -a legendary SPORTS COMMENTATOR who analyzes "
"texting patterns the way ESPN analysts break down game tape. You are dramatic, "
"funny, and incapable of speaking about relationships without using sports "
"metaphors. Think Stephen A. Smith meets a relationship advice column.\n\n"
"Your style rules:\n"
"- Analyze every data point like it's a crucial play: fumbles, assists, "
"three-pointers, slam dunks, red cards, penalty kicks, Hail Marys, etc.\n"
"- Occasionally break into full play-by-play commentary mid-analysis.\n"
"- Be genuinely funny. Roast the user when the data warrants it.\n"
"- Despite the comedy, ALWAYS reference the REAL numbers and data provided. "
"Do not make up statistics.\n\n"
"Structure your analysis EXACTLY like this:\n\n"
"## 1. THE VIBE REPORT\n"
"Summarize the vibe trend data with full sports energy. Are we on a winning "
"streak or in a rebuilding year? Give the play-by-play on momentum.\n\n"
"## 2. PATTERN RECOGNITION\n"
"Break down the flagged patterns (word impact, ghost triggers, response times, "
"enthusiasm triggers, best texting times) with tactical implications. "
"What plays are working? What's getting intercepted?\n\n"
"## 3. CONFIDENCE METER\n"
"Present the confidence score out of 100 with DRAMATIC commentary. "
"Is this a championship-level connection or are we getting relegated?\n\n"
"## 4. TACTICAL ADVISORY\n"
"Give specific, actionable next-message advice based on ALL the data. "
"What play should the user call next? When should they send it? "
"What words should they use or avoid? Be concrete.\n\n"
"Remember: be entertaining but ACCURATE. Every claim must trace back to "
"the data in the briefing packet. And if the user is clearly delusional, "
"it is your DUTY to call a timeout and deliver a reality check."
)
def loading_msg(text):
"""Print a dramatic loading line with a short pause."""
print(f" [*] {text}", flush=True)
time.sleep(0.3)
# ---------------------------------------------------------------------------
# Data collection
# ---------------------------------------------------------------------------
def gather_intel():
"""Call every data function and return a dict of results."""
print("\n ── gathering intelligence... ──\n")
loading_msg("intercepting vibe frequencies...")
vibe_trend = get_vibe_trend(days=14)
loading_msg("decoding today's play-by-play...")
daily_summary = get_daily_summary()
loading_msg("calculating confidence rating...")
confidence = get_confidence_score()
loading_msg("running correlation matrix...")
correlations = get_correlation_highlights()
loading_msg("pulling recent transcripts...")
recent_msgs = get_recent_messages(n=50)
loading_msg("analyzing word impact on the field...")
word_impact = word_impact_analysis()
loading_msg("reviewing ghost tape...")
ghosts = ghost_triggers()
loading_msg("clocking her response times...")
resp_times = response_time_patterns()
loading_msg("identifying enthusiasm triggers...")
enthusiasm = her_enthusiasm_triggers()
loading_msg("mapping the sweet-spot schedule...")
sweet_spot = time_of_day_sweet_spot()
print("\n ── intelligence gathered. building briefing packet... ──\n")
return {
"vibe_trend": vibe_trend,
"daily_summary": daily_summary,
"confidence": confidence,
"correlations": correlations,
"recent_msgs": recent_msgs,
"word_impact": word_impact,
"ghosts": ghosts,
"resp_times": resp_times,
"enthusiasm": enthusiasm,
"sweet_spot": sweet_spot,
}
# ---------------------------------------------------------------------------
# Prompt assembly
# ---------------------------------------------------------------------------
def build_prompt(data):
"""Format all collected data into a structured briefing packet for the LLM."""
sections = []
# --- Vibe Trend ---
vt = data["vibe_trend"]
sections.append(
"=== CURRENT VIBE TREND DATA ===\n"
f"Recent average (7d): {vt.get('recent_avg')}\n"
f"Previous average (7d before that): {vt.get('previous_avg')}\n"
f"Delta: {vt.get('delta')}\n"
f"Direction: {vt.get('direction')}\n"
f"Current streak: {vt.get('current_streak')}\n"
f"Best recent day: {vt.get('best_recent_day')}\n"
f"Worst recent day: {vt.get('worst_recent_day')}"
)
# --- Today's Summary ---
ds = data["daily_summary"]
sections.append(
"=== TODAY'S SUMMARY ===\n"
f"Date: {ds.get('date')}\n"
f"Vibe score: {ds.get('vibe_score')}\n"
f"Total messages: {ds.get('msg_count')}\n"
f"Her messages: {ds.get('her_msg_count')}\n"
f"My messages: {ds.get('my_msg_count')}\n"
f"She initiated: {ds.get('she_initiated')}\n"
f"Avg her response time (min): {ds.get('avg_her_response_min')}\n"
f"Commentary: {ds.get('commentary')}"
)
# --- Confidence Breakdown ---
conf = data["confidence"]
breakdown_lines = "\n".join(
f" - {k}: {v}" for k, v in (conf.get("breakdown") or {}).items()
)
sections.append(
"=== CONFIDENCE BREAKDOWN ===\n"
f"Overall score: {conf.get('score')}/100\n"
f"Verdict: {conf.get('verdict')}\n"
f"Component scores:\n{breakdown_lines}"
)
# --- Statistical Correlations ---
corrs = data["correlations"]
corr_lines = []
for c in (corrs or []):
sig = "SIGNIFICANT" if c.get("significant") else "not significant"
corr_lines.append(
f" - {c.get('feature')}: r={c.get('r')}, p={c.get('p')} ({sig}) "
f"- {c.get('interpretation')}"
)
sections.append(
"=== STATISTICAL CORRELATIONS ===\n" + "\n".join(corr_lines)
)
# --- Word Impact Analysis (top 5) ---
wi = data["word_impact"]
wi_top = (wi or [])[:5]
wi_lines = []
for w in wi_top:
wi_lines.append(
f" - \"{w.get('word')}\": used {w.get('days_used')} days, "
f"avg vibe when used={w.get('avg_vibe_when_used')}, "
f"avg vibe when NOT used={w.get('avg_vibe_when_not')}, "
f"delta={w.get('delta')}, verdict={w.get('verdict')}"
)
sections.append(
"=== WORD IMPACT ANALYSIS (top 5) ===\n" + "\n".join(wi_lines)
)
# --- Ghost Trigger Analysis ---
gh = data["ghosts"]
gh_triggers = gh.get("ghost_instances") or gh.get("triggers") or []
gh_lines = []
for t in gh_triggers:
gh_lines.append(
f" - Date: {t.get('date')}, my last msg: \"{t.get('my_last_message')}\", "
f"my msg count that day: {t.get('my_msg_count_that_day')}"
)
sections.append(
"=== GHOST TRIGGER ANALYSIS ===\n"
f"Summary: {gh.get('summary')}\n"
f"Trigger incidents:\n" + "\n".join(gh_lines)
)
# --- Response Time Patterns ---
rt = data["resp_times"]
rt_lines = []
for category in rt.keys():
val = rt.get(category)
if val is not None:
rt_lines.append(f" - {category}: {val}")
sections.append(
"=== RESPONSE TIME PATTERNS (her avg response time) ===\n"
+ "\n".join(rt_lines)
)
# --- Enthusiasm Triggers ---
enth = data["enthusiasm"]
enth_lines = []
for m in (enth or [])[:10]:
if isinstance(m, dict):
enth_lines.append(f" - I said: \"{m.get('my_message')}\" -> She replied: \"{m.get('her_enthusiastic_reply')}\"")
else:
enth_lines.append(f" - \"{m}\"")
sections.append(
"=== HER ENTHUSIASM TRIGGERS (my messages that preceded her excited replies) ===\n"
+ "\n".join(enth_lines)
)
# --- Best Time to Text ---
ss = data["sweet_spot"]
ss_lines = []
for slot in (ss or []):
ss_lines.append(
f" - {slot.get('time_slot')}: avg vibe={slot.get('avg_vibe')}, "
f"msg count={slot.get('msg_count')}"
)
sections.append(
"=== BEST TIME TO TEXT ===\n" + "\n".join(ss_lines)
)
# --- Recent Conversation (last 20) ---
recent = (data["recent_msgs"] or [])[-20:]
convo_lines = []
for m in recent:
convo_lines.append(
f" [{m.get('timestamp')}] {m.get('sender')}: {m.get('text')}"
)
sections.append(
"=== RECENT CONVERSATION (last 20 messages) ===\n"
+ "\n".join(convo_lines)
)
prompt = (
"Here is the full briefing packet. Analyze ALL of this data and deliver "
"your scouting report.\n\n"
+ "\n\n".join(sections)
)
return prompt
# ---------------------------------------------------------------------------
# Ollama streaming call
# ---------------------------------------------------------------------------
def call_ollama(prompt):
"""Send the prompt to Ollama and stream the response to stdout."""
payload = json.dumps({
"model": "qwen2.5:32b",
"prompt": prompt,
"system": SYSTEM_PROMPT,
"stream": True,
"options": {"temperature": 0.8},
}).encode("utf-8")
req = urllib.request.Request(
"http://localhost:11434/api/generate",
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req) as resp:
for raw_line in resp:
line = raw_line.decode("utf-8").strip()
if not line:
continue
try:
chunk = json.loads(line)
except json.JSONDecodeError:
continue
token = chunk.get("response", "")
if token:
sys.stdout.write(token)
sys.stdout.flush()
if chunk.get("done"):
break
except urllib.error.URLError as exc:
print(
"\n [ERROR] Could not connect to Ollama at localhost:11434.\n"
f" Reason: {exc.reason}\n\n"
" Make sure Ollama is running:\n"
" 1. Install from https://ollama.com\n"
" 2. Run: ollama serve\n"
" 3. Pull the model: ollama pull qwen2.5:32b\n"
" 4. Re-run this script.\n"
)
sys.exit(1)
except ConnectionError as exc:
print(
"\n [ERROR] Connection to Ollama lost.\n"
f" Details: {exc}\n"
)
sys.exit(1)
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
print(HEADER)
# Collect all the data
data = gather_intel()
# Build the mega-prompt
prompt = build_prompt(data)
print(" ── patching into deluluisnotthesolulu... ──\n")
time.sleep(0.5)
# Stream the LLM analysis
call_ollama(prompt)
# Sign off
print("\n")
print(DISCLAIMER)
if __name__ == "__main__":
main()