Spaces:
Running
Running
| 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 | |
| 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, | |
| ) | |