LiteLLM / proxy /config.py
Sanvi Jain
Integrate DeBERTa-v3 PII guardrail, rename guardrail modules, fix ModelResponse item assignment error, and display guardrailed query in Streamlit UI
42075bf
Raw
History Blame Contribute Delete
6.56 kB
import os
import yaml
import logging
from typing import List, Dict, Any, Optional
from pydantic import BaseModel, Field
logger = logging.getLogger("proxy.config")
class ModelEndpointConfig(BaseModel):
"""
Object-oriented definition of a specific physical LLM endpoint.
Includes rate limits (TPM/RPM), context limits (TPR), and API configurations.
"""
model_name: str = Field(..., description="Logical virtual model name (e.g. oss-chat-fast)")
model: str = Field(..., description="Physical model path (e.g. groq/llama3-8b-8192)")
api_key: Optional[str] = Field(None, description="API Key for the provider")
api_base: Optional[str] = Field(None, description="Optional custom base URL for the API (e.g. for Ollama)")
rpm: int = Field(default=1000, description="Requests Per Minute rate limit")
tpm: int = Field(default=100000, description="Tokens Per Minute rate limit")
tpr: int = Field(default=8192, description="Tokens Per Request (Context Window) limit")
cost_per_million: float = Field(default=0.1, description="Average cost in USD per million tokens")
max_tokens: Optional[int] = Field(None, description="Max response tokens allowed")
complexity_tier: Optional[str] = Field(None, description="Complexity tier for this endpoint: low, medium, or high")
class ProxyConfig:
"""
Loads, parses, and validates the LiteLLM Proxy configuration from YAML and Environment variables.
"""
def __init__(self, config_path: str = "config.yaml"):
self.config_path = config_path
self.endpoints: List[ModelEndpointConfig] = []
self.routing_strategy: str = "simple-shuffle"
self.fallback_policy: str = "retry_next_suitable"
self.num_retries: int = 3
self.timeout: int = 10
self.context_window_fallbacks: List[Dict[str, List[str]]] = []
self.general_fallbacks: List[Dict[str, List[str]]] = []
self.load_config()
def load_config(self):
"""Reads config.yaml, resolves env vars, and populates OOP configurations."""
if not os.path.exists(self.config_path):
raise FileNotFoundError(f"Configuration file not found at: {self.config_path}")
try:
with open(self.config_path, "r") as f:
raw_data = yaml.safe_load(f) or {}
# Parse general router settings
router_settings = raw_data.get("router_settings", {})
self.routing_strategy = router_settings.get("routing_strategy", "simple-shuffle")
self.fallback_policy = router_settings.get("fallback_policy", "retry_next_suitable")
self.num_retries = int(router_settings.get("num_retries", 3))
self.timeout = int(router_settings.get("timeout", 10))
# Parse fallbacks
self.context_window_fallbacks = raw_data.get("context_window_fallbacks", [])
self.general_fallbacks = raw_data.get("general_fallbacks", [])
# Parse model list
model_list = raw_data.get("model_list", [])
self.endpoints = []
for m in model_list:
model_name = m.get("model_name")
litellm_params = m.get("litellm_params", {})
# Resolve env vars in model params
raw_model = litellm_params.get("model", "")
raw_key = litellm_params.get("api_key", "")
raw_base = litellm_params.get("api_base", "")
api_key = None
if raw_key:
if raw_key.startswith("os.environ/"):
env_var_name = raw_key.replace("os.environ/", "")
api_key = os.environ.get(env_var_name)
else:
api_key = raw_key
api_base = None
if raw_base:
if raw_base.startswith("os.environ/"):
env_var_name = raw_base.replace("os.environ/", "")
api_base = os.environ.get(env_var_name)
else:
api_base = raw_base
cost = litellm_params.get("cost_per_million", 0.1)
tpr = litellm_params.get("tpr", 8192)
complexity_tier = litellm_params.get("complexity_tier")
# Intelligent tier fallback if not explicitly provided
if not complexity_tier:
if cost >= 0.50:
complexity_tier = "high"
elif cost >= 0.04:
complexity_tier = "medium"
else:
complexity_tier = "low"
endpoint = ModelEndpointConfig(
model_name=model_name,
model=raw_model,
api_key=api_key,
api_base=api_base,
rpm=litellm_params.get("rpm", 1000),
tpm=litellm_params.get("tpm", 100000),
tpr=tpr,
cost_per_million=cost,
max_tokens=litellm_params.get("max_tokens"),
complexity_tier=complexity_tier
)
self.endpoints.append(endpoint)
logger.info(f"Loaded {len(self.endpoints)} endpoints from {self.config_path}")
except Exception as e:
logger.error(f"Error loading configuration: {e}")
raise e
def get_endpoints_for_model(self, virtual_model_name: str) -> List[ModelEndpointConfig]:
"""Returns physical endpoints matching the requested virtual model name."""
return [e for e in self.endpoints if e.model_name == virtual_model_name]
def to_litellm_model_list(self) -> List[Dict[str, Any]]:
"""Converts OOP configuration list to the dictionary format expected by LiteLLM Router."""
litellm_list = []
for e in self.endpoints:
params = {
"model": e.model,
"rpm": e.rpm,
"tpm": e.tpm
}
if e.api_key:
params["api_key"] = e.api_key
if e.api_base:
params["api_base"] = e.api_base
if e.max_tokens:
params["max_tokens"] = e.max_tokens
litellm_list.append({
"model_name": e.model_name,
"litellm_params": params
})
return litellm_list