mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-05 07:37:50 +00:00
refactor: clean up fresh tech debt in proxy auth reads, chatgpt credential forwarding, messages drop_params reads, router context signatures and spend log reads (#46643)
* refactor: clean up fresh tech debt in proxy sub-call budget checks, model search and logger init Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: clean up fresh tech debt in otel baggage metadata, model budget limiter and batch line items Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Revert "refactor: clean up fresh tech debt in otel baggage metadata, model budget limiter and batch line items" This reverts commit68737c457c. * refactor: clean up fresh tech debt in guardrail init, cost map fetch and stream continuation Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: clean up fresh tech debt in kimi k3 token counting and two slop comments Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: clean up fresh tech debt in mcp tool call auth fields, messages drop_params, exception chain and spend log noqa Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: preserve dict-compatible drop params access Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: clean up fresh tech debt in user list sorting, opencode affinity header and aws polly comment Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: clean up fresh tech debt in responses bridge check, chatgpt authenticator and image header adapters Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: clean up fresh tech debt in auth 401 detection, cooldown fallback lookup and bedrock guardrail billing Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Revert "refactor: clean up fresh tech debt in auth 401 detection, cooldown fallback lookup and bedrock guardrail billing" This reverts commitd497e02bbf. * refactor: clean up fresh tech debt in responses stream usage cost, bedrock count tokens comments and router return types Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: clean up fresh tech debt in messages drop_params reads, auth zero cost comment and two return types Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: keep getattr drop_params reads where callers pass a plain dict Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: clean up fresh tech debt in router context signatures, search tool comment and gemini realtime init Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: drop fresh private-alias, getattr and duplicated response id helpers Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: bind response id helper to logging instance Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: simplify media guard and spend log reads Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: satisfy media guard type discipline and formatting Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor: keep media guard cast rather than trade it for new suppressions Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: mateo <277851410+mateo-berri@users.noreply.github.com>
This commit is contained in:
parent
48bd9b01f6
commit
fd4cef22ea
41 changed files with 110 additions and 164 deletions
|
|
@ -198,7 +198,6 @@ def _load_beta_headers_config() -> dict:
|
|||
if _BETA_HEADERS_CONFIG is not None:
|
||||
return _BETA_HEADERS_CONFIG
|
||||
|
||||
# Get the URL from environment or use default
|
||||
from litellm import anthropic_beta_headers_url
|
||||
|
||||
_BETA_HEADERS_CONFIG = get_beta_headers_config(url=anthropic_beta_headers_url)
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ from litellm.utils import (
|
|||
)
|
||||
|
||||
_PROVIDER_ROUTING_HINTS: Final = TypeAdapter(ProviderRoutingHints)
|
||||
_OPTIONAL_OBJECT_DICT_ADAPTER: Final = TypeAdapter[dict[str, object] | None](dict[str, object] | None)
|
||||
_OPTIONAL_STRING_DICT_ADAPTER: Final = TypeAdapter[dict[str, str] | None](dict[str, str] | None)
|
||||
_OBJECT_DICT_ADAPTER: Final = TypeAdapter[dict[str, object]](dict[str, object])
|
||||
_STRING_DICT_ADAPTER: Final = TypeAdapter[dict[str, str]](dict[str, str])
|
||||
_PROVIDERS_RESOLVING_THEIR_OWN_IMAGE_BASE: Final = (
|
||||
LlmProviders.DASHSCOPE,
|
||||
LlmProviders.QWENCLOUD,
|
||||
|
|
@ -93,8 +97,8 @@ def _chatgpt_image_params(kwargs: Mapping[str, object]) -> GenericLiteLLMParams:
|
|||
from litellm.responses.utils import ResponsesAPIRequestUtils
|
||||
|
||||
headers: Final = ResponsesAPIRequestUtils.merge_client_forwarded_headers(
|
||||
TypeAdapter[dict[str, object] | None](dict[str, object] | None).validate_python(kwargs.get("extra_headers")),
|
||||
TypeAdapter[dict[str, str] | None](dict[str, str] | None).validate_python(kwargs.get("headers")),
|
||||
_OPTIONAL_OBJECT_DICT_ADAPTER.validate_python(kwargs.get("extra_headers")),
|
||||
_OPTIONAL_STRING_DICT_ADAPTER.validate_python(kwargs.get("headers")),
|
||||
)
|
||||
return GenericLiteLLMParams.model_validate({**kwargs, "extra_headers": headers})
|
||||
|
||||
|
|
@ -167,7 +171,7 @@ async def aimage_generation(*args, **kwargs) -> ImageResponse:
|
|||
custom_llm_provider=provider_hints.get("custom_llm_provider"),
|
||||
api_base=provider_hints.get("api_base"),
|
||||
litellm_params=(
|
||||
_chatgpt_image_params(TypeAdapter(dict[str, object]).validate_python(kwargs))
|
||||
_chatgpt_image_params(_OBJECT_DICT_ADAPTER.validate_python(kwargs))
|
||||
if provider_hints.get("custom_llm_provider") == "chatgpt"
|
||||
or (isinstance(model, str) and model.startswith("chatgpt/"))
|
||||
else None
|
||||
|
|
@ -299,7 +303,7 @@ def image_generation(
|
|||
headers.update(extra_headers)
|
||||
model_response: ImageResponse = litellm.utils.ImageResponse()
|
||||
chatgpt_params: Final = (
|
||||
_chatgpt_image_params(TypeAdapter(dict[str, object]).validate_python({**kwargs, "api_key": api_key}))
|
||||
_chatgpt_image_params(_OBJECT_DICT_ADAPTER.validate_python({**kwargs, "api_key": api_key}))
|
||||
if custom_llm_provider == "chatgpt" or (model is not None and model.startswith("chatgpt/"))
|
||||
else None
|
||||
)
|
||||
|
|
@ -582,7 +586,7 @@ def image_generation(
|
|||
_chatgpt_image_headers(
|
||||
model,
|
||||
chatgpt_params.model_copy(update={"api_key": dynamic_api_key or api_key}),
|
||||
TypeAdapter(dict[str, str]).validate_python(headers),
|
||||
_STRING_DICT_ADAPTER.validate_python(headers),
|
||||
)
|
||||
)
|
||||
if chatgpt_params is not None
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ class _TranscriptionEventCollector:
|
|||
|
||||
|
||||
def _billed_usage(response: TranscriptionResponse) -> Usage:
|
||||
usage: Final[object] = getattr(response, "usage", None)
|
||||
usage: Final = response.usage
|
||||
token_usage: Final = (
|
||||
TranscriptionUsageObjectTransformation.transform_transcription_usage_object(usage)
|
||||
if isinstance(usage, TranscriptionUsageTokensObject)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from litellm.litellm_core_utils.fallback_generalizations import (
|
|||
match_routing_generalization,
|
||||
)
|
||||
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
||||
from litellm.llms.opencode.common_utils import is_opencode_api_base
|
||||
from litellm.secret_managers.main import get_secret, get_secret_str
|
||||
|
||||
from ..types.router import GenericLiteLLMParams, LiteLLM_Params
|
||||
|
|
@ -1008,6 +1009,4 @@ def _get_openai_compatible_provider_info(
|
|||
|
||||
|
||||
def get_provider_affinity_header(api_base: str | None) -> str | None:
|
||||
from litellm.llms.opencode.transformation import OpenCodeProviderAdapter
|
||||
|
||||
return OpenCodeProviderAdapter().get_provider_affinity_header(api_base)
|
||||
return "x-opencode-session" if is_opencode_api_base(api_base) else None
|
||||
|
|
|
|||
|
|
@ -320,9 +320,7 @@ def _classify_fetch_content(content: bytes, url: str, etag: str | None) -> _Fetc
|
|||
return ModelCostMapReloadUnavailable(reason=f"invalid JSON from {url}: {e}")
|
||||
if not isinstance(parsed, dict):
|
||||
return ModelCostMapReloadUnavailable(reason=f"expected a JSON object from {url}, got {type(parsed).__name__}")
|
||||
model_cost_map: Final = cast( # cast-ok: JSON object keys are strings by definition
|
||||
dict[str, object], parsed
|
||||
)
|
||||
model_cost_map: Final = _BUNDLED_CATALOG_ADAPTER.validate_python(parsed)
|
||||
return ModelCostMapReloaded(model_cost_map=model_cost_map, revision=git_blob_id(content), etag=etag)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,18 @@ _PROVIDER_HEADERS: Final[TypeAdapter[ProviderSpecificHeader | Sequence[ProviderS
|
|||
|
||||
class ProviderSpecificHeaderUtils:
|
||||
@staticmethod
|
||||
def forwarded_authorization(provider_specific_header: object, custom_llm_provider: str | None) -> str | None:
|
||||
headers: Final = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
def validated_provider_specific_headers(
|
||||
provider_specific_header: object, custom_llm_provider: str | None
|
||||
) -> Mapping[str, str]:
|
||||
return ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
_PROVIDER_HEADERS.validate_python(provider_specific_header), custom_llm_provider
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def forwarded_authorization(provider_specific_header: object, custom_llm_provider: str | None) -> str | None:
|
||||
headers: Final = ProviderSpecificHeaderUtils.validated_provider_specific_headers(
|
||||
provider_specific_header, custom_llm_provider
|
||||
)
|
||||
return next((value for key, value in headers.items() if key.lower() == "authorization"), None)
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -3389,21 +3389,12 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
set_hidden_params(served, {**(get_hidden_params(translated) or {}), **(get_hidden_params(response) or {})})
|
||||
return served
|
||||
|
||||
def _with_continued_response_id(
|
||||
def _with_response_id(
|
||||
self,
|
||||
response: ModelResponse | TextCompletionResponse | ResponsesAPIResponse | InteractionsAPIResponse,
|
||||
response_id: str | None,
|
||||
) -> ModelResponse | TextCompletionResponse | ResponsesAPIResponse | InteractionsAPIResponse:
|
||||
response_id: Final = self.model_call_details.get("continued_response_id")
|
||||
if not isinstance(response_id, str) or getattr(response, "id", None) == response_id:
|
||||
return response
|
||||
return response.model_copy(update={"id": response_id})
|
||||
|
||||
def _with_client_facing_response_id(
|
||||
self,
|
||||
response: ModelResponse | TextCompletionResponse | ResponsesAPIResponse | InteractionsAPIResponse,
|
||||
) -> ModelResponse | TextCompletionResponse | ResponsesAPIResponse | InteractionsAPIResponse:
|
||||
response_id: Final = self.client_facing_response_id
|
||||
if response_id is None or getattr(response, "id", None) == response_id:
|
||||
if response_id is None or response.id == response_id:
|
||||
return response
|
||||
return response.model_copy(update={"id": response_id})
|
||||
|
||||
|
|
@ -3411,8 +3402,13 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
self,
|
||||
response: ModelResponse | TextCompletionResponse | ResponsesAPIResponse | InteractionsAPIResponse,
|
||||
) -> ModelResponse | TextCompletionResponse | ResponsesAPIResponse | InteractionsAPIResponse:
|
||||
return self._with_client_facing_response_id(
|
||||
self._with_continued_response_id(self._with_client_facing_stream_model(response))
|
||||
continued_response_id: Final = self.model_call_details.get("continued_response_id")
|
||||
return self._with_response_id(
|
||||
self._with_response_id(
|
||||
self._with_client_facing_stream_model(response),
|
||||
continued_response_id if isinstance(continued_response_id, str) else None,
|
||||
),
|
||||
self.client_facing_response_id,
|
||||
)
|
||||
|
||||
def _success_handler_helper_fn(
|
||||
|
|
@ -6499,7 +6495,7 @@ def _init_custom_logger_compatible_class(
|
|||
logging_integration: str,
|
||||
internal_usage_cache: DualCache | None,
|
||||
llm_router: object,
|
||||
custom_logger_init_args: dict[str, object] | None = {},
|
||||
custom_logger_init_args: dict[str, object] | None = None,
|
||||
raise_import_errors: bool = False,
|
||||
) -> CustomLogger | None:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -2826,8 +2826,6 @@ class CustomStreamWrapper:
|
|||
return None if is_model_response_stream_empty(model_response=usage_free_chunk) else usage_free_chunk
|
||||
|
||||
def _accumulate_streamed_delta(self, delta: object) -> None:
|
||||
# Shared by the sync, async, and non-aiohttp iteration sites so answer
|
||||
# text and the disqualifying-content latch stay in step across all three.
|
||||
get: Final = getattr(delta, "get", None)
|
||||
content: Final = get("content") if callable(get) else None
|
||||
self.response_uptil_now += content if isinstance(content, str) else ""
|
||||
|
|
|
|||
|
|
@ -243,8 +243,7 @@ async def caller_within_model_budget(
|
|||
return "allowed"
|
||||
|
||||
model_max_budget: Final = getattr(user_api_key_auth, "model_max_budget", None)
|
||||
token: Final = getattr(user_api_key_auth, "token", None)
|
||||
if isinstance(model_max_budget, dict) and model_max_budget and token is not None:
|
||||
if isinstance(model_max_budget, dict) and model_max_budget and user_api_key_auth.token is not None:
|
||||
try:
|
||||
await model_max_budget_limiter.is_key_within_model_budget(
|
||||
user_api_key_dict=user_api_key_auth,
|
||||
|
|
@ -300,10 +299,8 @@ async def caller_within_model_budget(
|
|||
)
|
||||
return "denied_for_now"
|
||||
|
||||
end_user_model_max_budget: Final[dict[str, object] | None] = getattr(
|
||||
user_api_key_auth, "end_user_model_max_budget", None
|
||||
)
|
||||
end_user_id: Final[str | None] = getattr(user_api_key_auth, "end_user_id", None)
|
||||
end_user_model_max_budget: Final[dict[str, object] | None] = user_api_key_auth.end_user_model_max_budget
|
||||
end_user_id: Final[str | None] = user_api_key_auth.end_user_id
|
||||
if isinstance(end_user_model_max_budget, dict) and end_user_model_max_budget and end_user_id is not None:
|
||||
try:
|
||||
await model_max_budget_limiter.is_end_user_within_model_budget(
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@ class AWSPollyTextToSpeechConfig(BaseTextToSpeechConfig, BaseAWSLLM):
|
|||
else:
|
||||
mapped_params["output_format"] = self.DEFAULT_OUTPUT_FORMAT
|
||||
|
||||
# model arrives without the "aws_polly/" prefix, e.g. "standard"
|
||||
engine: Final = self._extract_engine_from_model(model)
|
||||
mapped_params["engine"] = engine
|
||||
|
||||
|
|
|
|||
|
|
@ -455,9 +455,7 @@ def _converse_request(
|
|||
if not all(_text_only(message) for message in messages):
|
||||
return None
|
||||
try:
|
||||
chat_tools: Final = tuple(
|
||||
chat_tool for tool in tools or () for chat_tool in _as_converse_input(tool)
|
||||
) # comprehension-ok: flattens each tool's translation
|
||||
chat_tools: Final = tuple(chain.from_iterable(_as_converse_input(tool) for tool in tools or ()))
|
||||
return _ConverseRequest.model_validate(
|
||||
litellm.AmazonConverseConfig().transform_request(
|
||||
model=model,
|
||||
|
|
|
|||
|
|
@ -66,6 +66,13 @@ def get_cached_authenticator(auth_file: str) -> "Authenticator":
|
|||
return Authenticator(auth_file=auth_file)
|
||||
|
||||
|
||||
def resolve_chatgpt_authenticator(
|
||||
litellm_params: "Mapping[str, object] | GenericLiteLLMParams | None", default: "Authenticator"
|
||||
) -> "Authenticator":
|
||||
auth_file: Final = get_chatgpt_auth_file(litellm_params)
|
||||
return get_cached_authenticator(auth_file) if auth_file else default
|
||||
|
||||
|
||||
def _token_refresh_timeout() -> httpx.Timeout:
|
||||
configured: Final = get_configured_request_timeout()
|
||||
seconds: Final = (
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from litellm.llms.openai.openai import OpenAIConfig
|
|||
from litellm.types.llms.openai import AllMessageValues
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
from ..authenticator import Authenticator, get_cached_authenticator, get_chatgpt_auth_file
|
||||
from ..authenticator import Authenticator, get_chatgpt_auth_file, resolve_chatgpt_authenticator
|
||||
from ..common_utils import (
|
||||
GetAccessTokenError,
|
||||
chatgpt_client_api_base,
|
||||
|
|
@ -30,12 +30,6 @@ class ChatGPTConfig(OpenAIConfig):
|
|||
super().__init__()
|
||||
self.authenticator = authenticator if authenticator is not None else Authenticator()
|
||||
|
||||
def _resolve_authenticator(
|
||||
self, litellm_params: Mapping[str, object] | GenericLiteLLMParams | None
|
||||
) -> Authenticator:
|
||||
auth_file: Final = get_chatgpt_auth_file(litellm_params)
|
||||
return get_cached_authenticator(auth_file) if auth_file else self.authenticator
|
||||
|
||||
@staticmethod
|
||||
def _get_access_token_or_raise(authenticator: Authenticator, model: str, llm_provider: str) -> str:
|
||||
try:
|
||||
|
|
@ -63,7 +57,7 @@ class ChatGPTConfig(OpenAIConfig):
|
|||
return chatgpt_client_api_base(api_base), client_credential.access_token, custom_llm_provider
|
||||
if api_key:
|
||||
return api_base or self.api_base_without_login(), api_key, custom_llm_provider
|
||||
authenticator: Final = self._resolve_authenticator(litellm_params)
|
||||
authenticator: Final = resolve_chatgpt_authenticator(litellm_params, self.authenticator)
|
||||
dynamic_api_base: Final = authenticator.get_api_base()
|
||||
dynamic_api_key: Final = self._get_access_token_or_raise(authenticator, model, custom_llm_provider)
|
||||
return dynamic_api_base, dynamic_api_key, custom_llm_provider
|
||||
|
|
@ -98,7 +92,7 @@ class ChatGPTConfig(OpenAIConfig):
|
|||
),
|
||||
**strip_chatgpt_client_credential_headers(headers),
|
||||
)
|
||||
authenticator: Final = self._resolve_authenticator(litellm_params)
|
||||
authenticator: Final = resolve_chatgpt_authenticator(litellm_params, self.authenticator)
|
||||
resolved_api_key: Final = (
|
||||
self._get_access_token_or_raise(authenticator, model, "chatgpt")
|
||||
if get_chatgpt_auth_file(litellm_params)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import hashlib
|
|||
import json
|
||||
import os
|
||||
import platform
|
||||
from collections.abc import Mapping, Sequence
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
from types import MappingProxyType
|
||||
from typing import TYPE_CHECKING, Final, TypeAlias
|
||||
|
|
@ -20,14 +20,10 @@ from pydantic import JsonValue, TypeAdapter
|
|||
from litellm.litellm_core_utils.get_provider_specific_headers import ProviderSpecificHeaderUtils
|
||||
from litellm.litellm_core_utils.provider_affinity import get_caller_litellm_session_id, get_requested_session_id
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
from litellm.types.utils import ProviderSpecificHeader
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
_SCOPED_HEADERS: Final[TypeAdapter[ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None]] = TypeAdapter(
|
||||
ProviderSpecificHeader | Sequence[ProviderSpecificHeader] | None
|
||||
)
|
||||
_STRING_HEADERS: Final = TypeAdapter(Mapping[str, str])
|
||||
_PARAMS: Final = TypeAdapter(Mapping[str, object])
|
||||
|
||||
|
|
@ -357,8 +353,8 @@ def get_chatgpt_request_credential(
|
|||
normalized: Final = _normalize_litellm_params(params)
|
||||
scoped: Final = normalized.get("provider_specific_header")
|
||||
extra: Final = normalized.get("extra_headers")
|
||||
headers: Final = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
provider_specific_header=_SCOPED_HEADERS.validate_python(scoped), custom_llm_provider="chatgpt"
|
||||
headers: Final = ProviderSpecificHeaderUtils.validated_provider_specific_headers(
|
||||
provider_specific_header=scoped, custom_llm_provider="chatgpt"
|
||||
)
|
||||
return get_chatgpt_client_credential(headers) or get_chatgpt_client_credential(
|
||||
_STRING_HEADERS.validate_python(extra) if extra is not None else MappingProxyType({})
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ from litellm.types.llms.openai import (
|
|||
from litellm.types.router import GenericLiteLLMParams
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
||||
from ..authenticator import Authenticator, get_cached_authenticator, get_chatgpt_auth_file
|
||||
from ..authenticator import Authenticator, get_chatgpt_auth_file, resolve_chatgpt_authenticator
|
||||
from ..common_utils import (
|
||||
CHATGPT_API_BASE,
|
||||
GetAccessTokenError,
|
||||
|
|
@ -64,10 +64,6 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig):
|
|||
def custom_llm_provider(self) -> LlmProviders:
|
||||
return LlmProviders.CHATGPT
|
||||
|
||||
def _resolve_authenticator(self, litellm_params: GenericLiteLLMParams | None) -> Authenticator:
|
||||
auth_file: Final = get_chatgpt_auth_file(litellm_params)
|
||||
return get_cached_authenticator(auth_file) if auth_file else self.authenticator
|
||||
|
||||
def validate_environment(
|
||||
self,
|
||||
headers: dict[str, str],
|
||||
|
|
@ -81,7 +77,7 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig):
|
|||
**get_chatgpt_default_headers(client_credential.access_token, client_credential.account_id, session_id),
|
||||
**strip_chatgpt_client_credential_headers(headers),
|
||||
}
|
||||
authenticator: Final = self._resolve_authenticator(litellm_params)
|
||||
authenticator: Final = resolve_chatgpt_authenticator(litellm_params, self.authenticator)
|
||||
safe_headers: Final = strip_chatgpt_client_credential_headers(headers)
|
||||
configured_api_key: Final = litellm_params.api_key if litellm_params is not None else None
|
||||
if configured_api_key and not get_chatgpt_auth_file(litellm_params):
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ class DatabricksAnthropicMessagesConfig(DatabricksBase, AnthropicMessagesConfig)
|
|||
headers=headers,
|
||||
)
|
||||
self._normalize_system_role_messages( # pyright: ignore[reportUnknownMemberType] # the shared helper takes a bare dict
|
||||
anthropic_messages_request, model=model, drop_params=getattr(litellm_params, "drop_params", None)
|
||||
anthropic_messages_request, model=model, drop_params=litellm_params.drop_params
|
||||
)
|
||||
self._remove_scope_from_cache_control(anthropic_messages_request) # pyright: ignore[reportUnknownMemberType] # the shared helper takes a bare dict
|
||||
return anthropic_messages_request
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ def _system_instruction_text(system_instruction: HttpxContentType | None) -> str
|
|||
class GeminiRealtimeConfig(BaseRealtimeConfig):
|
||||
_TOOL_CALL_ID_TO_NAME_MAX = 256 # LRU cap for call_id→name mapping
|
||||
|
||||
def __init__(self, drop_params: bool = False):
|
||||
def __init__(self, drop_params: bool = False) -> None:
|
||||
super().__init__()
|
||||
self.drop_params = drop_params
|
||||
self._tool_call_id_to_name: OrderedDict[str, str] = OrderedDict()
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
from litellm.llms.opencode.common_utils import is_opencode_api_base
|
||||
|
||||
|
||||
class OpenCodeProviderAdapter:
|
||||
def get_provider_affinity_header(self, api_base: str | None) -> str | None:
|
||||
return "x-opencode-session" if is_opencode_api_base(api_base) else None
|
||||
|
|
@ -249,7 +249,7 @@ class VertexAIPartnerModelsAnthropicMessagesConfig(AnthropicMessagesConfig, Vert
|
|||
)
|
||||
|
||||
self._normalize_system_role_messages(
|
||||
anthropic_messages_request, model=model, drop_params=getattr(litellm_params, "drop_params", None)
|
||||
anthropic_messages_request, model=model, drop_params=litellm_params.drop_params
|
||||
)
|
||||
|
||||
self._remove_scope_from_cache_control(anthropic_messages_request)
|
||||
|
|
|
|||
|
|
@ -6069,7 +6069,6 @@ def completion(
|
|||
return resolve_timeout(stream_kind=StreamKind.NOT_STREAMED)
|
||||
return resolve_timeout(stream_kind=StreamKind.FAKED if fakes_stream(attempt_params) else StreamKind.LIVE)
|
||||
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = timeout_for_optional_params(optional_params)
|
||||
logging.record_applied_timeout(timeout)
|
||||
processed_non_default_params: Final = pre_process_non_default_params(
|
||||
|
|
|
|||
|
|
@ -574,7 +574,6 @@ def is_model_cost_zero(model: str | list[str] | None, llm_router: RegistryReader
|
|||
return False
|
||||
continue
|
||||
try:
|
||||
# Use the router registry's group info directly for better reliability
|
||||
model_group_info = llm_router.registry().group(model_name, include_hidden=True)
|
||||
|
||||
if model_group_info is None:
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ def fips_mode_refusal(general_settings: Mapping[str, object]) -> str | None:
|
|||
|
||||
|
||||
def enforce_fips_mode(general_settings: Mapping[str, object]) -> None:
|
||||
"""Raise FipsModeError with the fips_mode_refusal reason, if there is one."""
|
||||
refusal: Final = fips_mode_refusal(general_settings)
|
||||
if refusal is not None:
|
||||
raise FipsModeError(refusal)
|
||||
|
|
|
|||
|
|
@ -598,7 +598,7 @@ class SpendLogCleanup:
|
|||
rows: Final = _STORAGE_ROWS.validate_python(
|
||||
await tx.query_raw(trusted_sql(_STORAGE_SQL), list(_SPEND_LOG_TABLES))
|
||||
)
|
||||
except Exception as e: # noqa: BLE001 - a failed measurement skips the size pass, it must not fail the run
|
||||
except Exception as e: # noqa: BLE001 # a failed measurement skips the size pass, it must not fail the run
|
||||
verbose_proxy_logger.warning("Could not measure spend-log storage: %s: %s", type(e).__name__, e)
|
||||
return None
|
||||
if not rows:
|
||||
|
|
@ -666,7 +666,7 @@ class SpendLogCleanup:
|
|||
await tx.query_raw(trusted_sql(_LOAD_SIZE_STATE_SQL), SPEND_LOG_SIZE_CLEANUP_STATE_PARAM)
|
||||
)
|
||||
return SizeCleanupState.model_validate_json(rows[0].state_json) if rows else None
|
||||
except Exception as e: # noqa: BLE001 - an unreadable count restarts from the catalog, it must not fail the run
|
||||
except Exception as e: # noqa: BLE001 # an unreadable count restarts from the catalog, it must not fail the run
|
||||
verbose_proxy_logger.warning("Could not read the spend-log size cleanup state: %s: %s", type(e).__name__, e)
|
||||
return None
|
||||
|
||||
|
|
@ -678,7 +678,7 @@ class SpendLogCleanup:
|
|||
await tx.execute_raw(
|
||||
trusted_sql(_SAVE_SIZE_STATE_SQL), SPEND_LOG_SIZE_CLEANUP_STATE_PARAM, state.model_dump_json()
|
||||
)
|
||||
except Exception as e: # noqa: BLE001 - a lost save costs one re-seed from the catalog, it must not fail the run
|
||||
except Exception as e: # noqa: BLE001 # a lost save costs one re-seed from the catalog, it must not fail the run
|
||||
verbose_proxy_logger.warning("Could not save the spend-log size cleanup state: %s: %s", type(e).__name__, e)
|
||||
|
||||
async def _size_step_cutoff(self, prisma_client: PrismaClient, deadline: float, rows: int) -> datetime | None:
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail"
|
|||
additional_provider_specific_params=getattr(litellm_params, "additional_provider_specific_params", {}),
|
||||
unreachable_fallback=getattr(litellm_params, "unreachable_fallback", "fail_closed"),
|
||||
fail_on_error=getattr(litellm_params, "fail_on_error", True),
|
||||
forward_messages_as_sent=getattr(litellm_params, "forward_messages_as_sent", False),
|
||||
forward_messages_as_sent=litellm_params.forward_messages_as_sent,
|
||||
extra_headers=getattr(litellm_params, "extra_headers", None),
|
||||
guardrail_name=guardrail.get("guardrail_name", ""),
|
||||
event_hook=litellm_params.mode,
|
||||
|
|
|
|||
|
|
@ -504,8 +504,7 @@ class NomaGuardrail(CustomGuardrail):
|
|||
messages[i]["content"] = anonymized_content
|
||||
break
|
||||
|
||||
def _replace_llm_response_content(self, response: LLMResponse, anonymized_content: str, position: int):
|
||||
"""Replace the content of the choice at ``position`` with its anonymized version."""
|
||||
def _replace_llm_response_content(self, response: LLMResponse, anonymized_content: str, position: int) -> None:
|
||||
if not isinstance(response, litellm.ModelResponse):
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ class _UserId(BaseModel):
|
|||
_USER_IDS: Final = TypeAdapter(tuple[_UserId, ...])
|
||||
|
||||
|
||||
def _csv_values(raw: str | None) -> tuple[str, ...]:
|
||||
return tuple(value.strip() for value in (raw or "").split(",") if value.strip())
|
||||
|
||||
|
||||
async def sorted_user_ids(
|
||||
prisma_client: PrismaClient,
|
||||
*,
|
||||
|
|
@ -30,9 +34,9 @@ async def sorted_user_ids(
|
|||
organization_ids: str | None,
|
||||
search: str | None,
|
||||
) -> Sequence[str]:
|
||||
ids: Final = tuple(value.strip() for value in (user_ids or "").split(",") if value.strip())
|
||||
sso_ids: Final = tuple(value.strip() for value in (sso_user_ids or "").split(",") if value.strip())
|
||||
org_ids: Final = tuple(value.strip() for value in (organization_ids or "").split(",") if value.strip())
|
||||
ids: Final = _csv_values(user_ids)
|
||||
sso_ids: Final = _csv_values(sso_user_ids)
|
||||
org_ids: Final = _csv_values(organization_ids)
|
||||
expression: Final = (
|
||||
"CASE WHEN u.metadata->'scim_active' = 'false'::jsonb THEN 1 ELSE 0 END"
|
||||
if sort_by == "status"
|
||||
|
|
|
|||
|
|
@ -6253,9 +6253,9 @@ class MCPServerManager:
|
|||
"server_name": server_name,
|
||||
"mcp_rate_limit_server_name": server.alias or server.server_name or server.name,
|
||||
"user_api_key_auth": checked_auth,
|
||||
"user_api_key_user_id": (getattr(checked_auth, "user_id", None) if checked_auth else None),
|
||||
"user_api_key_team_id": (getattr(checked_auth, "team_id", None) if checked_auth else None),
|
||||
"user_api_key_end_user_id": (getattr(checked_auth, "end_user_id", None) if checked_auth else None),
|
||||
"user_api_key_user_id": checked_auth.user_id if checked_auth else None,
|
||||
"user_api_key_team_id": checked_auth.team_id if checked_auth else None,
|
||||
"user_api_key_end_user_id": checked_auth.end_user_id if checked_auth else None,
|
||||
"user_api_key_hash": (getattr(checked_auth, "api_key_hash", None) if checked_auth else None),
|
||||
"incoming_bearer_token": caller_bearer.get_secret_value() if caller_bearer is not None else None,
|
||||
"headers": logging_safe_mcp_headers(raw_headers),
|
||||
|
|
|
|||
|
|
@ -1417,13 +1417,12 @@ async def handle_bedrock_count_tokens(
|
|||
if deployment is not None:
|
||||
model_litellm_params: Final = deployment.get("litellm_params", {})
|
||||
|
||||
# Get the resolved model ID from the configuration
|
||||
if "model" in model_litellm_params:
|
||||
resolved_model = model_litellm_params["model"]
|
||||
|
||||
# Copy all litellm_params - BaseAWSLLM will handle AWS credential discovery
|
||||
for key, value in model_litellm_params.items():
|
||||
if key != "user_api_key_dict": # Don't overwrite user_api_key_dict
|
||||
if key != "user_api_key_dict":
|
||||
litellm_params[key] = value
|
||||
|
||||
verbose_proxy_logger.debug("Count tokens litellm_params: %s", litellm_params)
|
||||
|
|
|
|||
|
|
@ -158,9 +158,7 @@ def _responses_turn(event: Mapping[str, object], session_model: str) -> _Complet
|
|||
return _CompletedTurn(
|
||||
model=ended.response.model or session_model,
|
||||
response=ended.response,
|
||||
usage=ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage( # pyright: ignore[reportPrivateUsage] # the one shared Responses usage converter
|
||||
ended.response.usage
|
||||
),
|
||||
usage=ResponseAPILoggingUtils.transform_response_api_usage_to_chat_usage(ended.response.usage),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13919,7 +13919,7 @@ async def audio_transcriptions(
|
|||
api_base=api_base,
|
||||
version=version,
|
||||
response_cost=response_cost,
|
||||
model_region=getattr(user_api_key_dict, "allowed_model_region", ""),
|
||||
model_region=user_api_key_dict.allowed_model_region,
|
||||
call_id=response_call_id,
|
||||
request_data=data,
|
||||
hidden_params=hidden_params,
|
||||
|
|
@ -17009,9 +17009,6 @@ async def _apply_search_filter_to_models(
|
|||
team_public_model_name: Final = (m.get("model_info") or {}).get("team_public_model_name") or ""
|
||||
if search_lower in team_public_model_name.lower():
|
||||
return True
|
||||
# Also match the underlying LiteLLM model name (e.g.
|
||||
# "openrouter/deepseek/deepseek-chat"), so users can find
|
||||
# deployments by typing the provider or the upstream model id.
|
||||
litellm_params: Final[Mapping[str, object]] = m.get("litellm_params") or MappingProxyType({})
|
||||
litellm_model: Final = litellm_params.get("model")
|
||||
return isinstance(litellm_model, str) and search_lower in litellm_model.lower()
|
||||
|
|
|
|||
|
|
@ -269,23 +269,13 @@ async def _query_raw_or_none(prisma_client: PrismaClient, sql_query: str, *args:
|
|||
|
||||
|
||||
async def _query_spend_logs(prisma_client: PrismaClient, sql_query: str, *args: object) -> Sequence[_RowT]:
|
||||
"""``_query_raw`` for raw ``LiteLLM_SpendLogs`` reads, which Postgres cancels at the statement timeout."""
|
||||
return cast( # cast-ok: callers declare the row shape their SELECT list produces, as with _query_raw
|
||||
"Sequence[_RowT]", await query_raw_within_statement_timeout(prisma_client, sql_query, *args)
|
||||
)
|
||||
|
||||
|
||||
async def _query_spend_logs_or_none(
|
||||
prisma_client: PrismaClient, sql_query: str, *args: object
|
||||
) -> Sequence[_RowT] | None:
|
||||
"""``_query_spend_logs`` for the call sites that guard the result against ``None``."""
|
||||
return await _query_spend_logs(prisma_client, sql_query, *args)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class _SpendLogsReader:
|
||||
"""``read_session_page``'s raw-query handle, each read cancelled by Postgres at the statement timeout."""
|
||||
|
||||
prisma_client: PrismaClient
|
||||
|
||||
async def query_raw(self, query: str, *args: object) -> Sequence[Mapping[str, object]]:
|
||||
|
|
@ -1591,12 +1581,7 @@ async def get_global_spend_report(
|
|||
ORDER BY
|
||||
total_cost DESC;
|
||||
"""
|
||||
db_response = await _query_spend_logs_or_none(
|
||||
prisma_client, sql_query, start_date_obj, end_date_obj, api_key
|
||||
)
|
||||
if db_response is None:
|
||||
return []
|
||||
|
||||
db_response = await _query_spend_logs(prisma_client, sql_query, start_date_obj, end_date_obj, api_key)
|
||||
return db_response
|
||||
elif internal_user_id is not None:
|
||||
verbose_proxy_logger.debug("Getting /spend for internal_user_id: %s", internal_user_id)
|
||||
|
|
@ -1636,12 +1621,9 @@ async def get_global_spend_report(
|
|||
ORDER BY
|
||||
total_cost DESC;
|
||||
"""
|
||||
db_response = await _query_spend_logs_or_none(
|
||||
db_response = await _query_spend_logs(
|
||||
prisma_client, sql_query, start_date_obj, end_date_obj, internal_user_id
|
||||
)
|
||||
if db_response is None:
|
||||
return []
|
||||
|
||||
return db_response
|
||||
elif team_id is not None and customer_id is not None:
|
||||
return await get_spend_by_team_and_customer(
|
||||
|
|
@ -1703,10 +1685,7 @@ async def get_global_spend_report(
|
|||
group_by_day;
|
||||
"""
|
||||
|
||||
db_response = await _query_spend_logs_or_none(prisma_client, sql_query, start_date_obj, end_date_obj)
|
||||
if db_response is None:
|
||||
return []
|
||||
|
||||
db_response = await _query_spend_logs(prisma_client, sql_query, start_date_obj, end_date_obj)
|
||||
return db_response
|
||||
elif group_by == "api_key":
|
||||
sql_query = """
|
||||
|
|
@ -1744,10 +1723,7 @@ async def get_global_spend_report(
|
|||
ORDER BY
|
||||
total_cost DESC;
|
||||
"""
|
||||
db_response = await _query_spend_logs_or_none(prisma_client, sql_query, start_date_obj, end_date_obj)
|
||||
if db_response is None:
|
||||
return []
|
||||
|
||||
db_response = await _query_spend_logs(prisma_client, sql_query, start_date_obj, end_date_obj)
|
||||
return db_response
|
||||
|
||||
except HTTPException:
|
||||
|
|
@ -1995,7 +1971,7 @@ async def get_key_spend_report(
|
|||
caller_value=LiteLLMProxyRequestSetup.get_logged_api_key(user_api_key_dict),
|
||||
scope_name="api_key",
|
||||
)
|
||||
db_response: Sequence[Mapping[str, object]] | None = await _query_spend_logs_or_none(
|
||||
db_response: Sequence[Mapping[str, object]] = await _query_spend_logs(
|
||||
prisma_client,
|
||||
_scoped_spend_report_sql(scope_column="api_key"),
|
||||
start_date_obj,
|
||||
|
|
@ -2037,7 +2013,7 @@ async def get_user_spend_report(
|
|||
caller_value=user_api_key_dict.user_id,
|
||||
scope_name="internal_user_id",
|
||||
)
|
||||
db_response: Sequence[Mapping[str, object]] | None = await _query_spend_logs_or_none(
|
||||
db_response: Sequence[Mapping[str, object]] = await _query_spend_logs(
|
||||
prisma_client,
|
||||
_scoped_spend_report_sql(scope_column="user"),
|
||||
start_date_obj,
|
||||
|
|
@ -2079,7 +2055,7 @@ async def get_team_spend_report(
|
|||
caller_value=user_api_key_dict.team_id,
|
||||
scope_name="team_id",
|
||||
)
|
||||
db_response: Sequence[Mapping[str, object]] | None = await _query_spend_logs_or_none(
|
||||
db_response: Sequence[Mapping[str, object]] = await _query_spend_logs(
|
||||
prisma_client,
|
||||
_scoped_spend_report_sql(scope_column="team_id"),
|
||||
start_date_obj,
|
||||
|
|
@ -2121,7 +2097,7 @@ async def get_organization_spend_report(
|
|||
organization_id=organization_id,
|
||||
prisma_client=prisma_client,
|
||||
)
|
||||
db_response: Sequence[Mapping[str, object]] | None = await _query_spend_logs_or_none(
|
||||
db_response: Sequence[Mapping[str, object]] = await _query_spend_logs(
|
||||
prisma_client,
|
||||
_ORG_SPEND_REPORT_SQL,
|
||||
start_date_obj,
|
||||
|
|
@ -3046,7 +3022,7 @@ async def ui_view_spend_logs(
|
|||
LIMIT ${p}
|
||||
) AS bounded_matches
|
||||
"""
|
||||
count_rows: Final[Sequence[_SpendLogsCountRow] | None] = await _query_spend_logs_or_none(
|
||||
count_rows: Final[Sequence[_SpendLogsCountRow]] = await _query_spend_logs(
|
||||
prisma_client, count_query, *sql_params, SPEND_LOGS_PAGINATION_COUNT_CAP + 1
|
||||
)
|
||||
raw_total: Final = int(count_rows[0]["total_count"]) if count_rows else 0
|
||||
|
|
|
|||
|
|
@ -1094,7 +1094,6 @@ async def _query_raw_rows(
|
|||
sql_query: str,
|
||||
*args: object,
|
||||
) -> Sequence[Mapping[str, object]] | None:
|
||||
"""A raw ``LiteLLM_SpendLogs`` read that Postgres cancels at the statement timeout."""
|
||||
return await query_raw_within_statement_timeout(prisma_client, sql_query, *args)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ from dataclasses import dataclass, field
|
|||
from datetime import date, datetime, timedelta, timezone
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from functools import partial
|
||||
from functools import partial, reduce
|
||||
from itertools import takewhile
|
||||
from operator import attrgetter
|
||||
from types import MappingProxyType
|
||||
|
|
@ -1681,14 +1681,17 @@ _LOG_DB_METRICS_CALL_TYPES: Final = frozenset(("get_data", "insert_data", "updat
|
|||
_EXCEPTION_CHAIN_MAX_DEPTH: Final = 16
|
||||
|
||||
|
||||
def _extended_exception_chain(chain: tuple[BaseException, ...], iteration: int) -> tuple[BaseException, ...]:
|
||||
if len(chain) != iteration + 1:
|
||||
return chain
|
||||
following: Final = chain[-1].__cause__ or chain[-1].__context__
|
||||
if following is None or following in chain:
|
||||
return chain
|
||||
return (*chain, following)
|
||||
|
||||
|
||||
def _exception_chain(exception: BaseException) -> tuple[BaseException, ...]:
|
||||
chain: Final[list[BaseException]] = [exception] # mutable-ok: built once, returned frozen
|
||||
for _ in range(_EXCEPTION_CHAIN_MAX_DEPTH):
|
||||
following = chain[-1].__cause__ or chain[-1].__context__
|
||||
if following is None or following in chain:
|
||||
break
|
||||
chain.append(following)
|
||||
return tuple(chain)
|
||||
return reduce(_extended_exception_chain, range(_EXCEPTION_CHAIN_MAX_DEPTH), (exception,))
|
||||
|
||||
|
||||
def _timed_out_reading_from_provider(exception: Exception) -> bool:
|
||||
|
|
|
|||
|
|
@ -759,9 +759,7 @@ def _api_base_kwarg(kwargs: Mapping[str, object]) -> str | None:
|
|||
return api_base if isinstance(api_base, str) else None
|
||||
|
||||
|
||||
def _dispatched_model_name(
|
||||
model: str, custom_llm_provider: str, api_base: str | None, litellm_params: GenericLiteLLMParams | None = None
|
||||
) -> str:
|
||||
def _dispatched_model_name(model: str, custom_llm_provider: str) -> str:
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import resolve_model_and_provider_for_metadata
|
||||
|
||||
provider_model, _ = resolve_model_and_provider_for_metadata(model=model, custom_llm_provider=custom_llm_provider)
|
||||
|
|
@ -774,7 +772,6 @@ def _will_bridge_to_chat_completions(
|
|||
use_chat_completions_api: bool,
|
||||
model_info: object,
|
||||
api_base: str | None,
|
||||
litellm_params: GenericLiteLLMParams | None = None,
|
||||
) -> bool:
|
||||
"""``_bridges_to_chat_completions`` for callers running before the provider config is resolved.
|
||||
|
||||
|
|
@ -791,7 +788,7 @@ def _will_bridge_to_chat_completions(
|
|||
return True
|
||||
return _bridges_to_chat_completions(
|
||||
_resolve_responses_api_provider_config(
|
||||
_dispatched_model_name(normalized_model[0], custom_llm_provider, api_base, litellm_params),
|
||||
_dispatched_model_name(normalized_model[0], custom_llm_provider),
|
||||
custom_llm_provider,
|
||||
model_info,
|
||||
api_base,
|
||||
|
|
@ -816,7 +813,6 @@ def provider_stores_the_conversation(model: str, custom_llm_provider: str | None
|
|||
bool(kwargs.get("use_chat_completions_api")),
|
||||
kwargs.get("model_info"),
|
||||
api_base,
|
||||
litellm_params,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -999,7 +995,6 @@ async def aresponses(
|
|||
bool(kwargs.get("use_chat_completions_api")),
|
||||
kwargs.get("model_info"),
|
||||
_api_base_kwarg(kwargs),
|
||||
GenericLiteLLMParams(**kwargs),
|
||||
),
|
||||
):
|
||||
_stamp_injection_points_for_the_hook(kwargs, model, custom_llm_provider, input, tools)
|
||||
|
|
@ -1175,7 +1170,6 @@ def _apply_prompt_management_to_responses_call(
|
|||
use_chat_completions_api,
|
||||
kwargs.get("model_info"),
|
||||
_api_base_kwarg(kwargs),
|
||||
GenericLiteLLMParams(**kwargs),
|
||||
),
|
||||
):
|
||||
_stamp_injection_points_for_the_hook(kwargs, model, custom_llm_provider, input, tools)
|
||||
|
|
|
|||
|
|
@ -1031,9 +1031,7 @@ class BaseResponsesAPIStreamingIterator:
|
|||
)
|
||||
if estimate is None:
|
||||
return
|
||||
usage: Final = ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage( # pyright: ignore[reportPrivateUsage] # same shared transform the success path above uses
|
||||
estimate
|
||||
)
|
||||
usage: Final = ResponseAPILoggingUtils.transform_response_api_usage_to_chat_usage(estimate)
|
||||
served_tier: Final[object] = getattr(self._created_response, "service_tier", None)
|
||||
assembled: Final = ModelResponse(
|
||||
model=self.model, usage=usage, service_tier=served_tier if isinstance(served_tier, str) else None
|
||||
|
|
@ -1892,7 +1890,7 @@ def stamp_responses_usage_cost(
|
|||
return
|
||||
if isinstance(bill, bool) or not isinstance(bill, (int, float)):
|
||||
return
|
||||
if bill > 0 or isinstance(getattr(usage_obj, "cost", None), (int, float)):
|
||||
if bill > 0 or isinstance(usage_obj.cost, (int, float)):
|
||||
stamp_litellm_bill_on_usage(response_obj, usage_obj, float(bill))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class CustomDenseEncoder(DenseEncoder):
|
|||
litellm_router_instance: ClassVar = RenamedRouterAttribute() # deprecated: the router attribute before ``ctx``
|
||||
|
||||
@renamed_router_param("litellm_router_instance")
|
||||
def __init__(self, ctx: Optional["RoutingContext"] = None, **kwargs):
|
||||
def __init__(self, ctx: Optional["RoutingContext"] = None, **kwargs) -> None:
|
||||
if "ctx" in kwargs:
|
||||
ctx = kwargs.pop("ctx")
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ class LiteLLMRouterEncoder(CustomDenseEncoder, AsymmetricDenseMixin):
|
|||
model_name: str,
|
||||
score_threshold: float | None = None,
|
||||
max_input_chars: int = 0,
|
||||
):
|
||||
) -> None:
|
||||
"""Initialize the LiteLLMEncoder.
|
||||
|
||||
:param ctx: The routing context of the owning router.
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class MaxParallelRequestsLimit:
|
|||
class InitalizeCachedClient:
|
||||
@staticmethod
|
||||
@renamed_router_param("litellm_router_instance")
|
||||
def set_max_parallel_requests_client(ctx: "RoutingContext", model: dict):
|
||||
def set_max_parallel_requests_client(ctx: "RoutingContext", model: dict) -> None:
|
||||
litellm_params: Final = model.get("litellm_params", {})
|
||||
model_id: Final = model["model_info"]["id"]
|
||||
rpm: Final = litellm_params.get("rpm", None)
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ class ModelRateLimitingCheck(CustomLogger):
|
|||
this check actively enforces those limits across ALL routing strategies.
|
||||
"""
|
||||
|
||||
def __init__(self, dual_cache: DualCache, clock: Callable[[], datetime] = get_utc_datetime):
|
||||
def __init__(self, dual_cache: DualCache, clock: Callable[[], datetime] = get_utc_datetime) -> None:
|
||||
self.dual_cache = dual_cache
|
||||
self.clock = clock
|
||||
# model_ids already warned about itpm/otpm + tpm/rpm on the same deployment,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class SearchAPIRouter:
|
|||
|
||||
@staticmethod
|
||||
@renamed_router_param("router_instance")
|
||||
async def update_router_search_tools(ctx: "RoutingContext", search_tools: list):
|
||||
async def update_router_search_tools(ctx: "RoutingContext", search_tools: list) -> None:
|
||||
"""
|
||||
Update the router with search tools from the database.
|
||||
|
||||
|
|
@ -143,7 +143,6 @@ class SearchAPIRouter:
|
|||
# Set up kwargs for the fallback system
|
||||
kwargs["model"] = search_tool_name # Use model field for compatibility with fallback system
|
||||
kwargs["original_generic_function"] = original_function
|
||||
# Bind ctx to the helper method using partial
|
||||
kwargs["original_function"] = partial(
|
||||
SearchAPIRouter.async_search_with_fallbacks_helper,
|
||||
litellm_routing_ctx=ctx,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class TextCompletionStreamWrapper:
|
|||
def __aiter__(self):
|
||||
return self
|
||||
|
||||
def convert_to_text_completion_object(self, chunk: ModelResponse | ModelResponseStream):
|
||||
def convert_to_text_completion_object(self, chunk: ModelResponse | ModelResponseStream) -> TextCompletionResponse:
|
||||
try:
|
||||
response: Final = TextCompletionResponse()
|
||||
response["id"] = chunk.get("id", None)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import pytest
|
|||
|
||||
from litellm.constants import SESSION_ID_GENERATED_METADATA_KEY
|
||||
from litellm.litellm_core_utils.provider_affinity import add_provider_affinity_header
|
||||
from litellm.llms.opencode.transformation import OpenCodeProviderAdapter
|
||||
from litellm.litellm_core_utils.get_llm_provider_logic import get_provider_affinity_header
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -22,7 +22,7 @@ def test_opencode_defaults_affinity_header_to_the_callers_session(api_base: str)
|
|||
headers: Final = {"Authorization": "Bearer test-key"}
|
||||
params: Final = {"metadata": {"session_id": "conversation-123"}}
|
||||
assert add_provider_affinity_header(
|
||||
headers, params, OpenCodeProviderAdapter().get_provider_affinity_header(api_base)
|
||||
headers, params, get_provider_affinity_header(api_base)
|
||||
) == {
|
||||
**headers,
|
||||
"x-opencode-session": "conversation-123",
|
||||
|
|
@ -50,7 +50,7 @@ def test_other_destinations_do_not_receive_an_automatic_session_header(api_base:
|
|||
add_provider_affinity_header(
|
||||
headers,
|
||||
{"litellm_session_id": "conversation-123"},
|
||||
OpenCodeProviderAdapter().get_provider_affinity_header(api_base),
|
||||
get_provider_affinity_header(api_base),
|
||||
)
|
||||
== headers
|
||||
)
|
||||
|
|
@ -71,7 +71,7 @@ def test_other_destinations_do_not_receive_an_automatic_session_header(api_base:
|
|||
def test_opencode_does_not_invent_a_conversation_id(params: dict[str, object]) -> None:
|
||||
assert (
|
||||
add_provider_affinity_header(
|
||||
{}, params, OpenCodeProviderAdapter().get_provider_affinity_header("https://opencode.ai/zen/go/v1")
|
||||
{}, params, get_provider_affinity_header("https://opencode.ai/zen/go/v1")
|
||||
)
|
||||
== {}
|
||||
)
|
||||
|
|
@ -81,10 +81,10 @@ def test_opencode_preserves_explicit_header_and_affinity_configuration() -> None
|
|||
assert add_provider_affinity_header(
|
||||
{"X-OpenCode-Session": "explicit"},
|
||||
{"litellm_session_id": "conversation-123"},
|
||||
OpenCodeProviderAdapter().get_provider_affinity_header("https://opencode.ai/zen/go/v1"),
|
||||
get_provider_affinity_header("https://opencode.ai/zen/go/v1"),
|
||||
) == {"X-OpenCode-Session": "explicit"}
|
||||
assert add_provider_affinity_header(
|
||||
{},
|
||||
{"litellm_session_id": "conversation-123", "provider_affinity_header": "x-custom-session"},
|
||||
OpenCodeProviderAdapter().get_provider_affinity_header("https://opencode.ai/zen/go/v1"),
|
||||
get_provider_affinity_header("https://opencode.ai/zen/go/v1"),
|
||||
) == {"x-custom-session": "conversation-123"}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue