Skip to main content
Glama
openrouter.py4.21 kB
""" OpenRouter service for LLM API calls with structured output support """ import os import json from typing import Any, Dict, List, Optional, Type, TypeVar from openai import OpenAI from pydantic import BaseModel DEFAULT_MODEL = "google/gemma-3-27b-it" T = TypeVar('T', bound=BaseModel) class OpenRouterService: """Service for making LLM calls through OpenRouter API""" def __init__(self, api_key: Optional[str] = None): self.api_key = api_key or os.getenv("OPENROUTER_API_KEY") if not self.api_key: raise ValueError("OPENROUTER_API_KEY environment variable is required") self.client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key=self.api_key ) async def completion( self, messages: List[Dict[str, str]], model: str = DEFAULT_MODEL, temperature: float = 0.1, max_tokens: Optional[int] = None ) -> Optional[str]: """Simple text completion""" try: response = self.client.chat.completions.create( model=model, messages=messages, temperature=temperature, max_tokens=max_tokens ) return response.choices[0].message.content except Exception as e: print(f"OpenRouter completion error: {e}") return None async def structured_completion( self, messages: List[Dict[str, str]], response_model: Type[T], model: str = DEFAULT_MODEL, temperature: float = 0.1, max_tokens: Optional[int] = None ) -> Optional[T]: """Structured completion with Pydantic model validation""" try: # Add JSON schema instruction to the last message schema = response_model.model_json_schema() schema_instruction = f""" Please respond with a valid JSON object that matches this exact schema: {json.dumps(schema, indent=2)} Respond ONLY with the JSON object, no additional text or formatting. """ # Clone messages and add schema instruction enhanced_messages = messages.copy() if enhanced_messages: enhanced_messages[-1]["content"] += "\n\n" + schema_instruction else: enhanced_messages.append({"role": "user", "content": schema_instruction}) response = self.client.chat.completions.create( model=model, messages=enhanced_messages, temperature=temperature, max_tokens=max_tokens ) content = response.choices[0].message.content if not content: return None # Try to parse JSON and validate with Pydantic model try: # Clean up response (remove markdown formatting if present) content = content.strip() if content.startswith("```json"): content = content[7:] if content.endswith("```"): content = content[:-3] content = content.strip() # Parse JSON and validate data = json.loads(content) return response_model.model_validate(data) except (json.JSONDecodeError, ValueError) as parse_error: print(f"Failed to parse structured response: {parse_error}") print(f"Raw response: {content}") return None except Exception as e: print(f"OpenRouter structured completion error: {e}") return None # Global service instance _openrouter_service: Optional[OpenRouterService] = None def get_openrouter_service() -> Optional[OpenRouterService]: """Get global OpenRouter service instance""" global _openrouter_service if _openrouter_service is None: try: _openrouter_service = OpenRouterService() except ValueError: # API key not available return None return _openrouter_service

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/GeorgeStrakhov/mcpeasy'

If you have feedback or need assistance with the MCP directory API, please join our Discord server