TurboSkillSlug / openrouter_client.py
legendarydragontamer's picture
deploy
51a9974
Raw
History Blame Contribute Delete
3.6 kB
"""
OpenRouter client for the skill-uplift eval.
Reads the API key from the OPENROUTER_API_KEY environment variable. NEVER hardcode
the key; NEVER commit it. Set it in your shell: export OPENROUTER_API_KEY=sk-or-...
Provides two factory helpers that return `complete(prompt) -> str` callables bound
to a specific model, so the eval's answerer and grader can be DIFFERENT models
(important: the grader must not be the same model that answered, to limit bias).
"""
from __future__ import annotations
import os
import time
import httpx
OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"
_KEY_ENV = "OPENROUTER_API_KEY"
def _key() -> str:
k = os.environ.get(_KEY_ENV)
if not k:
raise RuntimeError(
f"{_KEY_ENV} is not set. Run: export {_KEY_ENV}=sk-or-... "
"(never paste the key into code or chat)."
)
return k
def make_completer(model: str, *, system: str | None = None,
temperature: float = 0.2, max_tokens: int = 900,
timeout: float = 90.0, retries: int = 2):
"""Return a complete(prompt)->str bound to `model` on OpenRouter."""
def complete(prompt: str) -> str:
messages = []
if system:
messages.append({"role": "system", "content": system})
messages.append({"role": "user", "content": prompt})
body = {"model": model, "messages": messages,
"temperature": temperature, "max_tokens": max_tokens}
headers = {
"Authorization": f"Bearer {_key()}",
"Content-Type": "application/json",
# OpenRouter likes these for attribution; harmless if omitted.
"HTTP-Referer": "https://huggingface.co/spaces/build-small-hackathon/TurboSkillSlug",
"X-Title": "TurboSkillSlug skill-uplift eval",
}
last_err = None
for attempt in range(retries + 1):
try:
r = httpx.post(OPENROUTER_URL, json=body, headers=headers, timeout=timeout)
r.raise_for_status()
data = r.json()
return data["choices"][0]["message"]["content"]
except Exception as e:
last_err = e
time.sleep(1.5 * (attempt + 1))
raise RuntimeError(f"OpenRouter call to {model} failed after retries: {last_err}")
return complete
# Frontier model ids drift over time. These defaults reflect the OpenRouter
# catalog as of June 2026; OVERRIDE via env if the catalog has moved. Answerer and
# grader are intentionally DIFFERENT vendors to reduce self-grading bias.
DEFAULT_ANSWERER_MODEL = os.environ.get("EVAL_ANSWERER_MODEL", "anthropic/claude-opus-4.6")
DEFAULT_GRADER_MODEL = os.environ.get("EVAL_GRADER_MODEL", "openai/gpt-5.1")
def list_models() -> list[str]:
"""Fetch the live OpenRouter model catalog (ids only). Use this to verify a
model id is currently valid before running, so a renamed model fails loud,
not mid-eval."""
headers = {"Authorization": f"Bearer {_key()}"}
r = httpx.get("https://openrouter.ai/api/v1/models", headers=headers, timeout=30)
r.raise_for_status()
return [m["id"] for m in r.json().get("data", [])]
def verify_models(*model_ids: str) -> dict:
"""Return {model_id: is_available} by checking against the live catalog.
Call this before run_eval so a stale id surfaces immediately."""
try:
catalog = set(list_models())
except Exception as e:
return {mid: f"could not verify ({e})" for mid in model_ids}
return {mid: (mid in catalog) for mid in model_ids}