testspace / evals /src /clients.py
nilshoehing's picture
Upload TopoBench Space app
67acd34 verified
Raw
History Blame Contribute Delete
9.02 kB
from __future__ import annotations
import json
import os
from dataclasses import dataclass
from typing import Any
from dotenv import load_dotenv
load_dotenv()
def _parse_extra_arg(value: str) -> Any:
"""Parse CLI request args while allowing plain strings."""
try:
return json.loads(value)
except json.JSONDecodeError:
return value
def parse_request_args(pairs: list[str] | None) -> dict[str, Any]:
parsed: dict[str, Any] = {}
for pair in pairs or []:
if "=" not in pair:
raise ValueError(
f"Invalid --request-arg '{pair}'. Expected KEY=VALUE."
)
key, raw_value = pair.split("=", 1)
key = key.strip()
if not key:
raise ValueError(f"Invalid --request-arg '{pair}'. Empty key.")
parsed[key] = _parse_extra_arg(raw_value.strip())
return parsed
@dataclass(frozen=True)
class GenerationResult:
text: str
input_tokens: int | None = None
output_tokens: int | None = None
total_tokens: int | None = None
raw: dict[str, Any] | None = None
class ModelClient:
def generate(
self,
provider: str,
model: str,
prompt: str,
*,
max_output_tokens: int,
temperature: float | None,
extra_args: dict[str, Any] | None = None,
) -> GenerationResult:
provider = provider.lower()
extra_args = dict(extra_args or {})
if provider == "openai":
return self._generate_openai(
model,
prompt,
max_output_tokens=max_output_tokens,
temperature=temperature,
extra_args=extra_args,
)
if provider == "openrouter":
return self._generate_openrouter(
model,
prompt,
max_output_tokens=max_output_tokens,
temperature=temperature,
extra_args=extra_args,
)
if provider == "deepseek":
return self._generate_deepseek(
model,
prompt,
max_output_tokens=max_output_tokens,
temperature=temperature,
extra_args=extra_args,
)
if provider == "anthropic":
return self._generate_anthropic(
model,
prompt,
max_output_tokens=max_output_tokens,
temperature=temperature,
extra_args=extra_args,
)
if provider == "google":
return self._generate_google(
model,
prompt,
temperature=temperature,
extra_args=extra_args,
)
raise ValueError(
f"Unsupported provider '{provider}'. Use openai, openrouter, "
"deepseek, anthropic, or google."
)
def _generate_openai(
self,
model: str,
prompt: str,
*,
max_output_tokens: int,
temperature: float | None,
extra_args: dict[str, Any],
) -> GenerationResult:
from openai import OpenAI
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
params: dict[str, Any] = {
"model": model,
"input": prompt,
"max_output_tokens": max_output_tokens,
}
if temperature is not None:
params["temperature"] = temperature
params.update(extra_args)
response = client.responses.create(**params)
usage = getattr(response, "usage", None)
return GenerationResult(
text=response.output_text or "",
input_tokens=getattr(usage, "input_tokens", None),
output_tokens=getattr(usage, "output_tokens", None),
total_tokens=getattr(usage, "total_tokens", None),
raw=response.model_dump() if hasattr(response, "model_dump") else None,
)
def _generate_openrouter(
self,
model: str,
prompt: str,
*,
max_output_tokens: int,
temperature: float | None,
extra_args: dict[str, Any],
) -> GenerationResult:
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("OPENROUTER_API_KEY")
or os.environ.get("OPENROUTER_KEY"),
base_url="https://openrouter.ai/api/v1",
)
params: dict[str, Any] = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": max_output_tokens,
}
if temperature is not None:
params["temperature"] = temperature
params.update(extra_args)
response = client.chat.completions.create(**params)
usage = getattr(response, "usage", None)
message = response.choices[0].message
return GenerationResult(
text=message.content or "",
input_tokens=getattr(usage, "prompt_tokens", None),
output_tokens=getattr(usage, "completion_tokens", None),
total_tokens=getattr(usage, "total_tokens", None),
raw=response.model_dump() if hasattr(response, "model_dump") else None,
)
def _generate_deepseek(
self,
model: str,
prompt: str,
*,
max_output_tokens: int,
temperature: float | None,
extra_args: dict[str, Any],
) -> GenerationResult:
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com",
)
params: dict[str, Any] = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": max_output_tokens,
}
if temperature is not None:
params["temperature"] = temperature
params.update(extra_args)
response = client.chat.completions.create(**params)
usage = getattr(response, "usage", None)
message = response.choices[0].message
return GenerationResult(
text=message.content or "",
input_tokens=getattr(usage, "prompt_tokens", None),
output_tokens=getattr(usage, "completion_tokens", None),
total_tokens=getattr(usage, "total_tokens", None),
raw=response.model_dump() if hasattr(response, "model_dump") else None,
)
def _generate_anthropic(
self,
model: str,
prompt: str,
*,
max_output_tokens: int,
temperature: float | None,
extra_args: dict[str, Any],
) -> GenerationResult:
from anthropic import Anthropic
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
params: dict[str, Any] = {
"model": model,
"max_tokens": max_output_tokens,
"messages": [{"role": "user", "content": prompt}],
}
if temperature is not None:
params["temperature"] = temperature
params.update(extra_args)
response = client.messages.create(**params)
text_parts = [part.text for part in response.content if getattr(part, "text", "")]
usage = getattr(response, "usage", None)
input_tokens = getattr(usage, "input_tokens", None)
output_tokens = getattr(usage, "output_tokens", None)
total_tokens = None
if input_tokens is not None and output_tokens is not None:
total_tokens = input_tokens + output_tokens
return GenerationResult(
text="".join(text_parts),
input_tokens=input_tokens,
output_tokens=output_tokens,
total_tokens=total_tokens,
raw=response.model_dump() if hasattr(response, "model_dump") else None,
)
def _generate_google(
self,
model: str,
prompt: str,
*,
temperature: float | None,
extra_args: dict[str, Any],
) -> GenerationResult:
from google import genai
client = genai.Client(api_key=os.environ.get("GOOGLE_API_KEY"))
config = dict(extra_args)
if temperature is not None:
config["temperature"] = temperature
response = client.models.generate_content(
model=model,
contents=prompt,
config=config or None,
)
text = getattr(response, "text", None) or ""
usage = getattr(response, "usage_metadata", None)
input_tokens = getattr(usage, "prompt_token_count", None)
output_tokens = getattr(usage, "candidates_token_count", None)
total_tokens = getattr(usage, "total_token_count", None)
raw = response.model_dump() if hasattr(response, "model_dump") else None
return GenerationResult(
text=text,
input_tokens=input_tokens,
output_tokens=output_tokens,
total_tokens=total_tokens,
raw=raw,
)