Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/client/keys.py: 0%
139 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 litellm.litellm_core_utils.secret_redaction import redact_string
9from .exceptions import UnauthorizedError
12class KeysManagementClient:
13 def __init__(self, base_url: str, api_key: str | None = None, timeout: int = 30):
14 """
15 Initialize the KeysManagementClient.
17 Args:
18 base_url (str): The base URL of the LiteLLM proxy server (e.g., "http://localhost:8000")
19 api_key (Optional[str]): API key for authentication. If provided, it will be sent as a Bearer token.
20 timeout (int): Request timeout in seconds (default: 30)
21 """
22 self._base_url = base_url.rstrip("/") # Remove trailing slash if present
23 self._api_key = api_key
24 self._timeout = timeout
26 def _get_headers(self) -> dict[str, str]:
27 """
28 Get the headers for API requests, including authorization if api_key is set.
30 Returns:
31 Dict[str, str]: Headers to use for API requests
32 """
33 headers: Final = {"Content-Type": "application/json"}
34 if self._api_key:
35 headers["Authorization"] = f"Bearer {self._api_key}"
36 return headers
38 def list(
39 self,
40 page: int | None = None,
41 size: int | None = None,
42 user_id: str | None = None,
43 team_id: str | None = None,
44 organization_id: str | None = None,
45 key_hash: str | None = None,
46 key_alias: str | None = None,
47 return_full_object: bool | None = None,
48 include_team_keys: bool | None = None,
49 return_request: bool = False,
50 ) -> dict[str, Any] | requests.Request:
51 """
52 List all API keys with optional filtering and pagination.
54 Args:
55 page (Optional[int]): Page number for pagination
56 size (Optional[int]): Number of items per page
57 user_id (Optional[str]): Filter keys by user ID
58 team_id (Optional[str]): Filter keys by team ID
59 organization_id (Optional[str]): Filter keys by organization ID
60 key_hash (Optional[str]): Filter by specific key hash
61 key_alias (Optional[str]): Filter by key alias
62 return_full_object (Optional[bool]): Whether to return the full key object
63 include_team_keys (Optional[bool]): Whether to include team keys in the response
64 return_request (bool): If True, returns the prepared request object instead of executing it
66 Returns:
67 Union[Dict[str, Any], requests.Request]: Either the response from the server or
68 a prepared request object if return_request is True. The response contains a list
69 of API keys with their configurations.
71 Raises:
72 UnauthorizedError: If the request fails with a 401 status code
73 requests.exceptions.RequestException: If the request fails with any other error
74 """
75 url: Final = f"{self._base_url}/key/list"
76 params: Final[dict[str, int | str]] = {}
78 # Add optional query parameters
79 if page is not None:
80 params["page"] = page
81 if size is not None:
82 params["size"] = size
83 if user_id is not None:
84 params["user_id"] = user_id
85 if team_id is not None:
86 params["team_id"] = team_id
87 if organization_id is not None:
88 params["organization_id"] = organization_id
89 if key_hash is not None:
90 params["key_hash"] = key_hash
91 if key_alias is not None:
92 params["key_alias"] = key_alias
93 if return_full_object is not None:
94 params["return_full_object"] = str(return_full_object).lower()
95 if include_team_keys is not None:
96 params["include_team_keys"] = str(include_team_keys).lower()
98 request: Final = requests.Request("GET", url, headers=self._get_headers(), params=params)
100 if return_request:
101 return request
103 session: Final = requests.Session()
104 try:
105 response: Final = session.send(request.prepare(), timeout=self._timeout)
106 response.raise_for_status()
107 return response.json()
108 except requests.exceptions.HTTPError as e:
109 if e.response.status_code == 401:
110 raise UnauthorizedError(e)
111 raise
113 def generate(
114 self,
115 models: builtins.list[str] | None = None,
116 aliases: dict[str, str] | None = None,
117 spend: float | None = None,
118 duration: str | None = None,
119 key_alias: str | None = None,
120 team_id: str | None = None,
121 user_id: str | None = None,
122 budget_id: str | None = None,
123 config: Mapping[str, object] | None = None,
124 return_request: bool = False,
125 ) -> dict[str, object] | requests.Request:
126 """
127 Generate an API key based on the provided data.
129 Docs: https://docs.litellm.ai/docs/proxy/virtual_keys
131 Args:
132 models (Optional[List[str]]): List of allowed models for this key
133 aliases (Optional[Dict[str, str]]): Model alias mappings
134 spend (Optional[float]): Maximum spend limit for this key
135 duration (Optional[str]): Duration for which the key is valid (e.g. "24h", "7d")
136 key_alias (Optional[str]): Alias/name for the key for easier identification
137 team_id (Optional[str]): Team ID to associate the key with
138 user_id (Optional[str]): User ID to associate the key with
139 budget_id (Optional[str]): Budget ID to associate the key with
140 config (Optional[Dict[str, Any]]): Additional configuration parameters
141 return_request (bool): If True, returns the prepared request object instead of executing it
143 Returns:
144 Union[Dict[str, Any], requests.Request]: Either the response from the server or
145 a prepared request object if return_request is True
147 Raises:
148 UnauthorizedError: If the request fails with a 401 status code
149 requests.exceptions.RequestException: If the request fails with any other error
150 """
151 url: Final = f"{self._base_url}/key/generate"
153 data: Final[dict[str, object]] = {}
154 if models is not None:
155 data["models"] = models
156 if aliases is not None:
157 data["aliases"] = aliases
158 if spend is not None:
159 data["spend"] = spend
160 if duration is not None:
161 data["duration"] = duration
162 if key_alias is not None:
163 data["key_alias"] = key_alias
164 if team_id is not None:
165 data["team_id"] = team_id
166 if user_id is not None:
167 data["user_id"] = user_id
168 if budget_id is not None:
169 data["budget_id"] = budget_id
170 if config is not None:
171 data["config"] = config
173 request: Final = requests.Request("POST", url, headers=self._get_headers(), json=data)
175 if return_request:
176 return request
178 session: Final = requests.Session()
179 try:
180 response: Final = session.send(request.prepare(), timeout=self._timeout)
181 response.raise_for_status()
182 return response.json()
183 except requests.exceptions.HTTPError as e:
184 if e.response.status_code == 401:
185 raise UnauthorizedError(e)
186 raise
188 def delete(
189 self,
190 keys: builtins.list[str] | None = None,
191 key_aliases: builtins.list[str] | None = None,
192 return_request: bool = False,
193 ) -> dict[str, object] | requests.Request:
194 """
195 Delete existing keys
197 Args:
198 keys (List[str]): List of API keys to delete
199 key_aliases (List[str]): List of key aliases to delete
200 return_request (bool): If True, returns the prepared request object instead of executing it
202 Returns:
203 Union[Dict[str, Any], requests.Request]: Either the response from the server or
204 a prepared request object if return_request is True
206 Raises:
207 UnauthorizedError: If the request fails with a 401 status code
208 requests.exceptions.RequestException: If the request fails with any other error
209 """
210 url: Final = f"{self._base_url}/key/delete"
212 data: Final = {
213 "keys": keys,
214 "key_aliases": key_aliases,
215 }
217 request: Final = requests.Request("POST", url, headers=self._get_headers(), json=data)
219 if return_request:
220 return request
222 session: Final = requests.Session()
223 try:
224 response: Final = session.send(request.prepare(), timeout=self._timeout)
225 response.raise_for_status()
226 return response.json()
227 except requests.exceptions.HTTPError as e:
228 if e.response.status_code == 401:
229 raise UnauthorizedError(e)
230 raise
232 def update(
233 self,
234 key: str,
235 models: builtins.list[str] | None = None,
236 aliases: dict[str, str] | None = None,
237 spend: float | None = None,
238 duration: str | None = None,
239 key_alias: str | None = None,
240 team_id: str | None = None,
241 user_id: str | None = None,
242 ) -> dict[str, object] | requests.Request:
243 """
244 Update an existing API key's parameters.
246 Args:
247 models: Optional[List[str]] = None,
248 aliases: Optional[Dict[str, str]] = None,
249 spend: Optional[float] = None,
250 duration: Optional[str] = None,
251 key_alias: Optional[str] = None,
252 team_id: Optional[str] = None,
253 user_id: Optional[str] = None,
255 Returns:
256 Union[Dict[str, Any], requests.Request]: Either the response from the server or
257 a prepared request object if return_request is True
259 Raises:
260 UnauthorizedError: If the request fails with a 401 status code
261 requests.exceptions.RequestException: If the request fails with any other error
262 """
263 url: Final = f"{self._base_url}/key/update"
265 data: Final[dict[str, object]] = {"key": key}
267 if key_alias is not None:
268 data["key_alias"] = key_alias
269 if user_id is not None:
270 data["user_id"] = user_id
271 if team_id is not None:
272 data["team_id"] = team_id
273 if models is not None:
274 data["models"] = models
275 if spend is not None:
276 data["spend"] = spend
277 if duration is not None:
278 data["duration"] = duration
279 if aliases is not None:
280 data["aliases"] = aliases
281 request: Final = requests.Request("POST", url, headers=self._get_headers(), json=data)
282 session: Final = requests.Session()
283 response_text: str | None = None
284 try:
285 response: Final = session.send(request.prepare(), timeout=self._timeout)
286 response_text = response.text
287 response.raise_for_status()
288 return response.json()
289 except Exception:
290 raise Exception(f"Error updating key: {response_text}")
292 def info(self, key: str, return_request: bool = False) -> dict[str, object] | requests.Request:
293 """
294 Get information about API keys.
296 Args:
297 key (str): The key hash to get information about
298 return_request (bool): If True, returns the prepared request object instead of executing it
300 Returns:
301 Union[Dict[str, Any], requests.Request]: Either the response from the server or a prepared request object if return_request is True
303 Raises:
304 UnauthorizedError: If the request fails with a 401 status code
305 requests.exceptions.RequestException: If the request fails with any other error
306 """
307 url: Final = f"{self._base_url}/key/info?key={key}"
308 request: Final = requests.Request("GET", url, headers=self._get_headers())
310 if return_request:
311 return request
313 session: Final = requests.Session()
314 try:
315 response: Final = session.send(request.prepare(), timeout=self._timeout)
316 response.raise_for_status()
317 return response.json()
318 except requests.exceptions.HTTPError as e:
319 redacted_message: Final = redact_string(str(e))
320 if e.response.status_code == 401:
321 raise UnauthorizedError(e) from None
322 raise requests.exceptions.HTTPError(redacted_message, response=e.response) from None