SamiKoen commited on
Commit
2fb7273
·
1 Parent(s): b6da310

Live product image updates as assistant speaks (latest-mention wins)

Browse files
Files changed (1) hide show
  1. app.py +52 -15
app.py CHANGED
@@ -531,12 +531,11 @@ def _detect_colors_in_text(text_norm: str) -> set[str]:
531
  return found
532
 
533
  def find_main_product_in_text(text: str) -> dict | None:
534
- """Sorguda gecen ANA urunu bul (stok XML duserse fallback olarak kullanilir).
535
- Distinctive token kurallari:
536
- - Uzun kelimeler (>=4 karakter, ornek: 'marlin', 'domane') ZORUNLU
537
- - Model numaralari (1-2 basamakli sayilar: '4', '5', '6') ZORUNLU
538
- - Yil benzeri 4-basamakli sayilar (2026) optional
539
- TUM zorunlu tokenlar metinde gecmeli."""
540
  try:
541
  idx = _ensure_index()
542
  if not idx:
@@ -544,7 +543,7 @@ def find_main_product_in_text(text: str) -> dict | None:
544
  text_norm = _norm(text)
545
 
546
  best = None
547
- best_score = 0
548
  for p in idx["products"]:
549
  if p['is_variant']:
550
  continue
@@ -555,12 +554,20 @@ def find_main_product_in_text(text: str) -> dict | None:
555
  ]
556
  if not mandatory:
557
  continue
558
- if not all(t in text_norm for t in mandatory):
 
 
 
 
 
 
 
 
 
559
  continue
560
- # Tie-break: daha fazla token eslesen daha spesifik
561
- score = sum(1 for t in tokens if t in text_norm)
562
- if score > best_score:
563
- best_score = score
564
  best = p
565
  return _public_view(best)
566
  except Exception:
@@ -669,7 +676,13 @@ async def realtime_relay(client_ws: WebSocket):
669
 
670
  # Asistan transkripti per-response birikir, response.done'da urun aramasi yapilir
671
  # current_main_link: en son gosterilen ana urun (varyant aramasi icin scope)
672
- transcript_buf = {"text": "", "tool_link": None, "current_main_link": None}
 
 
 
 
 
 
673
 
674
  async def openai_to_client():
675
  try:
@@ -694,18 +707,41 @@ async def realtime_relay(client_ws: WebSocket):
694
  elif evt_type in ("input_audio_buffer.speech_started", "input_audio_buffer.speech_stopped", "input_audio_buffer.committed", "response.created"):
695
  logger.info(f"[Realtime] {evt_type}")
696
 
697
- # Yeni response basladiginda transkript buffer'i sifirla
 
698
  if evt_type == "response.created":
699
  transcript_buf["text"] = ""
 
 
 
 
 
700
  transcript_buf["tool_link"] = None
701
 
702
- # Asistan ses transkripti — fragmanlari biriktir
703
  if evt_type in ("response.audio_transcript.delta",
704
  "response.output_audio_transcript.delta",
705
  "response.output_text.delta"):
706
  d = data.get("delta", "")
707
  if isinstance(d, str):
708
  transcript_buf["text"] += d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
709
 
710
  # response.done — sadece RENK OVERRIDE icin transkript kullanilir.
711
  # Yeni ana urun gosterimi tamamen TOOL CAGRISINA birakilir (GPT'ye guven).
@@ -756,6 +792,7 @@ async def realtime_relay(client_ws: WebSocket):
756
  transcript_buf["tool_link"] = product.get("link") or product_link
757
  if product.get("link"):
758
  transcript_buf["current_main_link"] = product["link"]
 
759
  try:
760
  await client_ws.send_text(json.dumps({
761
  "type": "product.show",
 
531
  return found
532
 
533
  def find_main_product_in_text(text: str) -> dict | None:
534
+ """Metinde gecen ANA urunu bul. ZORUNLU tokenlar:
535
+ - 4+ karakter kelimeler (marlin, domane, emonda)
536
+ - 1-2 basamakli sayilar (model numaralari: 4, 5, 6)
537
+ Birden fazla urun gecerse, METINDE EN SON gecen kazanir
538
+ (asistan konustukca son bahsedilen urune gecisin)."""
 
539
  try:
540
  idx = _ensure_index()
541
  if not idx:
 
543
  text_norm = _norm(text)
544
 
545
  best = None
546
+ best_last_pos = -1
547
  for p in idx["products"]:
548
  if p['is_variant']:
549
  continue
 
554
  ]
555
  if not mandatory:
556
  continue
557
+ # Tum mandatory tokenlar metinde olmali
558
+ positions = []
559
+ ok = True
560
+ for t in mandatory:
561
+ pos = text_norm.rfind(t)
562
+ if pos < 0:
563
+ ok = False
564
+ break
565
+ positions.append(pos)
566
+ if not ok:
567
  continue
568
+ last_pos = max(positions)
569
+ if last_pos > best_last_pos:
570
+ best_last_pos = last_pos
 
571
  best = p
572
  return _public_view(best)
573
  except Exception:
 
676
 
677
  # Asistan transkripti per-response birikir, response.done'da urun aramasi yapilir
678
  # current_main_link: en son gosterilen ana urun (varyant aramasi icin scope)
679
+ transcript_buf = {
680
+ "text": "",
681
+ "tool_link": None,
682
+ "current_main_link": None,
683
+ "last_shown_in_response": None,
684
+ "last_check_len": 0,
685
+ }
686
 
687
  async def openai_to_client():
688
  try:
 
707
  elif evt_type in ("input_audio_buffer.speech_started", "input_audio_buffer.speech_stopped", "input_audio_buffer.committed", "response.created"):
708
  logger.info(f"[Realtime] {evt_type}")
709
 
710
+ # Yeni response basladiginda transkript ve live state sifirla
711
+ # tool_link KULLANICI YENI TURNE GECINCE sifirlanir (asagida)
712
  if evt_type == "response.created":
713
  transcript_buf["text"] = ""
714
+ transcript_buf["last_shown_in_response"] = None
715
+ transcript_buf["last_check_len"] = 0
716
+
717
+ # Kullanici yeni soru sormaya basladi — yeni turn
718
+ if evt_type == "input_audio_buffer.speech_started":
719
  transcript_buf["tool_link"] = None
720
 
721
+ # Asistan ses transkripti — fragmanlari biriktir + LIVE urun tespiti
722
  if evt_type in ("response.audio_transcript.delta",
723
  "response.output_audio_transcript.delta",
724
  "response.output_text.delta"):
725
  d = data.get("delta", "")
726
  if isinstance(d, str):
727
  transcript_buf["text"] += d
728
+ # Her ~80 char'da bir tara (perf icin throttle)
729
+ if len(transcript_buf["text"]) - transcript_buf["last_check_len"] >= 80:
730
+ transcript_buf["last_check_len"] = len(transcript_buf["text"])
731
+ live_p = find_main_product_in_text(transcript_buf["text"])
732
+ if live_p and live_p.get("image"):
733
+ live_link = live_p.get("link")
734
+ if live_link != transcript_buf["last_shown_in_response"]:
735
+ transcript_buf["last_shown_in_response"] = live_link
736
+ transcript_buf["current_main_link"] = live_link
737
+ logger.info(f"[product/live] {live_p['name']}")
738
+ try:
739
+ await client_ws.send_text(json.dumps({
740
+ "type": "product.show",
741
+ "product": live_p,
742
+ }))
743
+ except Exception:
744
+ pass
745
 
746
  # response.done — sadece RENK OVERRIDE icin transkript kullanilir.
747
  # Yeni ana urun gosterimi tamamen TOOL CAGRISINA birakilir (GPT'ye guven).
 
792
  transcript_buf["tool_link"] = product.get("link") or product_link
793
  if product.get("link"):
794
  transcript_buf["current_main_link"] = product["link"]
795
+ transcript_buf["last_shown_in_response"] = product["link"]
796
  try:
797
  await client_ws.send_text(json.dumps({
798
  "type": "product.show",