| import time
|
|
|
| try:
|
| from huggingface_hub import InferenceClient
|
| except ImportError:
|
| InferenceClient = None
|
| try:
|
| from openai import OpenAI
|
| except ImportError:
|
| OpenAI = None
|
|
|
| from config import (
|
| HF_MODEL, HF_TOKEN, GROQ_API_KEY, GROQ_MODEL, OPENAI_API_KEY, OPENAI_MODEL,
|
| LLM_PROVIDER, MAX_NEW_TOKENS
|
| )
|
|
|
|
|
|
|
|
|
|
|
| REQUEST_TIMEOUT_SECONDS = 25.0
|
|
|
| HF_CLIENT = (
|
| InferenceClient(model=HF_MODEL, token=HF_TOKEN, timeout=REQUEST_TIMEOUT_SECONDS)
|
| if HF_TOKEN and InferenceClient
|
| else None
|
| )
|
| GROQ_CLIENT = (
|
| OpenAI(api_key=GROQ_API_KEY, base_url="https://api.groq.com/openai/v1", timeout=REQUEST_TIMEOUT_SECONDS)
|
| if GROQ_API_KEY and OpenAI
|
| else None
|
| )
|
| OPENAI_CLIENT = (
|
| OpenAI(api_key=OPENAI_API_KEY, timeout=REQUEST_TIMEOUT_SECONDS)
|
| if OPENAI_API_KEY and OpenAI
|
| else None
|
| )
|
|
|
|
|
|
|
|
|
|
|
|
|
| RETRY_ATTEMPTS = 2
|
| RETRY_BACKOFF_SECONDS = 1.5
|
|
|
|
|
| def llm_available() -> bool:
|
| if LLM_PROVIDER == "auto":
|
| return any((HF_CLIENT, GROQ_CLIENT, OPENAI_CLIENT))
|
| if LLM_PROVIDER == "groq":
|
| return GROQ_CLIENT is not None
|
| if LLM_PROVIDER == "openai":
|
| return OPENAI_CLIENT is not None
|
| return HF_CLIENT is not None
|
|
|
|
|
| def call_chat_completion(messages: list[dict], response_format: str = "text") -> str:
|
| """Call the configured chat completion provider.
|
|
|
| response_format:
|
| "text" (default) — no format constraint, unchanged behavior.
|
| "json" — for providers that support it (OpenAI, Groq's OpenAI-
|
| compatible API), enforce {"type": "json_object"} at the API
|
| level rather than relying solely on prompt wording. Callers
|
| that expect strict JSON (e.g. proof_bundle.llm_claim_prompt)
|
| should pass response_format="json".
|
| """
|
| provider = LLM_PROVIDER
|
| if provider == "auto":
|
| provider = "huggingface" if HF_CLIENT is not None else "groq" if GROQ_CLIENT is not None else "openai"
|
|
|
| last_error: Exception | None = None
|
| for attempt in range(RETRY_ATTEMPTS):
|
| try:
|
| return _dispatch_chat_completion(provider, messages, response_format)
|
| except Exception as error:
|
| last_error = error
|
| if attempt < RETRY_ATTEMPTS - 1:
|
| time.sleep(RETRY_BACKOFF_SECONDS)
|
| continue
|
| assert last_error is not None
|
| raise last_error
|
|
|
|
|
| def _dispatch_chat_completion(provider: str, messages: list[dict], response_format: str) -> str:
|
| extra_kwargs = {}
|
| if response_format == "json" and provider in {"groq", "openai"}:
|
| extra_kwargs["response_format"] = {"type": "json_object"}
|
|
|
| if provider == "groq":
|
| if GROQ_CLIENT is None:
|
| raise RuntimeError("GROQ_API_KEY tanımlı değil.")
|
| response = GROQ_CLIENT.chat.completions.create(
|
| model=GROQ_MODEL,
|
| messages=messages,
|
| max_tokens=MAX_NEW_TOKENS,
|
| temperature=0.12,
|
| top_p=0.9,
|
| **extra_kwargs,
|
| )
|
| return response.choices[0].message.content.strip()
|
|
|
| if provider == "openai":
|
| if OPENAI_CLIENT is None:
|
| raise RuntimeError("OPENAI_API_KEY tanımlı değil.")
|
| response = OPENAI_CLIENT.chat.completions.create(
|
| model=OPENAI_MODEL,
|
| messages=messages,
|
| max_tokens=MAX_NEW_TOKENS,
|
| temperature=0.0,
|
| top_p=0.9,
|
| **extra_kwargs,
|
| )
|
| return response.choices[0].message.content.strip()
|
|
|
| if HF_CLIENT is None:
|
| raise RuntimeError("HF_TOKEN veya HUGGINGFACEHUB_API_TOKEN tanımlı değil.")
|
| response = HF_CLIENT.chat.completions.create(
|
| model=HF_MODEL,
|
| messages=messages,
|
| max_tokens=MAX_NEW_TOKENS,
|
| temperature=0.12,
|
| top_p=0.9,
|
| )
|
| return response.choices[0].message.content.strip() |