llm-api-proxy / src /rotator_library /model_definitions.py
Mirrowel
feat(providers): ✨ implement credential tier initialization and persistence system
4dfb828
import json
import os
import logging
from typing import Dict, Any, Optional
lib_logger = logging.getLogger("rotator_library")
lib_logger.propagate = False
if not lib_logger.handlers:
lib_logger.addHandler(logging.NullHandler())
class ModelDefinitions:
"""
Simple model definitions loader from environment variables.
Supports two formats:
1. Array format (simple): PROVIDER_MODELS=["model-1", "model-2", "model-3"]
- Each model name is used as both name and ID
2. Dict format (advanced): PROVIDER_MODELS={"model-name": {"id": "model-id", "options": {...}}}
- The 'id' field is optional - if not provided, the model name (key) is used as the ID
Examples:
- IFLOW_MODELS='["glm-4.6", "qwen3-max"]' - simple array format
- IFLOW_MODELS='{"glm-4.6": {}}' - dict format, uses "glm-4.6" as both name and ID
- IFLOW_MODELS='{"custom-name": {"id": "actual-id"}}' - dict format with custom ID
- IFLOW_MODELS='{"model": {"id": "id", "options": {"temperature": 0.7}}}' - with options
This class is a singleton - instantiated once and shared across all providers.
"""
_instance: Optional["ModelDefinitions"] = None
_initialized: bool = False
def __new__(cls, config_path: Optional[str] = None):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, config_path: Optional[str] = None):
"""Initialize model definitions loader (only runs once due to singleton)."""
if ModelDefinitions._initialized:
return
ModelDefinitions._initialized = True
self.config_path = config_path
self.definitions = {}
self._load_definitions()
def _load_definitions(self):
"""Load model definitions from environment variables."""
for env_var, env_value in os.environ.items():
if env_var.endswith("_MODELS"):
provider_name = env_var[:-7].lower() # Remove "_MODELS" (7 characters)
try:
models_json = json.loads(env_value)
# Handle dict format: {"model-name": {"id": "...", "options": {...}}}
if isinstance(models_json, dict):
self.definitions[provider_name] = models_json
lib_logger.info(
f"Loaded {len(models_json)} models for provider: {provider_name}"
)
# Handle array format: ["model-1", "model-2", "model-3"]
elif isinstance(models_json, list):
# Convert array to dict format with empty definitions
models_dict = {
model_name: {}
for model_name in models_json
if isinstance(model_name, str)
}
self.definitions[provider_name] = models_dict
lib_logger.info(
f"Loaded {len(models_dict)} models for provider: {provider_name} (array format)"
)
else:
lib_logger.warning(
f"{env_var} must be a JSON object or array, got {type(models_json).__name__}"
)
except (json.JSONDecodeError, TypeError) as e:
lib_logger.warning(f"Invalid JSON in {env_var}: {e}")
def get_provider_models(self, provider_name: str) -> Dict[str, Any]:
"""Get all models for a provider."""
return self.definitions.get(provider_name, {})
def get_model_definition(
self, provider_name: str, model_name: str
) -> Optional[Dict[str, Any]]:
"""Get a specific model definition."""
provider_models = self.get_provider_models(provider_name)
return provider_models.get(model_name)
def get_model_options(self, provider_name: str, model_name: str) -> Dict[str, Any]:
"""Get options for a specific model."""
model_def = self.get_model_definition(provider_name, model_name)
return model_def.get("options", {}) if model_def else {}
def get_model_id(self, provider_name: str, model_name: str) -> Optional[str]:
"""Get model ID for a specific model. Falls back to model_name if 'id' is not specified."""
model_def = self.get_model_definition(provider_name, model_name)
if not model_def:
return None
# Use 'id' if provided, otherwise use the model_name as the ID
return model_def.get("id", model_name)
def get_all_provider_models(self, provider_name: str) -> list:
"""Get all model names with provider prefix."""
provider_models = self.get_provider_models(provider_name)
return [f"{provider_name}/{model}" for model in provider_models.keys()]
def reload_definitions(self):
"""Reload model definitions from environment variables."""
self.definitions.clear()
self._load_definitions()