Files
AIPass/src/aipass/api/apps/handlers/openrouter/models.py
T
AIOSAIandClaude Opus 4.6 babedd9c64 feat(system): seedgo v2 operational, full system audit, 14/15 branches at 99%
Three days of intensive work bringing seedgo to full operational status
and driving all branches through comprehensive standards compliance.

Seedgo v2.0.0:
- 22 checkers active (up from 20), standards pack fully operational
- New introspection standard researched from Dev-Pass, FPLAN-0017 open
- Bypass system for false positives (.seedgo config)
- Standards query and audit commands fully functional

System-wide audit (FPLAN-0016):
- All 14 auditable branches at 99%+ compliance
- CLI imports standardized across all branches (console from cli.apps.modules)
- handle_command(command, args) → bool contract added to all modules
- print_help() function naming fixed for checker pattern matching
- Handler extraction: large modules split, file I/O moved to handler layer
- New handlers created across ai_mail, backup, daemon, flow, skills, spawn, seedgo

Branch-specific highlights:
- ai_mail: email.py split 840→420 lines, 4 new handlers
- flow: dplan_flow.py 688→591 lines, 4 new handlers
- seedgo: massive restructure — standards moved to handlers/aipass_standards/,
  old standards/ tree removed, bypass system added, diagnostics module
- commons: database module added, CLI imports fixed
- skills: 5 handle_commands added, help function renamed
- trigger: error reporter handler, handle_command routing
- All branches: consistent architecture, clean drone routing

Culture doc (CLAUDE.md) added — documents AIPass philosophy, identity,
memory system, and collaboration principles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 01:26:42 -07:00

400 lines
12 KiB
Python

# =================== AIPass ====================
# Name: models.py
# Description: OpenRouter Model Management
# Version: 1.0.0
# Created: 2025-11-16
# Modified: 2025-11-16
# =============================================
"""
OpenRouter Model Management Handler
Business logic for querying and filtering OpenRouter models:
- Fetch all available models from OpenRouter API
- Filter models by pricing (free models)
- Parse model data and capabilities
- Extract model metadata (context, pricing, capabilities)
Extracted from legacy archive files (openrouter.py, find_free_models.py).
"""
# AIPASS_ROOT import pattern
import sys
from pathlib import Path
# Standard library imports
from typing import Dict, List, Optional
# Logging
from aipass.prax import logger
# Third-party imports
import requests
# Internal imports
from aipass.api.apps.handlers.auth.keys import get_api_key
# =============================================
# CONSTANTS
# =============================================
OPENROUTER_API_URL = "https://openrouter.ai/api/v1/models"
DEFAULT_TIMEOUT = 10
MODULE_NAME = "openrouter.models"
# =============================================
# CORE FUNCTIONS
# =============================================
def get_available_models(api_key: Optional[str] = None) -> List[Dict]:
"""
Fetch all models from OpenRouter API
Returns full model data including pricing, context length,
and capabilities for all available models.
Args:
api_key: Optional OpenRouter API key (fetches from keys handler if None)
Returns:
List of model dictionaries with full metadata, empty list on failure
Example:
>>> models = get_available_models()
>>> logger.info(f"Found {len(models)} models")
"""
try:
# Get API key if not provided
if not api_key:
api_key = get_api_key("openrouter")
if not api_key:
# logger.info(f"[{MODULE_NAME}] No API key available for OpenRouter")
logger.warning("No OpenRouter API key found")
return []
# Fetch models from API
models = fetch_models_from_api(api_key)
if models:
# logger.info(f"[{MODULE_NAME}] Fetched {len(models)} models from OpenRouter")
return models
else:
# logger.info(f"[{MODULE_NAME}] No models returned from API")
return []
except Exception as e:
# logger.info(f"[{MODULE_NAME}] Failed to get available models: {e}")
logger.error(f"Error fetching models: {e}")
return []
def get_free_models(api_key: Optional[str] = None) -> List[Dict]:
"""
Fetch only free models ($0 pricing) from OpenRouter
Filters models where both prompt and completion costs are zero.
Useful for finding models that can be used without charges.
Args:
api_key: Optional OpenRouter API key (fetches from keys handler if None)
Returns:
List of free model dictionaries with full metadata, empty list on failure
Example:
>>> free_models = get_free_models()
>>> for model in free_models:
... logger.info(f"Free: {model['id']}")
"""
try:
# Get all models first
all_models = get_available_models(api_key)
if not all_models:
return []
# Filter for free models
free_models = filter_by_pricing(all_models, max_cost=0.0)
# logger.info(f"[{MODULE_NAME}] Found {len(free_models)} free models")
return free_models
except Exception as e:
# logger.info(f"[{MODULE_NAME}] Failed to get free models: {e}")
logger.error(f"Error fetching free models: {e}")
return []
def fetch_models_from_api(api_key: str) -> List[Dict]:
"""
Query OpenRouter models endpoint and parse response
Makes HTTP request to OpenRouter API and extracts model data.
Handles authentication, timeouts, and error responses.
Args:
api_key: Valid OpenRouter API key
Returns:
List of model dictionaries, empty list on failure
Raises:
No exceptions raised - returns empty list on all errors
"""
try:
# Prepare request headers
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
# Make API request
# logger.info(f"[{MODULE_NAME}] Requesting models from OpenRouter API")
response = requests.get(
OPENROUTER_API_URL,
headers=headers,
timeout=DEFAULT_TIMEOUT
)
# Check response status
if response.status_code != 200:
# logger.info(f"[{MODULE_NAME}] API request failed with status {response.status_code}")
logger.error(f"OpenRouter API error: {response.status_code}")
return []
# Parse JSON response
data = response.json()
# Extract models from response
if "data" in data and isinstance(data["data"], list):
models = data["data"]
# logger.info(f"[{MODULE_NAME}] Successfully parsed {len(models)} models")
return models
else:
# logger.info(f"[{MODULE_NAME}] Invalid response format - no 'data' field")
return []
except requests.exceptions.Timeout:
# logger.info(f"[{MODULE_NAME}] API request timeout after {DEFAULT_TIMEOUT}s")
logger.error("Request timeout - OpenRouter API not responding")
return []
except requests.exceptions.RequestException as e:
# logger.info(f"[{MODULE_NAME}] Network error: {e}")
logger.error(f"Network error: {e}")
return []
except ValueError as e:
# logger.info(f"[{MODULE_NAME}] JSON parse error: {e}")
logger.error("Invalid JSON response from API")
return []
except Exception as e:
# logger.info(f"[{MODULE_NAME}] Unexpected error fetching models: {e}")
logger.error(f"Error: {e}")
return []
def filter_by_pricing(models: List[Dict], max_cost: float = 0.0) -> List[Dict]:
"""
Filter models by maximum pricing threshold
Filters models where both prompt and completion costs are at or
below the specified maximum. Default of 0.0 returns only free models.
Args:
models: List of model dictionaries from API
max_cost: Maximum cost threshold (0.0 for free only)
Returns:
Filtered list of models matching pricing criteria
Example:
>>> all_models = get_available_models()
>>> free = filter_by_pricing(all_models, 0.0)
>>> cheap = filter_by_pricing(all_models, 0.0001)
"""
filtered = []
try:
for model in models:
# Extract pricing data
pricing = model.get("pricing", {})
# Convert pricing to float (handles string values)
try:
prompt_cost = float(pricing.get("prompt", "0"))
completion_cost = float(pricing.get("completion", "0"))
except (ValueError, TypeError):
# Skip models with invalid pricing data
continue
# Check if both costs are at or below threshold
if prompt_cost <= max_cost and completion_cost <= max_cost:
filtered.append(model)
# logger.info(f"[{MODULE_NAME}] Filtered {len(filtered)}/{len(models)} models at max_cost={max_cost}")
return filtered
except Exception as e:
# logger.info(f"[{MODULE_NAME}] Error filtering models: {e}")
return []
def get_model_by_id(model_id: str, api_key: Optional[str] = None) -> Optional[Dict]:
"""
Fetch specific model details by ID
Args:
model_id: Model identifier (e.g., "meta-llama/llama-3.3-70b-instruct:free")
api_key: Optional OpenRouter API key
Returns:
Model dictionary if found, None otherwise
"""
try:
all_models = get_available_models(api_key)
for model in all_models:
if model.get("id") == model_id:
# logger.info(f"[{MODULE_NAME}] Found model: {model_id}")
return model
# logger.info(f"[{MODULE_NAME}] Model not found: {model_id}")
return None
except Exception as e:
# logger.info(f"[{MODULE_NAME}] Error finding model {model_id}: {e}")
return None
def extract_model_metadata(model: Dict) -> Dict:
"""
Extract key metadata from model dictionary
Parses model data and extracts commonly used fields into
a simplified structure for easier consumption.
Args:
model: Raw model dictionary from API
Returns:
Dictionary with extracted metadata fields
Example:
>>> model = get_model_by_id("meta-llama/llama-3.3-70b-instruct:free")
>>> meta = extract_model_metadata(model)
>>> logger.info(f"Context: {meta['context_length']}")
"""
try:
pricing = model.get("pricing", {})
metadata = {
"id": model.get("id", ""),
"name": model.get("name", ""),
"context_length": model.get("context_length", 0),
"prompt_cost": float(pricing.get("prompt", "0")),
"completion_cost": float(pricing.get("completion", "0")),
"is_free": (
float(pricing.get("prompt", "0")) == 0.0 and
float(pricing.get("completion", "0")) == 0.0
),
"architecture": model.get("architecture", {}),
"top_provider": model.get("top_provider", {}),
"description": model.get("description", "")
}
return metadata
except Exception as e:
# logger.info(f"[{MODULE_NAME}] Error extracting metadata: {e}")
return {}
def list_model_ids(models: List[Dict]) -> List[str]:
"""
Extract just the model IDs from a list of model dictionaries
Args:
models: List of model dictionaries
Returns:
List of model ID strings
"""
try:
return [model.get("id", "") for model in models if model.get("id")]
except Exception as e:
# logger.info(f"[{MODULE_NAME}] Error extracting IDs: {e}")
return []
# =============================================
# CONVENIENCE FUNCTIONS
# =============================================
def display_models(models: List[Dict], show_pricing: bool = True) -> None:
"""
Display models in formatted output using logger
Args:
models: List of model dictionaries to display
show_pricing: Whether to show pricing information
"""
if not models:
logger.info("No models to display")
return
logger.info(f"Found {len(models)} models:")
for i, model in enumerate(models, 1):
model_id = model.get("id", "unknown")
name = model.get("name", "")
context = model.get("context_length", 0)
logger.info(f"{i}. {model_id}")
if name:
logger.info(f" Name: {name}")
logger.info(f" Context: {context:,} tokens")
if show_pricing:
pricing = model.get("pricing", {})
prompt_cost = pricing.get("prompt", "0")
completion_cost = pricing.get("completion", "0")
if prompt_cost == "0" and completion_cost == "0":
logger.info(" FREE")
else:
logger.info(f" Prompt: ${prompt_cost} / Completion: ${completion_cost}")
def display_free_models_summary(api_key: Optional[str] = None) -> None:
"""
Fetch and display summary of free models
Convenience function that fetches free models and displays
them in a formatted, user-friendly way.
Args:
api_key: Optional OpenRouter API key
"""
logger.info("Searching for FREE models on OpenRouter...")
logger.info("=" * 60)
free_models = get_free_models(api_key)
if not free_models:
logger.info("No free models found")
logger.info("OpenRouter may have changed pricing.")
logger.info("Alternative: Use very cheap models like openai/gpt-4o-mini")
return
display_models(free_models, show_pricing=False)
logger.info("RECOMMENDED FREE MODELS TO TRY:")
logger.info("-" * 60)
for model in free_models[:5]: # Top 5
logger.info(f" - {model.get('id', '')}")