mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
feat(endpoints.py): enable retrieving existing credentials by model name
Enables reusing existing credentials
This commit is contained in:
parent
6629354329
commit
605a4d1121
3 changed files with 142 additions and 47 deletions
|
|
@ -719,25 +719,9 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
|
||||
Masks the headers of the request sent from LiteLLM
|
||||
"""
|
||||
sensitive_keywords = [
|
||||
"authorization",
|
||||
"token",
|
||||
"key",
|
||||
"secret",
|
||||
]
|
||||
return {
|
||||
k: (
|
||||
(v[:-44] + "*" * 44)
|
||||
if (isinstance(v, str) and len(v) > 44)
|
||||
else "*****"
|
||||
)
|
||||
for k, v in headers.items()
|
||||
if not ignore_sensitive_headers
|
||||
or not any(
|
||||
sensitive_keyword in k.lower()
|
||||
for sensitive_keyword in sensitive_keywords
|
||||
)
|
||||
}
|
||||
return _get_masked_values(
|
||||
headers, ignore_sensitive_values=ignore_sensitive_headers
|
||||
)
|
||||
|
||||
def post_call(
|
||||
self, original_response, input=None, api_key=None, additional_args={}
|
||||
|
|
@ -2413,6 +2397,58 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
return result
|
||||
|
||||
|
||||
def _get_masked_values(
|
||||
sensitive_object: dict,
|
||||
ignore_sensitive_values: bool = False,
|
||||
mask_all_values: bool = False,
|
||||
unmasked_length: int = 44,
|
||||
number_of_asterisks: Optional[int] = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Internal debugging helper function
|
||||
|
||||
Masks the headers of the request sent from LiteLLM
|
||||
|
||||
Args:
|
||||
masked_length: Optional length for the masked portion (number of *). If set, will use exactly this many *
|
||||
regardless of original string length. The total length will be unmasked_length + masked_length.
|
||||
"""
|
||||
sensitive_keywords = [
|
||||
"authorization",
|
||||
"token",
|
||||
"key",
|
||||
"secret",
|
||||
]
|
||||
return {
|
||||
k: (
|
||||
(
|
||||
v[: unmasked_length // 2]
|
||||
+ "*" * number_of_asterisks
|
||||
+ v[-unmasked_length // 2 :]
|
||||
)
|
||||
if (
|
||||
isinstance(v, str)
|
||||
and len(v) > unmasked_length
|
||||
and number_of_asterisks is not None
|
||||
)
|
||||
else (
|
||||
(
|
||||
v[: unmasked_length // 2]
|
||||
+ "*" * (len(v) - unmasked_length)
|
||||
+ v[-unmasked_length // 2 :]
|
||||
)
|
||||
if (isinstance(v, str) and len(v) > unmasked_length)
|
||||
else "*****"
|
||||
)
|
||||
)
|
||||
for k, v in sensitive_object.items()
|
||||
if not ignore_sensitive_values
|
||||
or not any(
|
||||
sensitive_keyword in k.lower() for sensitive_keyword in sensitive_keywords
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def set_callbacks(callback_list, function_id=None): # noqa: PLR0915
|
||||
"""
|
||||
Globally sets the callback client
|
||||
|
|
|
|||
|
|
@ -2,15 +2,19 @@
|
|||
CRUD endpoints for storing reusable credentials.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response
|
||||
|
||||
import litellm
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.litellm_core_utils.credential_accessor import CredentialAccessor
|
||||
from litellm.litellm_core_utils.litellm_logging import _get_masked_values
|
||||
from litellm.proxy._types import CommonProxyErrors, UserAPIKeyAuth
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
|
||||
from litellm.proxy.common_utils.encrypt_decrypt_utils import encrypt_value_helper
|
||||
from litellm.proxy.utils import handle_exception_on_proxy, jsonify_object
|
||||
from litellm.types.router import CredentialLiteLLMParams
|
||||
from litellm.types.utils import CredentialItem
|
||||
|
||||
router = APIRouter()
|
||||
|
|
@ -101,30 +105,76 @@ async def get_credentials(
|
|||
|
||||
|
||||
@router.get(
|
||||
"/credentials/{credential_name}",
|
||||
"/credentials/by_name/{credential_name}",
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["credential management"],
|
||||
response_model=CredentialItem,
|
||||
)
|
||||
@router.get(
|
||||
"/credentials/by_model/{model_id}",
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["credential management"],
|
||||
response_model=CredentialItem,
|
||||
)
|
||||
async def get_credential(
|
||||
request: Request,
|
||||
fastapi_response: Response,
|
||||
credential_name: str,
|
||||
credential_name: Optional[str] = None,
|
||||
model_id: Optional[str] = None,
|
||||
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
|
||||
):
|
||||
"""
|
||||
[BETA] endpoint. This might change unexpectedly.
|
||||
"""
|
||||
from litellm.proxy.proxy_server import llm_router
|
||||
|
||||
try:
|
||||
for credential in litellm.credential_list:
|
||||
if credential.credential_name == credential_name:
|
||||
masked_credential = {
|
||||
"credential_name": credential.credential_name,
|
||||
"credential_values": credential.credential_values,
|
||||
}
|
||||
return {"success": True, "credential": masked_credential}
|
||||
return {"success": False, "message": "Credential not found"}
|
||||
if model_id:
|
||||
if llm_router is None:
|
||||
raise HTTPException(status_code=500, detail="LLM router not found")
|
||||
# get model from router
|
||||
model = llm_router.get_deployment(model_id)
|
||||
if model is None:
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Model not found. Got model ID: " + model_id
|
||||
)
|
||||
# get credential object from model
|
||||
credential_values = _get_masked_values(
|
||||
CredentialLiteLLMParams(**model.litellm_params.model_dump()).model_dump(
|
||||
exclude_none=True
|
||||
),
|
||||
unmasked_length=4,
|
||||
number_of_asterisks=4,
|
||||
)
|
||||
credential = CredentialItem(
|
||||
credential_name="{}-credential-{}".format(model.model_name, model_id),
|
||||
credential_values=credential_values,
|
||||
credential_info={},
|
||||
)
|
||||
# return credential object
|
||||
return credential
|
||||
elif credential_name:
|
||||
for credential in litellm.credential_list:
|
||||
if credential.credential_name == credential_name:
|
||||
masked_credential = CredentialItem(
|
||||
credential_name=credential.credential_name,
|
||||
credential_values=_get_masked_values(
|
||||
credential.credential_values
|
||||
),
|
||||
credential_info=credential.credential_info,
|
||||
)
|
||||
return masked_credential
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Credential not found. Got credential name: " + credential_name,
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Credential name or model ID required"
|
||||
)
|
||||
except Exception as e:
|
||||
return handle_exception_on_proxy(e)
|
||||
verbose_proxy_logger.exception(e)
|
||||
raise handle_exception_on_proxy(e)
|
||||
|
||||
|
||||
@router.delete(
|
||||
|
|
|
|||
|
|
@ -144,7 +144,26 @@ class ModelInfo(BaseModel):
|
|||
setattr(self, key, value)
|
||||
|
||||
|
||||
class GenericLiteLLMParams(BaseModel):
|
||||
class CredentialLiteLLMParams(BaseModel):
|
||||
api_key: Optional[str] = None
|
||||
api_base: Optional[str] = None
|
||||
api_version: Optional[str] = None
|
||||
## VERTEX AI ##
|
||||
vertex_project: Optional[str] = None
|
||||
vertex_location: Optional[str] = None
|
||||
vertex_credentials: Optional[Union[str, dict]] = None
|
||||
## UNIFIED PROJECT/REGION ##
|
||||
region_name: Optional[str] = None
|
||||
|
||||
## AWS BEDROCK / SAGEMAKER ##
|
||||
aws_access_key_id: Optional[str] = None
|
||||
aws_secret_access_key: Optional[str] = None
|
||||
aws_region_name: Optional[str] = None
|
||||
## IBM WATSONX ##
|
||||
watsonx_region_name: Optional[str] = None
|
||||
|
||||
|
||||
class GenericLiteLLMParams(CredentialLiteLLMParams):
|
||||
"""
|
||||
LiteLLM Params without 'model' arg (used across completion / assistants api)
|
||||
"""
|
||||
|
|
@ -152,9 +171,6 @@ class GenericLiteLLMParams(BaseModel):
|
|||
custom_llm_provider: Optional[str] = None
|
||||
tpm: Optional[int] = None
|
||||
rpm: Optional[int] = None
|
||||
api_key: Optional[str] = None
|
||||
api_base: Optional[str] = None
|
||||
api_version: Optional[str] = None
|
||||
timeout: Optional[Union[float, str, httpx.Timeout]] = (
|
||||
None # if str, pass in as os.environ/
|
||||
)
|
||||
|
|
@ -167,18 +183,7 @@ class GenericLiteLLMParams(BaseModel):
|
|||
|
||||
## LOGGING PARAMS ##
|
||||
litellm_trace_id: Optional[str] = None
|
||||
## UNIFIED PROJECT/REGION ##
|
||||
region_name: Optional[str] = None
|
||||
## VERTEX AI ##
|
||||
vertex_project: Optional[str] = None
|
||||
vertex_location: Optional[str] = None
|
||||
vertex_credentials: Optional[Union[str, dict]] = None
|
||||
## AWS BEDROCK / SAGEMAKER ##
|
||||
aws_access_key_id: Optional[str] = None
|
||||
aws_secret_access_key: Optional[str] = None
|
||||
aws_region_name: Optional[str] = None
|
||||
## IBM WATSONX ##
|
||||
watsonx_region_name: Optional[str] = None
|
||||
|
||||
## CUSTOM PRICING ##
|
||||
input_cost_per_token: Optional[float] = None
|
||||
output_cost_per_token: Optional[float] = None
|
||||
|
|
@ -245,7 +250,11 @@ class GenericLiteLLMParams(BaseModel):
|
|||
args.pop("__class__", None)
|
||||
if max_retries is not None and isinstance(max_retries, str):
|
||||
max_retries = int(max_retries) # cast to int
|
||||
super().__init__(max_retries=max_retries, **args, **params)
|
||||
# We need to keep max_retries in args since it's a parameter of GenericLiteLLMParams
|
||||
args["max_retries"] = (
|
||||
max_retries # Put max_retries back in args after popping it
|
||||
)
|
||||
super().__init__(**args, **params)
|
||||
|
||||
def __contains__(self, key):
|
||||
# Define custom behavior for the 'in' operator
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue