codebook / potato /ai /openrouter_endpoint.py
davidjurgens's picture
Deploy: Potato — Codebook Annotation
aceb1b2 verified
Raw
History Blame Contribute Delete
3.54 kB
"""
OpenRouter endpoint implementation.
This module provides integration with OpenRouter's API for LLM inference.
"""
import requests
from .ai_endpoint import BaseAIEndpoint, AIEndpointRequestError
DEFAULT_MODEL = "openai/gpt-4o-mini"
class OpenRouterEndpoint(BaseAIEndpoint):
"""OpenRouter endpoint for cloud-based LLM inference."""
# Models that support structured output
STRUCTURED_OUTPUT_MODELS = {
"openai/gpt-4o",
"openai/gpt-4o-mini",
"openai/gpt-4-turbo",
"anthropic/claude-3-5-sonnet",
"deepseek/deepseek-r1:free"
}
def _initialize_client(self) -> None:
"""Initialize the OpenAI client."""
api_key = self.ai_config.get("api_key", "")
if not api_key:
raise AIEndpointRequestError("OpenRouter API key is required")
def _get_default_model(self) -> str:
"""Get the default OpenAI model."""
return DEFAULT_MODEL
def supports_structured_output(self) -> bool:
"""Check if the current model supports structured output."""
model = self.model or DEFAULT_MODEL
return any(model.startswith(prefix.split('/')[0]) or model in self.STRUCTURED_OUTPUT_MODELS
for prefix in self.STRUCTURED_OUTPUT_MODELS)
def query(self, prompt: str, output_format: dict) -> str:
"""
Send a query to OpenRouter and return the response.
Args:
prompt: The prompt to send to the model (as messages list or string)
output_format: Pydantic model for structured output
Returns:
The model's response as a string
Raises:
AIEndpointRequestError: If the request fails
"""
try:
url = "https://openrouter.ai/api/v1/chat/completions"
headers = {
"Authorization": f"Bearer {self.ai_config.get('api_key')}",
"Content-Type": "application/json"
}
messages = [{"role": "user", "content": prompt}]
schema = output_format.model_json_schema()
body = {
"model": self.model or DEFAULT_MODEL,
"max_tokens": self.max_tokens,
"temperature": self.temperature,
}
# Handle structured output based on model support
if self.supports_structured_output():
body["messages"] = messages
body["response_format"] = {
"type": "json_schema",
"json_schema": {
"name": "response",
"schema": schema,
"strict": True
}
}
else:
# If model does not support structured format, just send raw prompt
body["messages"] = messages
r = requests.post(url, headers=headers, json=body)
if r.status_code >= 400:
raise AIEndpointRequestError(f"OpenRouter error {r.status_code}: {r.text}")
data = r.json()
if self.supports_structured_output():
return self.parseStringToJson(data["choices"][0]["message"]["content"])
return data["choices"][0]["message"]["content"]
except Exception as e:
raise AIEndpointRequestError(f"OpenRouter request failed: {e}")