CoolFace
Apppublic

senemde/time-zone-tool-calling

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
app.py339 linesDownload Raw Back to root
1"""2Tool Calling Demo — Saat Dilimi Farkı3=====================================4 5Bir LLM'in kullanıcı sorusuna göre doğru fonksiyonları (Tool / Function6Calling) çağırmasını sağlayan, timeapi.io (ücretsiz, anahtarsız) API'sinden7gerçek veri çeken ve çağrılan araçları arayüzde açıkça gösteren bir uygulama.8 9Araçlar:10  - get_current_time(timezone)                 -> o an ilgili zaman diliminde saat + UTC farkı11  - time_difference(timezone1, timezone2)      -> iki zaman dilimi arasındaki saat farkı12 13Not: timeapi.io'ya ulaşılamazsa Python'un yerleşik `zoneinfo` (IANA veritabanı)14kütüphanesine otomatik düşülür; böylece demo her koşulda çalışır.15 16Model: Hugging Face Inference Providers üzerinden tool-calling destekli bir17sohbet modeli (varsayılan: openai/gpt-oss-120b).18"""19 20import os21import json22from datetime import datetime23 24import requests25import gradio as gr26from huggingface_hub import InferenceClient27 28try:29    from zoneinfo import ZoneInfo  # Python 3.9+30except ImportError:  # çok eski sürümler için31    ZoneInfo = None32 33# ---------------------------------------------------------------------------34# ZeroGPU uyumluluğu35# ---------------------------------------------------------------------------36# HF Spaces ZeroGPU donanımı, başlatılabilmek için en az bir @spaces.GPU37# fonksiyonu ister. Bu uygulama GPU kullanmaz (yalnızca API çağırır); aşağıdaki38# fonksiyon sadece ZeroGPU'nun başlatma kontrolünü geçmek için vardır.39try:40    import spaces41 42    @spaces.GPU43    def _zerogpu_warmup():44        return True45except Exception:46    pass47 48MODEL_ID = os.environ.get("MODEL_ID", "openai/gpt-oss-120b")49HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACEHUB_API_TOKEN")50HF_PROVIDER = os.environ.get("HF_PROVIDER", "auto")51MAX_TURNS = 6  # Sonsuz döngüye karşı üst sınır52 53_client_kwargs = {"model": MODEL_ID, "token": HF_TOKEN}54if HF_PROVIDER:55    _client_kwargs["provider"] = HF_PROVIDER56client = InferenceClient(**_client_kwargs)57 58TIMEAPI_BASE = "https://timeapi.io/api"59 60 61def _fetch_zone(timezone: str) -> dict:62    """63    Önce timeapi.io public API'sinden dener; olmazsa yerel zoneinfo'ya düşer.64    Döner: {"timezone", "local_time", "utc_offset_hours"}65    """66    # 1) Birincil yol: public API67    try:68        r = requests.get(69            f"{TIMEAPI_BASE}/timezone/zone",70            params={"timeZone": timezone},71            timeout=10,72        )73        r.raise_for_status()74        data = r.json()75        offset_sec = data["currentUtcOffset"]["seconds"]76        local_raw = data.get("currentLocalTime", "")77        # "2026-07-30T15:30:00.123" -> "2026-07-30 15:30:00"78        local_time = local_raw.replace("T", " ").split(".")[0]79        return {80            "timezone": data.get("timeZone", timezone),81            "local_time": local_time,82            "utc_offset_hours": round(offset_sec / 3600, 2),83            "source": "timeapi.io",84        }85    except Exception:86        pass  # API başarısız -> yedek yola geç87 88    # 2) Yedek yol: yerel IANA veritabanı89    if ZoneInfo is not None:90        try:91            now = datetime.now(ZoneInfo(timezone))92            return {93                "timezone": timezone,94                "local_time": now.strftime("%Y-%m-%d %H:%M:%S"),95                "utc_offset_hours": round(now.utcoffset().total_seconds() / 3600, 2),96                "source": "zoneinfo (yerel)",97            }98        except Exception:99            pass100 101    return {"error": f"'{timezone}' geçerli bir IANA zaman dilimi değil (örn. 'Europe/Istanbul')."}102 103 104def get_current_time(timezone: str) -> dict:105    """Belirtilen IANA zaman diliminde güncel saati ve UTC farkını döndürür."""106    return _fetch_zone(timezone)107 108 109def time_difference(timezone1: str, timezone2: str) -> dict:110    """İki IANA zaman dilimi arasındaki saat farkını hesaplar."""111    z1 = _fetch_zone(timezone1)112    z2 = _fetch_zone(timezone2)113    if "error" in z1:114        return z1115    if "error" in z2:116        return z2117 118    diff = round(z2["utc_offset_hours"] - z1["utc_offset_hours"], 2)119    if diff > 0:120        note = f"{z2['timezone']}, {z1['timezone']}'dan {abs(diff)} saat ileridedir."121    elif diff < 0:122        note = f"{z2['timezone']}, {z1['timezone']}'dan {abs(diff)} saat geridedir."123    else:124        note = f"{z1['timezone']} ve {z2['timezone']} aynı saat dilimindedir."125 126    return {127        "timezone1": z1["timezone"],128        "timezone2": z2["timezone"],129        "difference_hours": diff,130        "note": note,131    }132 133 134TOOL_FUNCS = {135    "get_current_time": get_current_time,136    "time_difference": time_difference,137}138 139 140TOOLS = [141    {142        "type": "function",143        "function": {144            "name": "get_current_time",145            "description": (146                "Belirtilen zaman diliminde o anki yerel saati ve UTC farkını "147                "döndürür."148            ),149            "parameters": {150                "type": "object",151                "properties": {152                    "timezone": {153                        "type": "string",154                        "description": (155                            "IANA zaman dilimi kimliği, örn. 'Europe/Istanbul', "156                            "'America/New_York', 'Asia/Tokyo'. Kullanıcı şehir adı "157                            "verirse uygun IANA kimliğine çevir."158                        ),159                    }160                },161                "required": ["timezone"],162            },163        },164    },165    {166        "type": "function",167        "function": {168            "name": "time_difference",169            "description": "İki zaman dilimi arasındaki saat farkını hesaplar.",170            "parameters": {171                "type": "object",172                "properties": {173                    "timezone1": {174                        "type": "string",175                        "description": "Birinci IANA zaman dilimi, örn. 'America/New_York'.",176                    },177                    "timezone2": {178                        "type": "string",179                        "description": "İkinci IANA zaman dilimi, örn. 'Asia/Tokyo'.",180                    },181                },182                "required": ["timezone1", "timezone2"],183            },184        },185    },186]187 188SYSTEM_PROMPT = (189    "Sen araç kullanabilen yardımcı bir asistansın. Saat ve zaman dilimi "190    "sorularında ASLA bilgi uydurma; her zaman verilen araçları "191    "(get_current_time, time_difference) çağırarak gerçek verilere ulaş. "192    "Kullanıcı şehir adı verirse ('New York', 'Tokyo', 'İstanbul'), bunu doğru "193    "IANA zaman dilimi kimliğine çevir (örn. 'America/New_York', 'Asia/Tokyo', "194    "'Europe/Istanbul'). Birden fazla yer sorulduğunda her biri için ayrı araç "195    "çağır. Son yanıtını kullanıcının diliyle, kısa ve net ver."196)197 198 199def _fmt_args(args: dict) -> str:200    return ", ".join(f"{k}={v!r}" for k, v in args.items())201 202 203def run_agent(user_message: str, history_messages: list):204    """Modeli araçlarla çalıştırır. (nihai_yanit, adim_izi) döner."""205    if not HF_TOKEN:206        return (207            "⚠️ HF_TOKEN ayarlı değil. HF Spaces > Settings > Secrets bölümünden "208            "`HF_TOKEN` ekleyin (huggingface.co/settings/tokens).",209            "",210        )211 212    messages = [{"role": "system", "content": SYSTEM_PROMPT}]213    messages.extend(history_messages)214    messages.append({"role": "user", "content": user_message})215 216    trace_lines = []217 218    for turn in range(1, MAX_TURNS + 1):219        try:220            resp = client.chat_completion(221                messages=messages,222                tools=TOOLS,223                tool_choice="auto",224                max_tokens=1024,225                temperature=0.2,226            )227        except Exception as exc:228            return f"Model çağrısı başarısız oldu: {exc}", "\n".join(trace_lines)229 230        msg = resp.choices[0].message231        tool_calls = msg.tool_calls or []232 233        if not tool_calls:234            final = msg.content or "(boş yanıt)"235            if trace_lines:236                trace_lines.append(f"\n[Turn {turn}] Nihai Yanıt:")237                trace_lines.append(final)238            return final, "\n".join(trace_lines)239 240        messages.append(241            {242                "role": "assistant",243                "content": msg.content or "",244                "tool_calls": [245                    {246                        "id": tc.id,247                        "type": "function",248                        "function": {249                            "name": tc.function.name,250                            "arguments": tc.function.arguments,251                        },252                    }253                    for tc in tool_calls254                ],255            }256        )257 258        trace_lines.append(f"[Turn {turn}] Araç Çağrıları:")259        for tc in tool_calls:260            name = tc.function.name261            try:262                args = json.loads(tc.function.arguments or "{}")263            except json.JSONDecodeError:264                args = {}265 266            func = TOOL_FUNCS.get(name)267            result = func(**args) if func else {"error": f"Bilinmeyen araç: {name}"}268 269            trace_lines.append(f"   -> {name}({_fmt_args(args)})")270            trace_lines.append(f"   <- {result}")271 272            messages.append(273                {274                    "role": "tool",275                    "tool_call_id": tc.id,276                    "name": name,277                    "content": json.dumps(result, ensure_ascii=False),278                }279            )280 281    return "Adım sınırına ulaşıldı, yanıt tamamlanamadı.", "\n".join(trace_lines)282 283 284def respond(message, chat_history):285    history_messages = [286        {"role": t["role"], "content": t["content"]} for t in chat_history287    ]288    final, trace = run_agent(message, history_messages)289    chat_history = chat_history + [290        {"role": "user", "content": message},291        {"role": "assistant", "content": final},292    ]293    return chat_history, trace, ""294 295 296with gr.Blocks(title="Tool Calling — Saat Dilimi Farkı") as demo:297    gr.Markdown(298        "# 🕐 Tool Calling Demo — Saat Dilimi Farkı\n"299        "Model, sorunuza göre **`get_current_time`** ve **`time_difference`** "300        "araçlarını otomatik çağırır. Sağ tarafta arka planda çağrılan araçları "301        "ve adımları görebilirsiniz.\n\n"302        f"**Model:** `{MODEL_ID}` · **Veri kaynağı:** timeapi.io"303    )304 305    with gr.Row():306        with gr.Column(scale=3):307            chatbot = gr.Chatbot(height=420, label="Sohbet")308            msg = gr.Textbox(309                placeholder="Örn: New York ile Tokyo arasında kaç saat fark var, şu an saat kaç?",310                label="Sorunuz",311            )312            with gr.Row():313                send = gr.Button("Gönder", variant="primary")314                clear = gr.Button("Temizle")315        with gr.Column(scale=2):316            trace_box = gr.Textbox(317                label="🔧 Araç Çağrı Adımları (Tool Trace)",318                lines=22,319                interactive=False,320            )321 322    gr.Examples(323        examples=[324            "New York ile Tokyo arasında kaç saat fark var, şu an oralarda saat kaç?",325            "İstanbul'da şu an saat kaç?",326            "Londra mı ileride Los Angeles mı, aradaki fark nedir?",327            "Sidney ile Berlin arasındaki saat farkı kaç saat?",328        ],329        inputs=msg,330    )331 332    send.click(respond, [msg, chatbot], [chatbot, trace_box, msg])333    msg.submit(respond, [msg, chatbot], [chatbot, trace_box, msg])334    clear.click(lambda: ([], "", ""), None, [chatbot, trace_box, msg])335 336 337if __name__ == "__main__":338    demo.launch()339