ginnyxxxxxxx commited on
Commit
4db620f
Β·
1 Parent(s): 64743fe
Files changed (1) hide show
  1. app.py +87 -76
app.py CHANGED
@@ -290,50 +290,56 @@ def render_chain(s1_text, s2_text, s3_text, status="done"):
290
  s2_active = status in ("running2", "running3", "done")
291
  s3_active = status in ("running3", "done")
292
 
293
- # ── Stage 1 ───────────────────────────────────────────────────────────────
294
  if status == "running1":
295
  s1_content = f'<div class="thinking">Extracting features {_dots()}</div>'
296
  elif s1_text:
297
  tags = []
298
- # Parse LOCATION INVENTORY bullets: "- Name: N visits, description"
299
- in_inventory = False
300
- for line in s1_text.splitlines():
301
- line = line.strip()
302
- if "LOCATION INVENTORY" in line.upper():
303
- in_inventory = True
304
  continue
305
- if in_inventory:
306
- if line.startswith("TEMPORAL") or line.startswith("SEQUENCE") or (line and not line.startswith("-") and not line.startswith("*") and len(line) > 40):
307
- break
308
- if line.startswith("-"):
309
- # "- Name: N visits, type" or "- Name (N visits)"
310
- clean = line.lstrip("-").strip()
311
- # Shorten: keep "Name (N visits)" style
312
- m = re.match(r'(.+?):\s*(\d+)\s*visit', clean, re.IGNORECASE)
313
- if m:
314
- name = m.group(1).strip()
315
- n = m.group(2)
316
- tags.append(f"{name} Β· {n}x")
317
- elif clean:
318
- tags.append(clean[:55])
 
 
 
 
 
 
319
  if len(tags) >= 8:
320
  break
321
 
322
- # Fallback: also grab temporal summary line
323
  temporal_line = ""
324
- for line in s1_text.splitlines():
325
- line = line.strip()
326
- if "weekly distribution" in line.lower() or "weekday" in line.lower():
327
- temporal_line = line.lstrip("-").strip()[:70]
328
  break
329
 
330
- tag_html = "".join(f'<span class="tag">{t}</span>' for t in tags)
331
  temp_html = f'<div class="temporal-line">⏱ {temporal_line}</div>' if temporal_line else ""
332
  s1_content = f'<div class="tag-row">{tag_html}</div>{temp_html}'
333
  else:
334
  s1_content = '<div class="empty-hint">Press β–Ά to start</div>'
335
 
336
- # ── Stage 2 ───────────────────────────────────────────────────────────────
337
  KEYS = [
338
  ("SCHEDULE", ["ROUTINE", "SCHEDULE"]),
339
  ("ECONOMIC", ["ECONOMIC", "SPENDING", "FINANCIAL"]),
@@ -343,77 +349,82 @@ def render_chain(s1_text, s2_text, s3_text, status="done"):
343
  if status == "running2":
344
  s2_content = f'<div class="thinking" style="color:#a06030">Analyzing behavior {_dots()}</div>'
345
  elif s2_text:
346
- # Parse numbered sections
347
  sections = {}
348
  current_key = None
349
- current_bullets = []
350
  for line in s2_text.splitlines():
351
- line = line.strip()
352
- m = re.match(r'^\d+\.\s+(.+?)(?:\s+ANALYSIS)?(?:\s+PATTERNS)?(?:\s+INDICATORS)?:\s*$', line, re.IGNORECASE)
 
353
  if m:
354
- if current_key:
355
- sections[current_key] = current_bullets
356
- current_key = m.group(1).upper()
357
- current_bullets = []
358
- elif current_key and line.startswith("-"):
359
- bullet = line.lstrip("-").strip()
360
- if bullet:
361
- current_bullets.append(bullet)
362
- if current_key:
363
- sections[current_key] = current_bullets
 
 
 
 
 
 
364
 
365
  rows_html = ""
366
  for label, search_words in KEYS:
367
  val = "β€”"
368
- for k, bullets in sections.items():
369
- if any(w in k for w in search_words) and bullets:
370
- # Take first bullet, truncate at 2 sentences
371
- text = bullets[0]
372
- sentences = re.split(r'(?<=[.!?])\s+', text)
373
- val = " ".join(sentences[:2])
374
- if len(val) > 100:
375
- val = val[:97] + "..."
376
  break
377
  rows_html += f'<div class="bkey">{label}</div><div class="bval">{val}</div>'
378
  s2_content = f'<div class="behavior-row">{rows_html}</div>'
379
  else:
380
  s2_content = '<div class="empty-hint">Waiting...</div>'
381
 
382
- # ── Stage 3 ───────────────────────────────────────────────────────────────
383
  if status == "running3":
384
  s3_content = f'<div class="thinking" style="color:#c0392b">Inferring demographics {_dots()}</div>'
385
  elif s3_text:
386
- pred = reasoning = ""
387
- lines = s3_text.splitlines()
388
- i = 0
389
- while i < len(lines):
390
- line = lines[i].strip()
391
- if line.startswith("INCOME_PREDICTION:"):
392
- pred = line.replace("INCOME_PREDICTION:", "").strip()
393
- elif line.startswith("INCOME_REASONING:"):
394
- reasoning = line.replace("INCOME_REASONING:", "").strip()
395
- # Collect continuation lines until blank or next key
396
- i += 1
397
- while i < len(lines):
398
- nxt = lines[i].strip()
399
- if not nxt or nxt.startswith("INCOME_") or re.match(r'^\d+\.', nxt):
400
- break
401
- reasoning += " " + nxt
402
- i += 1
403
- continue
404
- i += 1
405
 
406
- # Truncate reasoning to ~2 sentences
407
- sentences = re.split(r'(?<=[.!?])\s+', reasoning.strip())
408
- short_reasoning = " ".join(sentences[:2])
409
- if len(short_reasoning) > 160:
410
- short_reasoning = short_reasoning[:157] + "..."
 
411
 
412
  s3_content = f"""
413
  <div class="pred-block">
414
  <div class="pred-label">Income Prediction</div>
415
  <div class="pred-value">{pred or "β€”"}</div>
416
- <div class="reasoning-text">{short_reasoning}</div>
417
  </div>"""
418
  else:
419
  s3_content = '<div class="empty-hint">Waiting...</div>'
 
290
  s2_active = status in ("running2", "running3", "done")
291
  s3_active = status in ("running3", "done")
292
 
293
+ # ── Stage 1: Location pills + temporal line ───────────────────────────────
294
  if status == "running1":
295
  s1_content = f'<div class="thinking">Extracting features {_dots()}</div>'
296
  elif s1_text:
297
  tags = []
298
+ lines = s1_text.splitlines()
299
+ # Find frequency section: "Frequency of visits" or direct "- Name: N visits"
300
+ freq_mode = False
301
+ for line in lines:
302
+ stripped = line.strip()
303
+ if not stripped:
304
  continue
305
+ # Enter frequency section
306
+ if re.search(r'frequency of visits', stripped, re.IGNORECASE):
307
+ freq_mode = True
308
+ continue
309
+ # Exit on next section heading
310
+ if freq_mode and re.match(r'^[A-Z][A-Z\s]+:', stripped) and not stripped.startswith('-'):
311
+ break
312
+ if freq_mode and stripped.startswith('-'):
313
+ # "- Name: N visits" or " - Name: N visits"
314
+ m = re.match(r'-+\s*(.+?):\s*(\d+)\s*visit', stripped, re.IGNORECASE)
315
+ if m:
316
+ tags.append(f"{m.group(1).strip()} Β· {m.group(2)}x")
317
+ if len(tags) >= 8:
318
+ break
319
+ continue
320
+ # Fallback: direct "- Name: N visits, description" under LOCATION INVENTORY
321
+ if not freq_mode:
322
+ m = re.match(r'-\s*(.+?):\s*(\d+)\s*visit', stripped, re.IGNORECASE)
323
+ if m and 'LOCATION' not in stripped.upper():
324
+ tags.append(f"{m.group(1).strip()} Β· {m.group(2)}x")
325
  if len(tags) >= 8:
326
  break
327
 
328
+ # Temporal summary line
329
  temporal_line = ""
330
+ for line in lines:
331
+ stripped = line.strip().lstrip('-').strip()
332
+ if re.search(r'\d+%\s*weekday', stripped, re.IGNORECASE):
333
+ temporal_line = stripped[:80]
334
  break
335
 
336
+ tag_html = "".join(f'<span class="tag">{t}</span>' for t in tags)
337
  temp_html = f'<div class="temporal-line">⏱ {temporal_line}</div>' if temporal_line else ""
338
  s1_content = f'<div class="tag-row">{tag_html}</div>{temp_html}'
339
  else:
340
  s1_content = '<div class="empty-hint">Press β–Ά to start</div>'
341
 
342
+ # ── Stage 2: 4 behavior dimensions ───────────────────────────────────────
343
  KEYS = [
344
  ("SCHEDULE", ["ROUTINE", "SCHEDULE"]),
345
  ("ECONOMIC", ["ECONOMIC", "SPENDING", "FINANCIAL"]),
 
349
  if status == "running2":
350
  s2_content = f'<div class="thinking" style="color:#a06030">Analyzing behavior {_dots()}</div>'
351
  elif s2_text:
352
+ # Parse both "1. HEADING:" and "STEP 1: HEADING" formats
353
  sections = {}
354
  current_key = None
355
+ current_sents = []
356
  for line in s2_text.splitlines():
357
+ stripped = line.strip()
358
+ # Match "1. TITLE ANALYSIS:" or "STEP 1: TITLE ANALYSIS"
359
+ m = re.match(r'^(?:\d+\.|STEP\s*\d+:)\s+(.+?)(?:\s+ANALYSIS|\s+PATTERNS|\s+INDICATORS|\s+BEHAVIOR|\s+CHARACTERISTICS)?:\s*$', stripped, re.IGNORECASE)
360
  if m:
361
+ if current_key and current_sents:
362
+ sections[current_key] = " ".join(current_sents)
363
+ current_key = m.group(1).upper().strip()
364
+ current_sents = []
365
+ elif current_key:
366
+ # Grab sub-bullet text (skip sub-headings like "1.1")
367
+ if re.match(r'^\d+\.\d+', stripped):
368
+ # sub-heading: extract its inline content
369
+ sub = re.sub(r'^\d+\.\d+[^:]*:\s*', '', stripped)
370
+ if sub:
371
+ current_sents.append(sub)
372
+ elif stripped.startswith('-'):
373
+ current_sents.append(stripped.lstrip('-').strip())
374
+
375
+ if current_key and current_sents:
376
+ sections[current_key] = " ".join(current_sents)
377
 
378
  rows_html = ""
379
  for label, search_words in KEYS:
380
  val = "β€”"
381
+ for k, text in sections.items():
382
+ if any(w in k for w in search_words) and text:
383
+ # First 2 sentences
384
+ sents = re.split(r'(?<=[.!?])\s+', text.strip())
385
+ val = " ".join(sents[:2])
386
+ if len(val) > 110:
387
+ val = val[:107] + "..."
 
388
  break
389
  rows_html += f'<div class="bkey">{label}</div><div class="bval">{val}</div>'
390
  s2_content = f'<div class="behavior-row">{rows_html}</div>'
391
  else:
392
  s2_content = '<div class="empty-hint">Waiting...</div>'
393
 
394
+ # ── Stage 3: prediction + full reasoning ─────────────────────────────────
395
  if status == "running3":
396
  s3_content = f'<div class="thinking" style="color:#c0392b">Inferring demographics {_dots()}</div>'
397
  elif s3_text:
398
+ pred = ""
399
+ reasoning_lines = []
400
+ in_reasoning = False
401
+ for line in s3_text.splitlines():
402
+ stripped = line.strip()
403
+ if stripped.startswith("INCOME_PREDICTION:"):
404
+ pred = stripped.replace("INCOME_PREDICTION:", "").strip()
405
+ in_reasoning = False
406
+ elif stripped.startswith("INCOME_REASONING:"):
407
+ in_reasoning = True
408
+ reasoning_lines.append(stripped.replace("INCOME_REASONING:", "").strip())
409
+ elif in_reasoning:
410
+ # Stop at second prediction block
411
+ if re.match(r'^2\.', stripped) or stripped.startswith("INCOME_CONFIDENCE"):
412
+ break
413
+ if stripped:
414
+ reasoning_lines.append(stripped)
 
 
415
 
416
+ reasoning = " ".join(reasoning_lines).strip()
417
+ # Truncate to 3 sentences
418
+ sents = re.split(r'(?<=[.!?])\s+', reasoning)
419
+ short = " ".join(sents[:3])
420
+ if len(short) > 220:
421
+ short = short[:217] + "..."
422
 
423
  s3_content = f"""
424
  <div class="pred-block">
425
  <div class="pred-label">Income Prediction</div>
426
  <div class="pred-value">{pred or "β€”"}</div>
427
+ <div class="reasoning-text">{short}</div>
428
  </div>"""
429
  else:
430
  s3_content = '<div class="empty-hint">Waiting...</div>'