import requests from typing import List, Dict, Any, Optional, Union from .exceptions import UnauthorizedError, NotFoundError class ModelsManagementClient: def __init__(self, base_url: str, api_key: Optional[str] = None): """ Initialize the ModelsManagementClient. Args: base_url (str): The base URL of the LiteLLM proxy server (e.g., "http://localhost:8000") api_key (Optional[str]): API key for authentication. If provided, it will be sent as a Bearer token. """ self._base_url = base_url.rstrip("/") # Remove trailing slash if present self._api_key = api_key def _get_headers(self) -> Dict[str, str]: """ Get the headers for API requests, including authorization if api_key is set. Returns: Dict[str, str]: Headers to use for API requests """ headers = {} if self._api_key: headers["Authorization"] = f"Bearer {self._api_key}" return headers def list(self, return_request: bool = False) -> Union[List[Dict[str, Any]], requests.Request]: """ Get the list of models supported by the server. Args: return_request (bool): If True, returns the prepared request object instead of executing it. Useful for inspection or modification before sending. Returns: Union[List[Dict[str, Any]], requests.Request]: Either a list of model information dictionaries or a prepared request object if return_request is True. Raises: UnauthorizedError: If the request fails with a 401 status code requests.exceptions.RequestException: If the request fails with any other error """ url = f"{self._base_url}/models" request = requests.Request("GET", url, headers=self._get_headers()) if return_request: return request # Prepare and send the request session = requests.Session() try: response = session.send(request.prepare()) response.raise_for_status() return response.json()["data"] except requests.exceptions.HTTPError as e: if e.response.status_code == 401: raise UnauthorizedError(e) raise def new( self, model_name: str, model_params: Dict[str, Any], model_info: Optional[Dict[str, Any]] = None, return_request: bool = False, ) -> Union[Dict[str, Any], requests.Request]: """ Add a new model to the proxy. Args: model_name (str): Name of the model to add model_params (Dict[str, Any]): Parameters for the model (e.g., model type, api_base, api_key) model_info (Optional[Dict[str, Any]]): Additional information about the model return_request (bool): If True, returns the prepared request object instead of executing it Returns: Union[Dict[str, Any], requests.Request]: Either the response from the server or a prepared request object if return_request is True Raises: UnauthorizedError: If the request fails with a 401 status code requests.exceptions.RequestException: If the request fails with any other error """ url = f"{self._base_url}/model/new" data = { "model_name": model_name, "litellm_params": model_params, } if model_info: data["model_info"] = model_info request = requests.Request("POST", url, headers=self._get_headers(), json=data) if return_request: return request # Prepare and send the request session = requests.Session() try: response = session.send(request.prepare()) response.raise_for_status() return response.json() except requests.exceptions.HTTPError as e: if e.response.status_code == 401: raise UnauthorizedError(e) raise def delete(self, model_id: str, return_request: bool = False) -> Union[Dict[str, Any], requests.Request]: """ Delete a model from the proxy. Args: model_id (str): ID of the model to delete (e.g., "2f23364f-4579-4d79-a43a-2d48dd551c2e") return_request (bool): If True, returns the prepared request object instead of executing it Returns: Union[Dict[str, Any], requests.Request]: Either the response from the server or a prepared request object if return_request is True Raises: UnauthorizedError: If the request fails with a 401 status code NotFoundError: If the request fails with a 404 status code or indicates the model was not found requests.exceptions.RequestException: If the request fails with any other error """ url = f"{self._base_url}/model/delete" data = {"id": model_id} request = requests.Request("POST", url, headers=self._get_headers(), json=data) if return_request: return request # Prepare and send the request session = requests.Session() try: response = session.send(request.prepare()) response.raise_for_status() return response.json() except requests.exceptions.HTTPError as e: if e.response.status_code == 401: raise UnauthorizedError(e) if e.response.status_code == 404 or "not found" in e.response.text.lower(): raise NotFoundError(e) raise def get( self, model_id: Optional[str] = None, model_name: Optional[str] = None, return_request: bool = False ) -> Union[Dict[str, Any], requests.Request]: """ Get information about a specific model by its ID or name. Args: model_id (Optional[str]): ID of the model to retrieve model_name (Optional[str]): Name of the model to retrieve return_request (bool): If True, returns the prepared request object instead of executing it Returns: Union[Dict[str, Any], requests.Request]: Either the model information from the server or a prepared request object if return_request is True Raises: ValueError: If neither model_id nor model_name is provided, or if both are provided UnauthorizedError: If the request fails with a 401 status code NotFoundError: If the model is not found requests.exceptions.RequestException: If the request fails with any other error """ if (model_id is None and model_name is None) or (model_id is not None and model_name is not None): raise ValueError("Exactly one of model_id or model_name must be provided") # If return_request is True, delegate to info if return_request: result = self.info(return_request=True) assert isinstance(result, requests.Request) return result # Get all models and filter models = self.info() assert isinstance(models, List) # Find the matching model for model in models: if (model_id and model.get("model_info", {}).get("id") == model_id) or ( model_name and model.get("model_name") == model_name ): return model # If we get here, no model was found if model_id: msg = f"Model with id={model_id} not found" elif model_name: msg = f"Model with model_name={model_name} not found" else: msg = "Unknown error trying to find model" raise NotFoundError( requests.exceptions.HTTPError( msg, response=requests.Response(), # Empty response since we didn't make a direct request ) ) def info(self, return_request: bool = False) -> Union[List[Dict[str, Any]], requests.Request]: """ Get detailed information about all models from the server. Args: return_request (bool): If True, returns the prepared request object instead of executing it Returns: Union[List[Dict[str, Any]], requests.Request]: Either a list of model information dictionaries or a prepared request object if return_request is True Raises: UnauthorizedError: If the request fails with a 401 status code requests.exceptions.RequestException: If the request fails with any other error """ url = f"{self._base_url}/v1/model/info" request = requests.Request("GET", url, headers=self._get_headers()) if return_request: return request # Prepare and send the request session = requests.Session() try: response = session.send(request.prepare()) response.raise_for_status() return response.json()["data"] except requests.exceptions.HTTPError as e: if e.response.status_code == 401: raise UnauthorizedError(e) raise def update( self, model_id: str, model_params: Dict[str, Any], model_info: Optional[Dict[str, Any]] = None, return_request: bool = False, ) -> Union[Dict[str, Any], requests.Request]: """ Update an existing model's configuration. Args: model_id (str): ID of the model to update model_params (Dict[str, Any]): New parameters for the model (e.g., model type, api_base, api_key) model_info (Optional[Dict[str, Any]]): Additional information about the model return_request (bool): If True, returns the prepared request object instead of executing it Returns: Union[Dict[str, Any], requests.Request]: Either the response from the server or a prepared request object if return_request is True Raises: UnauthorizedError: If the request fails with a 401 status code NotFoundError: If the model is not found requests.exceptions.RequestException: If the request fails with any other error """ url = f"{self._base_url}/model/update" data = { "id": model_id, "litellm_params": model_params, } if model_info: data["model_info"] = model_info request = requests.Request("POST", url, headers=self._get_headers(), json=data) if return_request: return request # Prepare and send the request session = requests.Session() try: response = session.send(request.prepare()) response.raise_for_status() return response.json() except requests.exceptions.HTTPError as e: if e.response.status_code == 401: raise UnauthorizedError(e) if e.response.status_code == 404 or "not found" in e.response.text.lower(): raise NotFoundError(e) raise