"""Realtime asistan icin tool implementasyonlari. WhatsApp BF-WAB'da kanitlanmis smart_warehouse_with_price.py'yi kullanir. """ from __future__ import annotations import logging import re from product_matcher import ( find_main_local_substring, find_main_product_in_text, find_main_via_nano, ) from warehouse_lookup import lookup_stock_for_query logger = logging.getLogger(__name__) # ---------- Telaffuz duzeltmeleri ---------- def apply_pronunciation_fixes(text: str) -> str: """Sesli asistan Ingilizce fonetik kullaniyor — TR 'C' sesi Ingilizce 'J' ile uyusur. Caddebostan/CADDEBOSTAN -> Jaddebostan (J = TR C)""" if not isinstance(text, str): return text for src in ("Caddebostan", "CADDEBOSTAN", "Cadde Bostan", "CADDE BOSTAN"): text = text.replace(src, "Jaddebostan") return text # ---------- URL'leri tool sonucundan strip ---------- def strip_urls(text: str) -> str: """Modelin URL okumamasi icin tool result'undan link satirlari kaldirilir.""" if not text: return text text = re.sub(r"(?im)^\s*(?:Link|URL|Url|🔗.*?):.*$", "", text) text = re.sub(r"https?://\S+", "", text) text = re.sub(r"\n{3,}", "\n\n", text).strip() return text # ---------- OpenAI realtime tool definition ---------- TOOLS = [ { "type": "function", "name": "show_product", "description": ( "Bir urun bahsedildiginde sag monitorde urun sayfasini OTOMATIK acar. " "Hizli ve hafif (XML/stok cekmez). Musteri sadece urun adi soylediginde " "veya hakkinda bilgi istediginde KULLAN — fiyat/gorsel/aciklama sayfada gorunur. " "Stok/depo sorulari icin kullanma." ), "parameters": { "type": "object", "properties": { "product_name": { "type": "string", "description": ( "Tam urun adi: marka + model + kategori " "(orn. 'Marlin 5', 'Madone SLR 9', 'Bontrager Comp sele')" ), } }, "required": ["product_name"], }, }, { "type": "function", "name": "check_warehouse_stock", "description": ( "Müşteri SADECE stok/depo durumunu sordugunda kullan: " "'var mi?', 'stokta mi?', 'Caddebostan'da var mi?', 'hangi magazada?'. " "Magaza-bazli stok bilgisi için BizimHesap XML'ini ceker (yavas/maliyetli). " "Sadece urun gostermek/aciklamak icin show_product kullan." ), "parameters": { "type": "object", "properties": { "user_message": { "type": "string", "description": ( "Musterinin orijinal stok/depo sorusu " "(orn. 'Madone SLR 9 Caddebostan'da var mi', 'Marlin 5 stokta mi')" ), } }, "required": ["user_message"], }, }, ] def show_product_local(product_name: str) -> str: """Hybrid matcher: 1) Local substring match (en kesin, bedava, anlik) — 'marlin 6' -> 'MARLIN 6 GEN 3' 2) Nano-first (gpt-5-nano) — fuzzy/eksik kelimeli sorgular icin 3) Local distinctive-token matcher — son catalog """ q = product_name or "" # 1) Substring (cogu net soru bunu eslesir, anlik) p = find_main_local_substring(q) if p: logger.info(f"[show] substring hit: {p.get('name')}") else: # 2) Nano (fuzzy/eksik/marka tutarsizligi durumlari) p = find_main_via_nano(q) if p: logger.info(f"[show] nano hit: {p.get('name')}") else: # 3) Token-based (network/quota fail icin) p = find_main_product_in_text(q) if not p: return f"Urun bulunamadi: {product_name}" name = p.get("name") or "" link = p.get("link") or "" if not link: return name return f"{name}\nLink: {link}" def _normalize_tool_input(arguments: dict) -> dict: """Tool arg'larinda 'Jaddebostan' (sesli telaffuz) -> 'Caddebostan' (gercek depo adi). BizimHesap'ta JADDEBOSTAN diye depo yok; arama bozulmasin.""" out = dict(arguments) for k, v in out.items(): if isinstance(v, str): out[k] = v.replace("Jaddebostan", "Caddebostan").replace("JADDEBOSTAN", "CADDEBOSTAN") return out def handle_tool_call_sync(name: str, arguments: dict) -> str: """Tool dispatcher. Sync — realtime relay'de to_thread ile sarilacak.""" arguments = _normalize_tool_input(arguments) try: if name == "show_product": q = arguments.get("product_name", "") logger.info(f"[tool] show_product: {q!r}") return apply_pronunciation_fixes(show_product_local(q)) if name == "check_warehouse_stock": msg = arguments.get("user_message", "") logger.info(f"[tool] check_warehouse_stock: {msg!r}") result = lookup_stock_for_query(msg) if result is None: return "Stok bilgisi bulunamadi." return apply_pronunciation_fixes(str(result)) return f"Bilinmeyen fonksiyon: {name}" except Exception as e: logger.exception(f"Tool call hatasi ({name})") return f"Hata: {e}"