ariankhalfani commited on
Commit
bc34c69
ยท
verified ยท
1 Parent(s): 40f6e3d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +428 -0
app.py ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import json
4
+ import tempfile
5
+ import random
6
+ from datetime import datetime
7
+ from typing import List, Dict, Any, Tuple
8
+ import dspy
9
+ from dataclasses import dataclass
10
+ import gradio as gr
11
+ from dotenv import load_dotenv
12
+ import os
13
+
14
+ load_dotenv()
15
+
16
+ api_key = os.getenv("API_KEY")
17
+
18
+
19
+ if api_key:
20
+ openrouter_lm = dspy.LM(
21
+ model="openrouter/deepseek/deepseek-chat-v3-0324:free",
22
+ api_base="https://openrouter.ai/api/v1",
23
+ api_key=api_key
24
+ )
25
+ dspy.configure(lm=openrouter_lm)
26
+
27
+ @dataclass
28
+ class ChatMessage:
29
+ timestamp: str
30
+ sender: str
31
+ message: str
32
+ datetime_obj: datetime
33
+
34
+ # === ENHANCED LLM SIGNATURES ===
35
+ class DeepContextualLoveAnalysis(dspy.Signature):
36
+ """Advanced contextual analysis of love and emotional connection through conversation patterns"""
37
+ conversation_context = dspy.InputField(desc="Balanced sample of conversation messages across different time periods")
38
+ user_name = dspy.InputField(desc="Name of the user receiving messages")
39
+ partner_name = dspy.InputField(desc="Name of the partner sending messages")
40
+ total_messages = dspy.InputField(desc="Total number of messages in conversation")
41
+
42
+ love_percentage = dspy.OutputField(desc="Love intensity score (0-100) based on emotional expression, care, and affection patterns")
43
+ emotional_connection = dspy.OutputField(desc="Quality and depth of emotional bond demonstrated")
44
+ communication_style = dspy.OutputField(desc="How partner expresses feelings and maintains connection")
45
+ relationship_investment = dspy.OutputField(desc="Level of investment and commitment shown in conversations")
46
+ affection_patterns = dspy.OutputField(desc="Specific ways love and care are expressed")
47
+ intimacy_level = dspy.OutputField(desc="Emotional intimacy and closeness demonstrated")
48
+
49
+ class RelationshipDynamicsAnalysis(dspy.Signature):
50
+ """Analyze overall relationship health and compatibility"""
51
+ love_context = dspy.InputField(desc="Results from deep love analysis")
52
+ conversation_dynamics = dspy.InputField(desc="Overall conversation patterns and frequency")
53
+
54
+ relationship_health = dspy.OutputField(desc="Overall relationship wellness score (0-100)")
55
+ compatibility_indicators = dspy.OutputField(desc="Signs of long-term compatibility")
56
+ communication_quality = dspy.OutputField(desc="Effectiveness and warmth of communication")
57
+ growth_potential = dspy.OutputField(desc="Potential for relationship development")
58
+ unique_strengths = dspy.OutputField(desc="What makes this relationship special")
59
+ future_outlook = dspy.OutputField(desc="Likely trajectory of the relationship")
60
+
61
+ # === ENHANCED LOVE ANALYZER ===
62
+ class AdvancedLoveAnalyzer:
63
+ def __init__(self):
64
+ self.love_analyzer = dspy.Predict(DeepContextualLoveAnalysis)
65
+ self.dynamics_analyzer = dspy.Predict(RelationshipDynamicsAnalysis)
66
+ random.seed(42) # For reproducible sampling
67
+
68
+ def parse_messages(self, text: str) -> List[ChatMessage]:
69
+ """Enhanced message parsing with better error handling"""
70
+ messages = []
71
+ patterns = [
72
+ r'\[(\d{2}/\d{2}/\d{2}, \d{2}\.\d{2}\.\d{2})\] ([^:]+): (.+)',
73
+ r'(\d{2}/\d{2}/\d{2}, \d{2}\.\d{2}\.\d{2}) - ([^:]+): (.+)',
74
+ r'(\d{1,2}/\d{1,2}/\d{2,4}, \d{1,2}:\d{2}) - ([^:]+): (.+)',
75
+ r'(\d{1,2}/\d{1,2}/\d{2,4}, \d{1,2}:\d{2}:\d{2}) - ([^:]+): (.+)',
76
+ ]
77
+
78
+ for line in text.strip().split('\n'):
79
+ line = line.strip()
80
+ if not line or line.startswith('โ€Ž') or len(line) < 10:
81
+ continue
82
+
83
+ for pattern in patterns:
84
+ match = re.match(pattern, line)
85
+ if match:
86
+ timestamp_str, sender, message = match.groups()
87
+ try:
88
+ # Enhanced timestamp parsing
89
+ timestamp_formats = [
90
+ '%d/%m/%y, %H.%M.%S',
91
+ '%d/%m/%y, %H:%M',
92
+ '%m/%d/%Y, %H:%M',
93
+ '%d/%m/%Y, %H:%M:%S',
94
+ '%m/%d/%y, %H:%M'
95
+ ]
96
+
97
+ dt = None
98
+ for fmt in timestamp_formats:
99
+ try:
100
+ dt = datetime.strptime(timestamp_str, fmt)
101
+ break
102
+ except ValueError:
103
+ continue
104
+
105
+ if dt:
106
+ messages.append(ChatMessage(
107
+ timestamp_str,
108
+ sender.strip(),
109
+ message.strip(),
110
+ dt
111
+ ))
112
+ break
113
+ except Exception:
114
+ continue
115
+
116
+ return sorted(messages, key=lambda x: x.datetime_obj)
117
+
118
+ def identify_partner(self, messages: List[ChatMessage], user_name: str) -> str:
119
+ """Smart partner identification"""
120
+ participants = {}
121
+ user_variations = [user_name.lower(), user_name.split()[0].lower(), "thariq", "arian"]
122
+
123
+ for msg in messages:
124
+ sender = msg.sender.strip()
125
+ if sender in participants:
126
+ participants[sender] += 1
127
+ else:
128
+ participants[sender] = 1
129
+
130
+ # Find partner (not user)
131
+ for participant, count in sorted(participants.items(), key=lambda x: x[1], reverse=True):
132
+ if not any(var in participant.lower() for var in user_variations):
133
+ return participant
134
+
135
+ return list(participants.keys())[0] if participants else "Unknown"
136
+
137
+ def balanced_sampling(self, messages: List[ChatMessage], sample_size: int = 200) -> List[ChatMessage]:
138
+ """Create balanced random sample across conversation timeline"""
139
+ if len(messages) <= sample_size:
140
+ return messages
141
+
142
+ # Sort by timestamp
143
+ sorted_messages = sorted(messages, key=lambda x: x.datetime_obj)
144
+ total_msgs = len(sorted_messages)
145
+
146
+ # Divide into time segments for balanced sampling
147
+ num_segments = min(10, total_msgs // 20) # At least 20 messages per segment
148
+ segment_size = total_msgs // num_segments
149
+ sampled_messages = []
150
+
151
+ messages_per_segment = sample_size // num_segments
152
+ remaining_samples = sample_size % num_segments
153
+
154
+ for i in range(num_segments):
155
+ start_idx = i * segment_size
156
+ end_idx = start_idx + segment_size if i < num_segments - 1 else total_msgs
157
+ segment_messages = sorted_messages[start_idx:end_idx]
158
+
159
+ # Sample from this segment
160
+ segment_sample_size = messages_per_segment
161
+ if i < remaining_samples:
162
+ segment_sample_size += 1
163
+
164
+ if len(segment_messages) <= segment_sample_size:
165
+ sampled_messages.extend(segment_messages)
166
+ else:
167
+ sampled_messages.extend(random.sample(segment_messages, segment_sample_size))
168
+
169
+ return sorted(sampled_messages, key=lambda x: x.datetime_obj)
170
+
171
+ def analyze_love_from_file(self, file_path: str, user_name: str = "Thariq Arian") -> Dict[str, Any]:
172
+ """Enhanced love analysis with balanced sampling and context focus"""
173
+ # Parse messages
174
+ with open(file_path, 'r', encoding='utf-8') as f:
175
+ content = f.read()
176
+
177
+ messages = self.parse_messages(content)
178
+ if not messages:
179
+ return {"error": "No messages found"}
180
+
181
+ partner_name = self.identify_partner(messages, user_name)
182
+ partner_messages = [msg for msg in messages if msg.sender == partner_name]
183
+
184
+ if not partner_messages:
185
+ return {"error": f"No messages found from partner: {partner_name}"}
186
+
187
+ # Balanced sampling
188
+ sampled_messages = self.balanced_sampling(partner_messages, 200)
189
+
190
+ # Prepare context for LLM
191
+ conversation_context = "\n".join([
192
+ f"[{msg.timestamp}] {msg.message}"
193
+ for msg in sampled_messages
194
+ ])
195
+
196
+ try:
197
+ # Deep love analysis
198
+ love_analysis = self.love_analyzer(
199
+ conversation_context=conversation_context,
200
+ user_name=user_name,
201
+ partner_name=partner_name,
202
+ total_messages=str(len(partner_messages))
203
+ )
204
+
205
+ # Relationship dynamics analysis
206
+ dynamics = self.dynamics_analyzer(
207
+ love_context=json.dumps({
208
+ 'love_percentage': love_analysis.love_percentage,
209
+ 'emotional_connection': love_analysis.emotional_connection,
210
+ 'communication_style': love_analysis.communication_style,
211
+ 'relationship_investment': love_analysis.relationship_investment
212
+ }),
213
+ conversation_dynamics=f"Total: {len(partner_messages)}, Sampled: {len(sampled_messages)}, Time span: {partner_messages[0].timestamp} to {partner_messages[-1].timestamp}"
214
+ )
215
+
216
+ return {
217
+ 'user_name': user_name,
218
+ 'partner_name': partner_name,
219
+ 'total_messages': len(partner_messages),
220
+ 'analyzed_sample': len(sampled_messages),
221
+ 'love_analysis': {
222
+ 'love_percentage': love_analysis.love_percentage,
223
+ 'emotional_connection': love_analysis.emotional_connection,
224
+ 'communication_style': love_analysis.communication_style,
225
+ 'relationship_investment': love_analysis.relationship_investment,
226
+ 'affection_patterns': love_analysis.affection_patterns,
227
+ 'intimacy_level': love_analysis.intimacy_level
228
+ },
229
+ 'relationship_dynamics': {
230
+ 'relationship_health': dynamics.relationship_health,
231
+ 'compatibility_indicators': dynamics.compatibility_indicators,
232
+ 'communication_quality': dynamics.communication_quality,
233
+ 'growth_potential': dynamics.growth_potential,
234
+ 'unique_strengths': dynamics.unique_strengths,
235
+ 'future_outlook': dynamics.future_outlook
236
+ },
237
+ 'analysis_timestamp': datetime.now().isoformat()
238
+ }
239
+
240
+ except Exception as e:
241
+ return {"error": str(e)}
242
+
243
+ def extract_score(self, score_text: str) -> int:
244
+ """Extract numeric score from LLM response"""
245
+ try:
246
+ # Look for percentage or number
247
+ numbers = re.findall(r'\b(\d{1,3})\b', str(score_text))
248
+ if numbers:
249
+ score = int(numbers[0])
250
+ return min(100, max(0, score)) # Clamp to 0-100
251
+ return 0
252
+ except:
253
+ return 0
254
+
255
+ def generate_clean_report(self, analysis_result: Dict[str, Any]) -> str:
256
+ """Generate clean, readable report without excessive characters"""
257
+ if 'error' in analysis_result:
258
+ return f"Analysis failed: {analysis_result['error']}"
259
+
260
+ love_data = analysis_result['love_analysis']
261
+ dynamics = analysis_result['relationship_dynamics']
262
+
263
+ # Extract clean love score
264
+ love_score = self.extract_score(love_data['love_percentage'])
265
+ health_score = self.extract_score(dynamics['relationship_health'])
266
+
267
+ # Simple love meter
268
+ hearts = "โค๏ธ" * (love_score // 10)
269
+ empty_hearts = "๐Ÿค" * (10 - love_score // 10)
270
+ meter = hearts + empty_hearts
271
+
272
+ # Love verdict
273
+ if love_score >= 85:
274
+ verdict = "DEEPLY IN LOVE"
275
+ elif love_score >= 70:
276
+ verdict = "STRONG LOVE"
277
+ elif love_score >= 55:
278
+ verdict = "GENUINE AFFECTION"
279
+ elif love_score >= 40:
280
+ verdict = "CARING FEELINGS"
281
+ else:
282
+ verdict = "FRIENDLY FEELINGS"
283
+
284
+ report = f"""
285
+ LOVE ANALYSIS REPORT
286
+ {analysis_result['partner_name']} โ†’ {analysis_result['user_name']}
287
+
288
+ LOVE SCORE: {love_score}%
289
+ {meter}
290
+ VERDICT: {verdict}
291
+
292
+ EMOTIONAL ANALYSIS:
293
+ Connection Level: {love_data['emotional_connection']}
294
+ Communication Style: {love_data['communication_style']}
295
+ Relationship Investment: {love_data['relationship_investment']}
296
+ Affection Patterns: {love_data['affection_patterns']}
297
+ Intimacy Level: {love_data['intimacy_level']}
298
+
299
+ RELATIONSHIP DYNAMICS:
300
+ Health Score: {health_score}%
301
+ Compatibility: {dynamics['compatibility_indicators']}
302
+ Communication Quality: {dynamics['communication_quality']}
303
+ Growth Potential: {dynamics['growth_potential']}
304
+ Unique Strengths: {dynamics['unique_strengths']}
305
+ Future Outlook: {dynamics['future_outlook']}
306
+
307
+ ANALYSIS SUMMARY:
308
+ Total Messages: {analysis_result['total_messages']}
309
+ Sample Analyzed: {analysis_result['analyzed_sample']}
310
+ Analysis Date: {datetime.now().strftime('%Y-%m-%d %H:%M')}
311
+
312
+ Advanced Context-Based Love Analyzer v4.0
313
+ """
314
+ return report
315
+
316
+ # === GRADIO FUNCTIONS ===
317
+ def process_love_analysis(uploaded_file) -> Tuple[str, str]:
318
+ """Process uploaded file and return analysis results"""
319
+ if uploaded_file is None:
320
+ return None, "โŒ Please upload a .txt file first!"
321
+
322
+ try:
323
+ analyzer = AdvancedLoveAnalyzer()
324
+
325
+ # Read uploaded file
326
+ file_path = uploaded_file.name
327
+ debug_info = f"๐Ÿ“ Processing file: {os.path.basename(file_path)}\n"
328
+
329
+ # Analyze love
330
+ debug_info += "๐Ÿ”ฎ Starting advanced love analysis...\n"
331
+ result = analyzer.analyze_love_from_file(file_path, "Thariq Arian")
332
+
333
+ if 'error' in result:
334
+ debug_info += f"โŒ Error: {result['error']}\n"
335
+ return None, debug_info
336
+
337
+ # Generate report
338
+ debug_info += f"๐Ÿ’• Found partner: {result['partner_name']}\n"
339
+ debug_info += f"๐Ÿ“Š Analyzed {result['analyzed_sample']} out of {result['total_messages']} messages\n"
340
+ debug_info += f"โœจ Love score: {analyzer.extract_score(result['love_analysis']['love_percentage'])}%\n"
341
+ debug_info += "๐ŸŽ‰ Analysis completed successfully!"
342
+
343
+ report = analyzer.generate_clean_report(result)
344
+
345
+ # Save report to temporary file
346
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as f:
347
+ f.write(report)
348
+ temp_file_path = f.name
349
+
350
+ return temp_file_path, debug_info
351
+
352
+ except Exception as e:
353
+ error_msg = f"โŒ Analysis failed: {str(e)}"
354
+ return None, error_msg
355
+
356
+ # === GRADIO APP ===
357
+ with gr.Blocks(title="Ultimate Crush Detector", theme=gr.themes.Monochrome()) as app:
358
+ gr.Markdown(
359
+ """
360
+ # ๐Ÿ’˜ <span style='color:#ff99cc;'>Ultimate Crush Detector</span>
361
+ ---
362
+ <p style='font-size:1.1em;'>Upload your <code>.txt</code> file and click <strong>Submit</strong> to reveal your ultimate crush โœจ</p>
363
+ """,
364
+ elem_id="title-markdown"
365
+ )
366
+
367
+ with gr.Column(scale=1):
368
+ input_file = gr.File(
369
+ label="๐Ÿ’Œ Upload your Love Letter (.txt)",
370
+ file_types=[".txt"],
371
+ elem_id="input-file"
372
+ )
373
+ submit_btn = gr.Button("๐Ÿ’– Reveal My Crush ๐Ÿ’–", variant="primary", elem_id="submit-btn")
374
+
375
+ output_file = gr.File(
376
+ label="๐Ÿ“ฅ Download your result (.txt)"
377
+ )
378
+
379
+ debug_box = gr.Textbox(
380
+ label="๐Ÿ”ฎ Debug & Love Notes",
381
+ placeholder="Status, secrets, and logs will appear here...",
382
+ interactive=False,
383
+ lines=5
384
+ )
385
+
386
+ submit_btn.click(
387
+ process_love_analysis,
388
+ inputs=input_file,
389
+ outputs=[output_file, debug_box]
390
+ )
391
+
392
+ # Advanced Love Theme Custom Styling โ€” Full width
393
+ app.css = """
394
+ body {
395
+ background: radial-gradient(circle at top, #1a0023, #3a0b52, #8900a1);
396
+ color: #ff99cc;
397
+ font-family: 'Orbitron', sans-serif;
398
+ }
399
+
400
+ #title-markdown h1 {
401
+ font-family: 'Pacifico', cursive;
402
+ font-weight: 800;
403
+ text-shadow: 0 0 15px #ff99cc;
404
+ }
405
+
406
+ .gr-button {
407
+ background: linear-gradient(135deg, #ff66b2 0%, #ff0080 100%);
408
+ border: none;
409
+ color: #fff;
410
+ font-weight: bold;
411
+ font-size: 1.2em;
412
+ width: 100% !important;
413
+ padding: 1em;
414
+ border-radius: 12px;
415
+ }
416
+
417
+ .gr-button:hover {
418
+ box-shadow: 0 0 20px #ff66b2;
419
+ transform: scale(1.02);
420
+ }
421
+
422
+ #input-file, .gr-file, .gr-file .upload-box {
423
+ width: 100% !important;
424
+ }
425
+ """
426
+
427
+ if __name__ == "__main__":
428
+ app.launch(share=False)