Spaces:
Paused
Paused
| """ | |
| Model Capabilities API Endpoint | |
| SINGLE SOURCE OF TRUTH for model thinking capabilities. | |
| Frontend fetches this to determine UI controls dynamically. | |
| Configuration is loaded from config/model_capabilities.json. | |
| When new models are released, update the JSON file - no code changes needed. | |
| """ | |
| import json | |
| import re | |
| from functools import lru_cache | |
| from pathlib import Path | |
| from typing import Any | |
| from fastapi import APIRouter | |
| from fastapi.responses import JSONResponse | |
| router = APIRouter() | |
| # Config file path | |
| _CONFIG_PATH = ( | |
| Path(__file__).parent.parent.parent / "config" / "model_capabilities.json" | |
| ) | |
| def _load_config() -> dict[str, Any]: | |
| """ | |
| Load model capabilities configuration from JSON file. | |
| Uses LRU cache to avoid repeated file reads. | |
| Raises FileNotFoundError if config is missing. | |
| """ | |
| if not _CONFIG_PATH.exists(): | |
| raise FileNotFoundError(f"Model capabilities config not found: {_CONFIG_PATH}") | |
| with open(_CONFIG_PATH, encoding="utf-8") as f: | |
| return json.load(f) | |
| def reload_config() -> None: | |
| """Clear the config cache, forcing a reload on next access.""" | |
| _load_config.cache_clear() | |
| def _get_model_capabilities(model_id: str) -> dict[str, Any]: | |
| """ | |
| Determine thinking capabilities for a model. | |
| Returns dict with: | |
| - thinkingType: "level" | "budget" | "none" | |
| - levels: List of thinking levels (for type="level") | |
| - alwaysOn: Whether thinking is always on (for Gemini 2.5 Pro) | |
| - budgetRange: [min, max] for budget slider | |
| - supportsGoogleSearch: Whether the model supports Google Search | |
| """ | |
| config = _load_config() | |
| categories = config.get("categories", {}) | |
| matchers = config.get("matchers", []) | |
| model_lower = model_id.lower() | |
| # Try each matcher in order (order matters: more specific first) | |
| for matcher in matchers: | |
| pattern = matcher.get("pattern", "") | |
| category_name = matcher.get("category", "") | |
| if pattern and category_name: | |
| try: | |
| if re.search(pattern, model_lower, re.IGNORECASE): | |
| if category_name in categories: | |
| return categories[category_name].copy() | |
| except re.error: | |
| # Invalid regex pattern, skip | |
| continue | |
| # Default to "other" category | |
| return categories.get( | |
| "other", {"thinkingType": "none", "supportsGoogleSearch": True} | |
| ) | |
| async def get_model_capabilities() -> JSONResponse: | |
| """ | |
| Return thinking capabilities for all known model categories. | |
| Frontend uses this to dynamically configure thinking controls. | |
| """ | |
| config = _load_config() | |
| return JSONResponse(content=config) | |
| async def get_single_model_capabilities(model_id: str) -> JSONResponse: | |
| """ | |
| Return thinking capabilities for a specific model. | |
| Args: | |
| model_id: Model identifier (e.g., "gemini-2.5-flash-preview") | |
| """ | |
| return JSONResponse(content=_get_model_capabilities(model_id)) | |