Spaces:
Running
Running
SamiKoen commited on
Commit ·
2fb7273
1
Parent(s): b6da310
Live product image updates as assistant speaks (latest-mention wins)
Browse files
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 |
-
"""
|
| 535 |
-
|
| 536 |
-
-
|
| 537 |
-
|
| 538 |
-
|
| 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 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 559 |
continue
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 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 = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
| 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",
|