Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/client/models.py: 0%
114 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:01 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:01 +0000
1import builtins
2from collections.abc import Mapping
3from typing import Any, Final
5import requests
7from .exceptions import NotFoundError, UnauthorizedError
10class ModelsManagementClient:
11 def __init__(self, base_url: str, api_key: str | None = None, timeout: int = 30):
12 """
13 Initialize the ModelsManagementClient.
15 Args:
16 base_url (str): The base URL of the LiteLLM proxy server (e.g., "http://localhost:8000")
17 api_key (Optional[str]): API key for authentication. If provided, it will be sent as a Bearer token.
18 timeout (int): Request timeout in seconds (default: 30)
19 """
20 self._base_url = base_url.rstrip("/") # Remove trailing slash if present
21 self._api_key = api_key
22 self._timeout = timeout
24 def _get_headers(self) -> dict[str, str]:
25 """
26 Get the headers for API requests, including authorization if api_key is set.
28 Returns:
29 Dict[str, str]: Headers to use for API requests
30 """
31 headers: Final = {}
32 if self._api_key:
33 headers["Authorization"] = f"Bearer {self._api_key}"
34 return headers
36 def list(self, return_request: bool = False) -> builtins.list[dict[str, Any]] | requests.Request:
37 """
38 Get the list of models supported by the server.
40 Args:
41 return_request (bool): If True, returns the prepared request object instead of executing it.
42 Useful for inspection or modification before sending.
44 Returns:
45 Union[List[Dict[str, Any]], requests.Request]: Either a list of model information dictionaries
46 or a prepared request object if return_request is True.
48 Raises:
49 UnauthorizedError: If the request fails with a 401 status code
50 requests.exceptions.RequestException: If the request fails with any other error
51 """
52 url: Final = f"{self._base_url}/models"
53 request: Final = requests.Request("GET", url, headers=self._get_headers())
55 if return_request:
56 return request
58 # Prepare and send the request
59 session: Final = requests.Session()
60 try:
61 response: Final = session.send(request.prepare(), timeout=self._timeout)
62 response.raise_for_status()
63 return response.json()["data"]
64 except requests.exceptions.HTTPError as e:
65 if e.response.status_code == 401:
66 raise UnauthorizedError(e)
67 raise
69 def new(
70 self,
71 model_name: str,
72 model_params: Mapping[str, object],
73 model_info: Mapping[str, object] | None = None,
74 return_request: bool = False,
75 ) -> dict[str, Any] | requests.Request:
76 """
77 Add a new model to the proxy.
79 Args:
80 model_name (str): Name of the model to add
81 model_params (Dict[str, Any]): Parameters for the model (e.g., model type, api_base, api_key)
82 model_info (Optional[Dict[str, Any]]): Additional information about the model
83 return_request (bool): If True, returns the prepared request object instead of executing it
85 Returns:
86 Union[Dict[str, Any], requests.Request]: Either the response from the server or
87 a prepared request object if return_request is True
89 Raises:
90 UnauthorizedError: If the request fails with a 401 status code
91 requests.exceptions.RequestException: If the request fails with any other error
92 """
93 url: Final = f"{self._base_url}/model/new"
95 data: Final = {
96 "model_name": model_name,
97 "litellm_params": model_params,
98 }
99 if model_info:
100 data["model_info"] = model_info
102 request: Final = requests.Request("POST", url, headers=self._get_headers(), json=data)
104 if return_request:
105 return request
107 # Prepare and send the request
108 session: Final = requests.Session()
109 try:
110 response: Final = session.send(request.prepare(), timeout=self._timeout)
111 response.raise_for_status()
112 return response.json()
113 except requests.exceptions.HTTPError as e:
114 if e.response.status_code == 401:
115 raise UnauthorizedError(e)
116 raise
118 def delete(self, model_id: str, return_request: bool = False) -> dict[str, Any] | requests.Request:
119 """
120 Delete a model from the proxy.
122 Args:
123 model_id (str): ID of the model to delete (e.g., "2f23364f-4579-4d79-a43a-2d48dd551c2e")
124 return_request (bool): If True, returns the prepared request object instead of executing it
126 Returns:
127 Union[Dict[str, Any], requests.Request]: Either the response from the server or
128 a prepared request object if return_request is True
130 Raises:
131 UnauthorizedError: If the request fails with a 401 status code
132 NotFoundError: If the request fails with a 404 status code or indicates the model was not found
133 requests.exceptions.RequestException: If the request fails with any other error
134 """
135 url: Final = f"{self._base_url}/model/delete"
136 data: Final = {"id": model_id}
138 request: Final = requests.Request("POST", url, headers=self._get_headers(), json=data)
140 if return_request:
141 return request
143 # Prepare and send the request
144 session: Final = requests.Session()
145 try:
146 response: Final = session.send(request.prepare(), timeout=self._timeout)
147 response.raise_for_status()
148 return response.json()
149 except requests.exceptions.HTTPError as e:
150 if e.response.status_code == 401:
151 raise UnauthorizedError(e)
152 if e.response.status_code == 404 or "not found" in e.response.text.lower():
153 raise NotFoundError(e)
154 raise
156 def get(
157 self,
158 model_id: str | None = None,
159 model_name: str | None = None,
160 return_request: bool = False,
161 ) -> dict[str, Any] | requests.Request:
162 """
163 Get information about a specific model by its ID or name.
165 Args:
166 model_id (Optional[str]): ID of the model to retrieve
167 model_name (Optional[str]): Name of the model to retrieve
168 return_request (bool): If True, returns the prepared request object instead of executing it
170 Returns:
171 Union[Dict[str, Any], requests.Request]: Either the model information from the server or
172 a prepared request object if return_request is True
174 Raises:
175 ValueError: If neither model_id nor model_name is provided, or if both are provided
176 UnauthorizedError: If the request fails with a 401 status code
177 NotFoundError: If the model is not found
178 requests.exceptions.RequestException: If the request fails with any other error
179 """
180 if (model_id is None and model_name is None) or (model_id is not None and model_name is not None):
181 raise ValueError("Exactly one of model_id or model_name must be provided")
183 # If return_request is True, delegate to info
184 if return_request:
185 result: Final = self.info(return_request=True)
186 assert isinstance(result, requests.Request)
187 return result
189 # Get all models and filter
190 models: Final = self.info()
191 assert isinstance(models, list)
193 # Find the matching model
194 for model in models:
195 if (model_id and model.get("model_info", {}).get("id") == model_id) or (
196 model_name and model.get("model_name") == model_name
197 ):
198 return model
200 # If we get here, no model was found
201 if model_id:
202 msg = f"Model with id={model_id} not found"
203 elif model_name:
204 msg = f"Model with model_name={model_name} not found"
205 else:
206 msg = "Unknown error trying to find model"
207 raise NotFoundError(
208 requests.exceptions.HTTPError(
209 msg,
210 response=requests.Response(), # Empty response since we didn't make a direct request
211 )
212 )
214 def info(self, return_request: bool = False) -> builtins.list[dict[str, Any]] | requests.Request:
215 """
216 Get detailed information about all models from the server.
218 Args:
219 return_request (bool): If True, returns the prepared request object instead of executing it
221 Returns:
222 Union[List[Dict[str, Any]], requests.Request]: Either a list of model information dictionaries
223 or a prepared request object if return_request is True
225 Raises:
226 UnauthorizedError: If the request fails with a 401 status code
227 requests.exceptions.RequestException: If the request fails with any other error
228 """
229 url: Final = f"{self._base_url}/v1/model/info"
230 request: Final = requests.Request("GET", url, headers=self._get_headers())
232 if return_request:
233 return request
235 # Prepare and send the request
236 session: Final = requests.Session()
237 try:
238 response: Final = session.send(request.prepare(), timeout=self._timeout)
239 response.raise_for_status()
240 return response.json()["data"]
241 except requests.exceptions.HTTPError as e:
242 if e.response.status_code == 401:
243 raise UnauthorizedError(e)
244 raise
246 def update(
247 self,
248 model_id: str,
249 model_params: Mapping[str, object],
250 model_info: Mapping[str, object] | None = None,
251 return_request: bool = False,
252 ) -> dict[str, Any] | requests.Request:
253 """
254 Update an existing model's configuration.
256 Args:
257 model_id (str): ID of the model to update
258 model_params (Dict[str, Any]): New parameters for the model (e.g., model type, api_base, api_key)
259 model_info (Optional[Dict[str, Any]]): Additional information about the model
260 return_request (bool): If True, returns the prepared request object instead of executing it
262 Returns:
263 Union[Dict[str, Any], requests.Request]: Either the response from the server or
264 a prepared request object if return_request is True
266 Raises:
267 UnauthorizedError: If the request fails with a 401 status code
268 NotFoundError: If the model is not found
269 requests.exceptions.RequestException: If the request fails with any other error
270 """
271 url: Final = f"{self._base_url}/model/update"
273 data: Final = {
274 "id": model_id,
275 "litellm_params": model_params,
276 }
277 if model_info:
278 data["model_info"] = model_info
280 request: Final = requests.Request("POST", url, headers=self._get_headers(), json=data)
282 if return_request:
283 return request
285 # Prepare and send the request
286 session: Final = requests.Session()
287 try:
288 response: Final = session.send(request.prepare(), timeout=self._timeout)
289 response.raise_for_status()
290 return response.json()
291 except requests.exceptions.HTTPError as e:
292 if e.response.status_code == 401:
293 raise UnauthorizedError(e)
294 if e.response.status_code == 404 or "not found" in e.response.text.lower():
295 raise NotFoundError(e)
296 raise