refactor(types): replace Any with proven types in 157 files (#44798)

* refactor(types): replace Any with proven types in 299 files

Clears 871 basedpyright Any errors (reportAny 6,703 to 6,240, reportExplicitAny 1,651 to 1,243) without adding a cast, an ignore or a suppression, and without touching any budget file

Most edits are annotation-only: a parameter, return or local goes from Any to object, Mapping[str, object] or the concrete type the value always held. Fourteen files validate untyped JSON once where it enters, through a module-level pydantic TypeAdapter or model_validate, and then use real types

No HTTP status, error type or response shape changes. Mistral speech and fal.ai Bria image generation now report a pydantic ValidationError instead of an AttributeError when the provider answers 2xx with a body that is not a JSON object

* refactor(types): make the config locals fix effective and trim no-op edits

The Mapping[str, object] annotation on `locals().copy()` removed no error,
because the checker narrows the variable back to the dict[str, Any] the call
returns. 21 provider config constructors now build the same copy with
dict(locals()), which the checker infers as object values under that
annotation, so each file loses one reportAny.

The same annotation is reverted in 31 other config files where it stayed a
no-op, together with the tests that were added only to cover those lines, and
the one MCP server manager line that no CI coverage shard executes is
reverted too. The pull request drops from 345 to 297 changed files.

* refactor(types): accept only int in the proxy state setter

get_proxy_state_variable is annotated to return int, but
set_proxy_state_variable still took Any, so the checker could not hold
callers to the type the getter promises. The setter now takes int, which is
what its only caller already passes.

* refactor(types): index the proxy state key so the getter returns int

* refactor(types): keep public annotations and provider error text unchanged

Restore every public return, public method parameter, public attribute and exported
alias to its annotation on main so code that type-checks against the package keeps
type-checking, and take the Mistral speech and fal.ai Bria changes back out so no
provider error message differs from main

* refactor(types): leave the Vertex RAG chunking read as it is on main

Take the chunking format validation back out of the Vertex RAG ingestion path. It needs the vertexai SDK, a storage bucket and a RAG corpus to execute, so nothing here could run it end to end, and it cleared only two errors

* test(integration): pin the validated provider boundaries on a live proxy

* test(integration): give the held burst a client that outlasts the gate

The fault cell holds a burst at the upstream for up to 60 seconds while it kills a worker, but sent the burst through the shared 15 second client, so a slow box could time the survivors out before the gate opened. The burst now goes through its own client whose timeout is twice the gate, and the gate length is one named constant.

* refactor(types): keep the license reply handling and experimental MCP signatures as they were

The license check validated the whole reply as a mapping, which changed the error text logged for a reply that is not an object. It now validates only the verify value, so every reply is handled and logged exactly as before while the value is still typed.

Three files under the experimental MCP server changed annotations on public functions and methods (three returns and three parameters). They go back to their previous content so no public signature in the diff is narrowed.

* test(integration): answer the proxy's model-list call in the OpenAI stand-ins

Every 300 seconds each proxy worker asks an OpenAI deployment for GET /v1/models. Four new cells own an OpenAI stand-in that accepted only the call under test, so a refresh landing inside a cell failed it. The stand-ins now answer that call through the suite's own helper and the cells count only the provider calls they drive.
This commit is contained in:
Mateo Wang 2026-10-06 15:31:54 +00:00 • committed by GitHub
parent 126e79c967
commit d4a791d650
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
191 changed files with 2852 additions and 290 deletions

View file

@ -239,7 +239,7 @@ def create_assistants(
temperature: float | None = None,
top_p: float | None = None,
response_format: str | dict[str, str] | None = None,
client: Any | None = None,
client: object | None = None,
api_key: str | None = None,
api_base: str | None = None,
api_version: str | None = None,
@ -410,7 +410,7 @@ async def adelete_assistant(
def delete_assistant(
custom_llm_provider: Literal["openai", "azure"],
assistant_id: str,
client: Any | None = None,
client: object | None = None,
api_key: str | None = None,
api_base: str | None = None,
api_version: str | None = None,
@ -1181,7 +1181,7 @@ async def arun_thread(
model: str | None = None,
stream: bool | None = None,
tools: Iterable[AssistantToolParam] | None = None,
client: Any | None = None,
client: object | None = None,
**kwargs,
) -> Run:
loop: Final = asyncio.get_event_loop()
@ -1246,7 +1246,7 @@ def run_thread(
model: str | None = None,
stream: bool | None = None,
tools: Iterable[AssistantToolParam] | None = None,
client: Any | None = None,
client: object | None = None,
event_handler: AssistantEventHandler | None = None, # for stream=True calls
**kwargs,
) -> Run:

View file

@ -239,7 +239,7 @@ class ValkeySemanticCache(RedisSemanticCache):
kwargs.setdefault("metadata", {})["semantic-similarity"] = similarity
@staticmethod
def _embedding_metadata(kwargs: dict[str, Any]) -> dict[str, Any] | None:
def _embedding_metadata(kwargs: dict[str, Any]) -> dict[str, object] | None:
"""The request metadata forwarded to the embedding call."""
return kwargs.get("metadata")

View file

@ -6,7 +6,7 @@ Computes cosine similarity between the query embedding and each message embeddin
import math
from collections.abc import Mapping
from typing import Any, Final
from typing import Final
from litellm.caching.dual_cache import DualCache
@ -75,7 +75,7 @@ def embedding_score_messages(
# Filter out empty texts — replace with a placeholder to maintain indexing
processed_texts: Final = [t if t.strip() else "empty" for t in texts]
kwargs: dict[str, Any] = {
kwargs: dict[str, object] = {
"model": model,
"input": processed_texts,
"caching": cache is not None,

View file

@ -13,6 +13,8 @@ from functools import partial
from pathlib import Path
from typing import Final, Literal
from pydantic import ConfigDict, TypeAdapter
import litellm
from litellm.constants import request_timeout as DEFAULT_REQUEST_TIMEOUT
from litellm.containers.utils import decode_managed_container_id_for_request
@ -33,13 +35,14 @@ RESPONSE_TYPES: Final[dict[str, type]] = {
"ContainerFileObject": ContainerFileObject,
"DeleteContainerFileResponse": DeleteContainerFileResponse,
}
_ENDPOINTS_DOCUMENT: Final = TypeAdapter(dict[str, object], config=ConfigDict(hide_input_in_errors=True))
def _load_endpoints_config() -> dict:
"""Load the endpoints configuration from JSON file."""
config_path: Final = Path(__file__).parent / "endpoints.json"
with open(config_path) as f:
return json.load(f)
return _ENDPOINTS_DOCUMENT.validate_python(json.load(f))
def create_sync_endpoint_function(endpoint_config: dict) -> Callable:

View file

@ -78,7 +78,7 @@ class BaseGoogleGenAIGenerateContentStreamingIterator:
self.endpoint_type: Final = (
EndpointType.GEMINI if custom_llm_provider == litellm.LlmProviders.GEMINI.value else EndpointType.VERTEX_AI
)
self._hidden_params: dict[str, Any] = hidden_params or {}
self._hidden_params: dict[str, object] = hidden_params or {}
async def _handle_async_streaming_logging(
self,

View file

@ -628,8 +628,8 @@ class ModelEndpoint:
def _sdk_kwargs(
self, body: Mapping[str, object]
) -> dict[str, Any]: # mutable-ok: SDK call kwargs, mutated by _invoke_sdk then splatted
kwargs: dict[str, Any] = {**body} # mutable-ok: SDK call kwargs built from the JSON body, then overridden
) -> dict[str, object]: # mutable-ok: SDK call kwargs, mutated by _invoke_sdk then splatted
kwargs: dict[str, object] = {**body} # mutable-ok: SDK call kwargs built from the JSON body, then overridden
if self.model:
kwargs["model"] = self.model
if self.api_key:

View file

@ -1807,7 +1807,7 @@ Model Info:
async def _run_scheduled_daily_report(
self,
llm_router: Any | None = None,
llm_router: object | None = None,
pod_lock_manager: "PodLockManager | None" = None,
):
"""

View file

@ -11,7 +11,7 @@ gzip JSONEachRow insert, either every `CLICKHOUSE_FLUSH_INTERVAL_SECONDS` or as
import asyncio
from collections.abc import Mapping, Sequence
from contextlib import suppress
from typing import Any, ClassVar, Final
from typing import ClassVar, Final
from litellm._logging import verbose_logger
from litellm.constants import (
@ -89,7 +89,7 @@ class ClickHouseBatchLogger(CustomBatchLogger):
async def async_send_batch(self) -> None:
await self.flush_queue()
async def _insert(self, batch: list[dict[str, Any]]) -> bool:
async def _insert(self, batch: list[dict[str, object]]) -> bool:
try:
await self.storage.insert_rows(self.table, batch)
self.rows_written += len(batch)

View file

@ -68,7 +68,7 @@ class CloudZeroStreamer:
def _group_by_date(self, data: pl.DataFrame) -> dict[str, pl.DataFrame]:
"""Group data by date, converting to UTC and validating dates."""
daily_batches: Final[dict[str, list[dict[str, Any]]]] = {}
daily_batches: Final[dict[str, list[dict[str, object]]]] = {}
# Ensure we have the required columns
if "time/usage_start" not in data.columns:
@ -209,7 +209,7 @@ class CloudZeroStreamer:
return payload
def _convert_cbf_to_api_format(self, row: dict[str, Any]) -> dict[str, Any] | None:
def _convert_cbf_to_api_format(self, row: dict[str, object]) -> dict[str, Any] | None:
"""Convert CBF row to CloudZero API format - keeping CBF field names as CloudZero expects them."""
try:
# CloudZero expects CBF format field names directly, not converted names

View file

@ -19,7 +19,7 @@
"""Database connection and data extraction for LiteLLM."""
from datetime import datetime
from typing import Any, Final
from typing import Final
import polars as pl
@ -80,7 +80,7 @@ class LiteLLMDatabase:
ORDER BY dus.date DESC, dus.created_at DESC
"""
params: Final[list[Any]] = [
params: Final[list[object]] = [
start_time_utc,
end_time_utc,
]

View file

@ -39,7 +39,7 @@ class FocusDestinationFactory:
def _resolve_config(
*,
provider: str,
overrides: dict[str, Any],
overrides: dict[str, object],
) -> dict[str, Any]:
if provider == "s3":
resolved = {

View file

@ -93,7 +93,7 @@ class GenericPromptManager(CustomPromptManagement):
headers["Authorization"] = f"Bearer {self.api_key}"
return headers
def _fetch_prompt_from_api(self, prompt_id: str | None, prompt_spec: PromptSpec | None) -> dict[str, Any]:
def _fetch_prompt_from_api(self, prompt_id: str | None, prompt_spec: PromptSpec | None) -> dict[str, object]:
"""
Fetch a prompt from the API.

View file

@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Any, Final
from typing import TYPE_CHECKING, Final
if TYPE_CHECKING:
from litellm.integrations.custom_prompt_management import CustomPromptManagement
@ -66,7 +66,7 @@ def _gitlab_prompt_initializer(
# You can store arbitrary integration-specific config on PromptLiteLLMParams.
# If your dataclass doesn't have these attributes, add them or put inside
# `litellm_params.extra` and pull them from there.
gitlab_config: Final[dict[str, Any]] = getattr(litellm_params, "gitlab_config", None) or {}
gitlab_config: Final[dict[str, object]] = getattr(litellm_params, "gitlab_config", None) or {}
git_ref: Final[str | None] = getattr(litellm_params, "git_ref", None)
if not gitlab_config:

View file

@ -81,7 +81,7 @@ class LangFuseHandler:
return globalLangfuseLogger
credentials_dict: dict[
str, Any
str, object
] = {} # the global langfuse logger uses Environment Variables, there are no dynamic credentials
globalLangfuseLogger = in_memory_dynamic_logger_cache.get_cache(
credentials=credentials_dict,

View file

@ -7,7 +7,7 @@ import time
from collections import OrderedDict
from collections.abc import Callable, Iterable, Mapping, Sequence
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, Final, Literal
from typing import TYPE_CHECKING, Final, Literal
from opentelemetry import _logs, baggage, metrics, trace
from opentelemetry._logs import Logger, LoggerProvider, NoOpLoggerProvider
@ -1072,7 +1072,7 @@ def build_metric_reader(config: OpenTelemetryV2Config) -> "MetricReader":
)
tls: Final = resolve_otlp_http_tls("METRICS")
exporter: Any = HTTPMetricExporter(
exporter: object = HTTPMetricExporter(
endpoint=_otlp_metrics_endpoint(config.endpoint),
headers=parse_headers(config.headers),
certificate_file=tls.certificate_file,

View file

@ -10,7 +10,7 @@ worker thread, off any event loop — and caches it for the process lifetime.
"""
from collections.abc import Sequence
from typing import Any, Final
from typing import Final
import httpx
from opentelemetry.sdk.trace import ReadableSpan
@ -117,7 +117,7 @@ def _build_agentops_exporter(spec: ExporterSpec) -> SpanExporter:
return _LazyAuthAgentOpsExporter(endpoint=spec.endpoint, api_key=options.get("api_key"))
def _fetch_agentops_jwt(api_key: str) -> dict[str, Any]:
def _fetch_agentops_jwt(api_key: str) -> dict[str, object]:
# Own a short-lived client rather than ``_get_httpx_client()``: that returns
# a process-wide cached ``HTTPHandler`` whose connection pool is shared by
# every caller, so closing it here would break concurrent/subsequent

View file

@ -6,7 +6,7 @@ Helpers for the Prometheus integration (extracted to keep ``prometheus.py`` smal
from __future__ import annotations
from typing import Any, Final, cast
from typing import Final, cast
from litellm.types.integrations.prometheus import (
UserAPIKeyLabelValues,
@ -66,7 +66,7 @@ class PrometheusLabelFactoryContext:
for k, v in get_custom_labels_from_tags(enum_values.tags).items():
self._tag_labels[k] = _sanitize_prometheus_label_value(v)
# Use a dedicated sentinel so `None` can be cached as a computed result.
self._resolved_end_user: Any = self._END_USER_NOT_COMPUTED
self._resolved_end_user: object = self._END_USER_NOT_COMPUTED
def get_resolved_end_user(self) -> str | None:
if self._resolved_end_user is self._END_USER_NOT_COMPUTED:

View file

@ -8,7 +8,7 @@ This module handles transforming between:
from collections.abc import Mapping, Sequence
from types import MappingProxyType
from typing import Any, Final, cast
from typing import Final, cast
from pydantic import BaseModel
@ -251,7 +251,7 @@ class LiteLLMResponsesInteractionsConfig:
# Build interactions response — populate both `outputs` (legacy schema) and
# `steps` (new schema) so callers work regardless of which schema they expect.
interactions_response_dict: Final[dict[str, Any]] = {
interactions_response_dict: Final[dict[str, object]] = {
"id": getattr(responses_response, "id", ""),
"object": "interaction",
"status": interactions_status,
@ -274,4 +274,4 @@ class LiteLLMResponsesInteractionsConfig:
# Add updated (same as created for now)
interactions_response_dict["updated"] = created
return InteractionsAPIResponse(**interactions_response_dict)
return InteractionsAPIResponse.model_validate(interactions_response_dict)

View file

@ -116,7 +116,7 @@ def encode_s3_object_key_for_url(object_key: str) -> str:
def should_allow_legacy_cloud_file_ids(
litellm_params: Mapping[str, Any] | None = None,
litellm_params: Mapping[str, object] | None = None,
) -> bool:
value = None
if isinstance(litellm_params, Mapping):

View file

@ -725,7 +725,7 @@ def redact_nested_match_and_regex_keys(
if payload is None or isinstance(payload, str):
return payload
try:
redacted: Final[dict | list[Any] | str | None] = copy.deepcopy(payload)
redacted: Final[dict | list[object] | str | None] = copy.deepcopy(payload)
except Exception:
return payload

View file

@ -26,7 +26,7 @@ from types import MappingProxyType
from typing import Final, Protocol
import httpx
from pydantic import TypeAdapter
from pydantic import ConfigDict, TypeAdapter
from typing_extensions import ReadOnly, TypedDict
from litellm import verbose_logger
@ -40,6 +40,7 @@ from litellm.litellm_core_utils.fallback_generalizations import (
FALLBACK_GENERALIZATIONS_KEY: Final = "fallback_generalizations"
_CATALOG_ADAPTER: Final = TypeAdapter(dict[str, dict[str, object]])
_BUNDLED_CATALOG_ADAPTER: Final = TypeAdapter(dict[str, object], config=ConfigDict(hide_input_in_errors=True))
_CLI_ENTRYPOINT_NAMES: Final = frozenset({"lite", "litellm-proxy"})
@ -83,7 +84,7 @@ class GetModelCostMap:
@staticmethod
def load_local_model_cost_map_with_revision() -> "ModelCostMapReloaded":
body: Final = GetModelCostMap.read_local_model_cost_map_bytes()
content: Final = json.loads(body)
content: Final = _BUNDLED_CATALOG_ADAPTER.validate_python(json.loads(body))
return ModelCostMapReloaded(model_cost_map=content, revision=git_blob_id(body))
@staticmethod
@ -230,7 +231,7 @@ class _FetchAttemptRetryable:
def _parse_retry_after_seconds(response: httpx.Response) -> float | None:
header: Final = response.headers.get("Retry-After")
header: Final[str | None] = response.headers.get("Retry-After")
if header is None:
return None
try:

View file

@ -148,7 +148,7 @@ _trusted_overlay_callback_params: Final = frozenset(
)
def get_trusted_callback_params(kwargs: Mapping[str, Any] | None) -> tuple[tuple[str, str], ...]:
def get_trusted_callback_params(kwargs: Mapping[str, object] | None) -> tuple[tuple[str, str], ...]:
"""
Read callback params the proxy itself stamped from admin-configured team/key callback settings.

View file

@ -50,7 +50,7 @@ _DATA_URI_RE: Final = re.compile(r"data:([^;]+);base64,([A-Za-z0-9+/=]+)")
_MAX_TRUNCATION_DEPTH: Final = 20
def _base64_data_uri_replacer(match: re.Match) -> str:
def _base64_data_uri_replacer(match: re.Match[str]) -> str:
"""Replace a single base64 data-URI match with a size placeholder if too long."""
mime_type: Final = match.group(1)
payload: Final = match.group(2)

View file

@ -377,7 +377,7 @@ def should_redact_message_logging(model_call_details: dict) -> bool:
return litellm.turn_off_message_logging is True
def redact_message_input_output_from_logging(model_call_details: dict, result, input: Any | None = None) -> Any:
def redact_message_input_output_from_logging(model_call_details: dict, result, input: object | None = None) -> Any:
"""
Removes messages, prompts, input, response from logging. This modifies the data in-place
only redacts when litellm.turn_off_message_logging == True

View file

@ -6,7 +6,7 @@ import json
from typing import Any
def safe_json_loads(data: str, default: Any = None) -> Any:
def safe_json_loads(data: str, default: object = None) -> Any:
"""
Safely parse a JSON string. If parsing fails, return the default value (None by default).
"""

View file

@ -124,7 +124,7 @@ class SensitiveDataMasker:
if depth >= max_depth:
return data
masked_data: Final[dict[str, Any]] = {}
masked_data: Final[dict[str, object]] = {}
for k, v in data.items():
try:
key_is_sensitive = self.is_sensitive_key(k, excluded_keys)

View file

@ -1,4 +1,4 @@
from typing import Any, Final
from typing import Final
import litellm
from litellm.litellm_core_utils.llm_cost_calc.utils import resolve_image_model_info
@ -7,7 +7,7 @@ from litellm.types.utils import ImageResponse, ModelInfo
def cost_calculator(
model: str,
image_response: Any,
image_response: object,
model_info: ModelInfo | None = None,
) -> float:
"""

View file

@ -316,7 +316,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
metadata: dict | None = None,
system: str | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -1645,7 +1645,7 @@ def strip_thinking_blocks_from_anthropic_messages_request_dict(
def strip_empty_content_blocks_from_anthropic_messages(
messages: list[Any],
messages: Sequence[object],
) -> list[Any]:
"""
Return a new message list with empty or whitespace-only ``{"type": "text"}``
@ -1760,7 +1760,7 @@ def _sanitize_tool_use_id_content_block(block: object) -> object:
return block
def sanitize_tool_use_ids_in_anthropic_messages(messages: list[Any]) -> list[Any]:
def sanitize_tool_use_ids_in_anthropic_messages(messages: Sequence[object]) -> list[Any]:
"""
Return a new message list with ``tool_use`` / ``server_tool_use`` ``id`` and
``tool_result`` ``tool_use_id`` values rewritten to satisfy Anthropic's
@ -1934,7 +1934,7 @@ def _flatten_web_search_results_in_message(message: object) -> object:
def flatten_unencrypted_web_search_results_in_anthropic_messages(
messages: list[Any],
messages: Sequence[object],
) -> list[Any]:
"""
Return a new message list with replayed ``web_search_tool_result`` blocks that

View file

@ -6,7 +6,7 @@ Litellm provider slug: `anthropic_text/<model_name>`
import json
import time
from collections.abc import AsyncIterator, Iterator
from collections.abc import AsyncIterator, Iterator, Mapping
from typing import TYPE_CHECKING, Final
import httpx
@ -73,7 +73,7 @@ class AnthropicTextConfig(BaseConfig):
top_k: int | None = None,
metadata: dict | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -962,7 +962,7 @@ class LiteLLMAnthropicMessagesAdapter:
)
elif isinstance(system_content, list):
# Convert Anthropic system content blocks to OpenAI format
openai_system_content: Final[list[dict[str, Any]]] = []
openai_system_content: Final[list[dict[str, object]]] = []
model_name: Final = anthropic_message_request.get("model", "")
for block in system_content:
if isinstance(block, dict) and block.get("type") == "text":

View file

@ -361,7 +361,7 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig):
self,
headers: dict, # mutable-ok: out-param
optional_params: dict, # mutable-ok: out-param
messages: list[Any], # mutable-ok: mirrors the validate_anthropic_messages_environment contract
messages: list[object], # mutable-ok: mirrors the validate_anthropic_messages_environment contract
) -> dict: # mutable-ok: out-param
if "anthropic-version" not in headers:
headers["anthropic-version"] = DEFAULT_ANTHROPIC_API_VERSION

View file

@ -5,7 +5,7 @@ Used when the target model is an OpenAI or Azure model.
"""
from collections.abc import AsyncIterator, Coroutine, Mapping
from typing import Any, Final, TypeAlias
from typing import Final, TypeAlias
import litellm
from litellm.types.llms.anthropic import (
@ -63,7 +63,7 @@ def _build_responses_kwargs(
top_p: float | None = None,
output_format: AnthropicOutputSchema | None = None,
extra_kwargs: Mapping[str, object] | None = None,
) -> dict[str, Any]:
) -> dict[str, object]:
"""
Build the kwargs dict to pass directly to litellm.responses() / litellm.aresponses().
"""

View file

@ -257,7 +257,7 @@ class LiteLLMAnthropicToResponsesAPIAdapter:
)
elif isinstance(content, list):
user_parts: list[Mapping[str, object]] = []
tool_image_parts: list[dict[str, Any]] = [] # mutable-ok: json content parts
tool_image_parts: list[dict[str, object]] = [] # mutable-ok: json content parts
for block in content:
if not isinstance(block, dict):
continue
@ -367,7 +367,7 @@ class LiteLLMAnthropicToResponsesAPIAdapter:
for _, group in groupby(enumerate(blocks), key=self._assistant_block_group_key)
for item in self._assistant_group_to_input_items(tuple(block for _, block in group))
)
asst_parts: list[dict[str, Any]] = [ # mutable-ok: API message payload
asst_parts: list[dict[str, object]] = [ # mutable-ok: API message payload
{"type": "output_text", "text": block.get("text", "")}
for block in blocks
if block.get("type") == "text"
@ -533,7 +533,7 @@ class LiteLLMAnthropicToResponsesAPIAdapter:
},
)
responses_kwargs: Final[dict[str, Any]] = {
responses_kwargs: Final[dict[str, object]] = {
"model": model,
"input": input_items,
}

View file

@ -93,7 +93,7 @@ class AzureOpenAIConfig(BaseConfig):
temperature: int | None = None,
top_p: int | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -5,7 +5,7 @@ import os
from collections.abc import Callable, Mapping
from functools import lru_cache
from types import MappingProxyType
from typing import Any, Final, Literal, NamedTuple, cast
from typing import Final, Literal, NamedTuple, cast
import httpx
from openai import AsyncAzureOpenAI, AsyncOpenAI, AzureOpenAI, OpenAI
@ -549,7 +549,7 @@ class BaseAzureLLM(BaseOpenAILLM):
# on every request (via `_refresh_api_key`), so passing
# `azure_ad_token_provider` directly preserves Azure AD token refresh
# behavior that the regular AzureOpenAI client provides.
v1_api_key: str | Callable[[], Any] | None = (
v1_api_key: str | Callable[[], object] | None = (
azure_client_params.get("api_key")
or azure_client_params.get("azure_ad_token_provider")
or azure_client_params.get("azure_ad_token")

View file

@ -180,7 +180,7 @@ class AzureTextCompletion(BaseAzureLLM):
except Exception as e:
status_code: Final = getattr(e, "status_code", 500)
error_headers = getattr(e, "headers", None)
error_response: Final = getattr(e, "response", None)
error_response: Final[object] = getattr(e, "response", None)
if error_headers is None and error_response:
error_headers = getattr(error_response, "headers", None)
raise AzureOpenAIError(status_code=status_code, message=str(e), headers=error_headers)
@ -241,7 +241,7 @@ class AzureTextCompletion(BaseAzureLLM):
except Exception as e:
status_code: Final = getattr(e, "status_code", 500)
error_headers = getattr(e, "headers", None)
error_response: Final = getattr(e, "response", None)
error_response: Final[object] = getattr(e, "response", None)
if error_headers is None and error_response:
error_headers = getattr(error_response, "headers", None)
raise AzureOpenAIError(status_code=status_code, message=str(e), headers=error_headers)
@ -352,7 +352,7 @@ class AzureTextCompletion(BaseAzureLLM):
except Exception as e:
status_code: Final = getattr(e, "status_code", 500)
error_headers = getattr(e, "headers", None)
error_response: Final = getattr(e, "response", None)
error_response: Final[object] = getattr(e, "response", None)
if error_headers is None and error_response:
error_headers = getattr(error_response, "headers", None)
raise AzureOpenAIError(status_code=status_code, message=str(e), headers=error_headers)

View file

@ -27,7 +27,7 @@ class AzureOpenAIExceptionMapping:
# Keep the OpenAI-style body fields populated so downstream (proxy + SDK)
# can surface `type` / `code` correctly.
openai_style_body: Final[dict[str, Any]] = {
openai_style_body: Final[dict[str, object]] = {
"message": provider_message,
"type": provider_type or "invalid_request_error",
"code": provider_code or "content_policy_violation",
@ -54,7 +54,7 @@ class AzureOpenAIExceptionMapping:
@staticmethod
def _extract_azure_error(
original_exception: Exception,
) -> tuple[dict[str, Any], dict | None]:
) -> tuple[dict[str, object], dict | None]:
"""Extract Azure OpenAI error payload and inner error details.
Azure error formats can vary by endpoint/version. Common shapes:

View file

@ -216,7 +216,7 @@ class AzureAIAgentsConfig(BaseConfig):
converted_messages.append({"role": role, "content": content})
payload: Final[dict[str, Any]] = {
payload: Final[dict[str, object]] = {
"agent_id": agent_id,
"messages": converted_messages,
"api_version": self._get_api_version(optional_params),

View file

@ -196,7 +196,7 @@ class AzureAnthropicChatCompletion(AnthropicChatCompletion):
status_code: Final = getattr(e, "status_code", 500)
error_headers = getattr(e, "headers", None)
error_text = getattr(e, "text", str(e))
error_response: Final = getattr(e, "response", None)
error_response: Final[object] = getattr(e, "response", None)
if error_headers is None and error_response:
error_headers = getattr(error_response, "headers", None)
if error_response and hasattr(error_response, "text"):

View file

@ -1,5 +1,5 @@
from collections.abc import Mapping
from typing import Any, Final
from typing import Final
import litellm
from litellm.litellm_core_utils.llm_cost_calc.utils import (
@ -23,7 +23,7 @@ def _input_cost_per_pixel(resolved: ModelInfo) -> float:
def cost_calculator(
model: str,
image_response: Any,
image_response: object,
size: str | None = None,
n: int | None = None,
optional_params: Mapping[str, object] | None = None,

View file

@ -192,7 +192,7 @@ def blocked_responses_stream_usage(original_response: object) -> ResponseAPIUsag
def effective_skip_system_message_for_guardrail(guardrail_to_apply: object) -> bool:
per: Final = getattr(guardrail_to_apply, "skip_system_message_in_guardrail", None)
per: Final[object] = getattr(guardrail_to_apply, "skip_system_message_in_guardrail", None)
if per is not None:
return bool(per)
import litellm
@ -201,7 +201,7 @@ def effective_skip_system_message_for_guardrail(guardrail_to_apply: object) -> b
def effective_skip_tool_message_for_guardrail(guardrail_to_apply: object) -> bool:
per: Final = getattr(guardrail_to_apply, "skip_tool_message_in_guardrail", None)
per: Final[object] = getattr(guardrail_to_apply, "skip_tool_message_in_guardrail", None)
if per is not None:
return bool(per)
import litellm

View file

@ -357,7 +357,7 @@ class BaseResponsesAPIConfig(ABC):
"""
if not isinstance(input, list):
return input
out: Final[list[Any]] = []
out: Final[list[object]] = []
for item in input:
if isinstance(item, dict) and item.get("type") == "custom_tool_call":
out.append({k: v for k, v in item.items() if k != "namespace"})

View file

@ -283,7 +283,7 @@ class BaseAWSLLM(SignsRequestsWithAWS):
self,
credential_args: Mapping[str, str | bool | tuple[AwsSessionTag, ...] | None],
credential_fetcher: Callable[[], tuple[Credentials, int | None]],
) -> Any:
) -> Credentials:
"""
Read-through IAM cache on the process-wide ``DualCache``.

View file

@ -164,7 +164,7 @@ class AmazonConverseConfig(BaseConfig):
topP: int | None = None,
topK: int | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -1,4 +1,5 @@
import types
from collections.abc import Mapping
from typing import Final
from litellm.llms.base_llm.chat.transformation import BaseConfig
@ -46,7 +47,7 @@ class AmazonAI21Config(AmazonInvokeConfig, BaseConfig):
presencePenalty: dict | None = None,
countPenalty: dict | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -1,4 +1,5 @@
import types
from collections.abc import Mapping
from typing import Final
from litellm.llms.bedrock.chat.invoke_transformations.base_invoke_transformation import (
@ -27,7 +28,7 @@ class AmazonCohereConfig(AmazonInvokeConfig, CohereChatConfig):
temperature: float | None = None,
return_likelihood: str | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -1,4 +1,5 @@
import types
from collections.abc import Mapping
from typing import Final
from litellm.llms.base_llm.chat.transformation import BaseConfig
@ -28,7 +29,7 @@ class AmazonLlamaConfig(AmazonInvokeConfig, BaseConfig):
temperature: float | None = None,
topP: int | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -1,4 +1,5 @@
import types
from collections.abc import Mapping
from typing import TYPE_CHECKING, Final
from litellm.llms.base_llm.chat.transformation import BaseConfig
@ -37,7 +38,7 @@ class AmazonMistralConfig(AmazonInvokeConfig, BaseConfig):
top_k: float | None = None,
stop: list[str] | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -6,6 +6,7 @@ Inherits from `AmazonInvokeConfig`
Qwen3 + Invoke API Tutorial: https://docs.aws.amazon.com/bedrock/latest/userguide/invoke-imported-model.html
"""
from collections.abc import Mapping
from typing import TYPE_CHECKING, Final
import httpx
@ -43,7 +44,7 @@ class AmazonQwen3Config(AmazonInvokeConfig, BaseConfig):
top_k: int | None = None,
stop: list[str] | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -1,5 +1,6 @@
import re
import types
from collections.abc import Mapping
from typing import Final
import litellm
@ -33,7 +34,7 @@ class AmazonTitanConfig(AmazonInvokeConfig, BaseConfig):
temperature: float | None = None,
topP: int | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -1,4 +1,5 @@
import types
from collections.abc import Mapping
from typing import Final
import litellm
@ -36,7 +37,7 @@ class AmazonAnthropicConfig(AmazonInvokeConfig):
top_p: int | None = None,
anthropic_version: str | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -22,7 +22,7 @@ if TYPE_CHECKING:
from litellm.types.llms.bedrock import BedrockCreateBatchRequest
import httpx
from pydantic import TypeAdapter, ValidationError
from pydantic import ConfigDict, TypeAdapter, ValidationError
import litellm
from litellm import verbose_logger
@ -1691,6 +1691,9 @@ def get_bedrock_chat_config(model: str):
return litellm.AmazonInvokeConfig()
_BOTOCORE_SERVICE_DESCRIPTION: Final = TypeAdapter(Mapping[str, object], config=ConfigDict(hide_input_in_errors=True))
def _load_bedrock_response_stream_shape():
"""
Load the ResponseStream shape from botocore's bundled bedrock-runtime schema.
@ -1703,7 +1706,9 @@ def _load_bedrock_response_stream_shape():
from botocore.model import ServiceModel
loader: Final = Loader()
service_dict: Final = loader.load_service_model("bedrock-runtime", "service-2")
service_dict: Final = _BOTOCORE_SERVICE_DESCRIPTION.validate_python(
loader.load_service_model("bedrock-runtime", "service-2")
)
return ServiceModel(service_dict).shape_for("ResponseStream")
except Exception as e:
verbose_logger.warning(
@ -1856,9 +1861,12 @@ class BedrockEventStreamDecoderBase:
return chunk.decode()
_JSON_VALUE: Final = TypeAdapter(object)
def _decoded_json_value(raw: str) -> object:
"""Decode a JSON document into an opaque value for isinstance narrowing."""
return json.loads(raw)
return _JSON_VALUE.validate_python(json.loads(raw))
def get_anthropic_beta_from_headers(headers: dict) -> list[str]:

View file

@ -10,6 +10,7 @@ Docs - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-tit
"""
import types
from collections.abc import Mapping
from typing import Final
from litellm.types.llms.bedrock import (
@ -27,7 +28,7 @@ class AmazonTitanG1Config:
def __init__(
self,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -10,6 +10,7 @@ Docs - https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-tit
"""
import types
from collections.abc import Mapping
from typing import Final
from litellm.types.llms.bedrock import (
@ -31,7 +32,7 @@ class AmazonTitanV2Config:
dimensions: int | None = None
def __init__(self, normalize: bool | None = None, dimensions: int | None = None) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -855,8 +855,8 @@ class AmazonAnthropicClaudeMessagesConfig(
@staticmethod
def _merge_message_start_cache_into_delta_usage(
delta_usage: dict[str, Any],
start_usage: dict[str, Any] | None,
delta_usage: dict[str, object],
start_usage: Mapping[str, object] | None,
) -> None:
"""
Copy cache breakdown from message_start onto message_delta usage when
@ -885,7 +885,7 @@ class AmazonAnthropicClaudeMessagesConfig(
"""
_CACHE_FIELDS: Final = ("cache_creation_input_tokens", "cache_read_input_tokens")
pending_delta: dict[str, Any] | None = None
start_usage_snapshot: dict[str, Any] | None = None
start_usage_snapshot: Mapping[str, object] | None = None
async for chunk in completion_stream:
if not isinstance(chunk, dict):
@ -898,7 +898,7 @@ class AmazonAnthropicClaudeMessagesConfig(
chunk_type = chunk.get("type")
if chunk_type == "message_start":
msg: dict[str, Any] = cast(dict[str, Any], chunk.get("message") or {})
msg: dict[str, object] = cast(dict[str, Any], chunk.get("message") or {})
u = msg.get("usage")
if isinstance(u, dict):
start_usage_snapshot = dict(u)

View file

@ -77,7 +77,7 @@ class CodexStreamState:
started: set[str] = field(default_factory=set) # mutable-ok: parser records announced tool items
def _tool_input(item: Mapping[str, Any]) -> tuple[str, str, Mapping[str, Any], bool]:
def _tool_input(item: Mapping[str, Any]) -> tuple[str, str, Mapping[str, object], bool]:
"""(normalized name, native name, input, builtin) for a tool-like item."""
item_type = item.get("type")
if item_type == "command_execution":

View file

@ -40,7 +40,7 @@ class CometAPIConfig(OpenAIGPTConfig):
mapped_openai_params: Final = super().map_openai_params(non_default_params, optional_params, model, drop_params)
# CometAPI-specific parameters (if any)
extra_body: Final[dict[str, Any]] = {}
extra_body: Final[dict[str, object]] = {}
# TODO: Add CometAPI-specific parameter handling here
# Example:
# custom_param = non_default_params.pop("custom_param", None)

View file

@ -1,4 +1,4 @@
from typing import Any, Final
from typing import Final
import litellm
from litellm.litellm_core_utils.llm_cost_calc.utils import resolve_image_model_info
@ -7,7 +7,7 @@ from litellm.types.utils import ImageResponse, ModelInfo
def cost_calculator(
model: str,
image_response: Any,
image_response: object,
model_info: ModelInfo | None = None,
) -> float:
"""

View file

@ -11,6 +11,7 @@ from pathlib import Path
from typing import TYPE_CHECKING, Any, Final
import httpx
from pydantic import TypeAdapter
from typing_extensions import NotRequired, ReadOnly, TypedDict
import litellm
@ -53,6 +54,9 @@ class EndpointsConfig(TypedDict):
endpoints: ReadOnly[Sequence[EndpointConfig]]
_ENDPOINTS_CONFIG: Final = TypeAdapter(EndpointsConfig)
class ContainerErrorDetail(TypedDict, total=False):
"""The ``error`` object of a container API error body."""
@ -81,7 +85,7 @@ def _load_endpoints_config() -> EndpointsConfig:
"""Load the endpoints configuration from JSON file."""
config_path: Final = Path(__file__).parent.parent.parent / "containers" / "endpoints.json"
with open(config_path) as f:
return json.load(f)
return _ENDPOINTS_CONFIG.validate_python(json.load(f))
def _get_endpoint_config(endpoint_name: str) -> EndpointConfig | None:

View file

@ -3,7 +3,7 @@ Translates from OpenAI's `/v1/chat/completions` to DashScope's `/v1/chat/complet
"""
from collections.abc import Coroutine
from typing import Any, Final, Literal, overload
from typing import Final, Literal, overload
from litellm.secret_managers.main import get_secret_str
from litellm.types.llms.openai import AllMessageValues, ChatCompletionToolParam
@ -33,7 +33,7 @@ class DashScopeChatConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -45,7 +45,7 @@ class DashScopeChatConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
if is_async:
return super()._transform_messages(messages=messages, model=model, is_async=True)
else:

View file

@ -1,6 +1,6 @@
import json
from collections.abc import Coroutine
from typing import Any, Final, Literal, cast, overload
from collections.abc import Coroutine, Mapping
from typing import Final, Literal, cast, overload
import litellm
from litellm.constants import MIN_NON_ZERO_TEMPERATURE
@ -50,7 +50,7 @@ class DeepInfraConfig(OpenAIGPTConfig):
tools: list | None = None,
tool_choice: str | dict | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)
@ -154,7 +154,7 @@ class DeepInfraConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -166,7 +166,7 @@ class DeepInfraConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""
Transform messages for DeepInfra compatibility.
Handles both sync and async transformations.

View file

@ -3,7 +3,7 @@ Translates from OpenAI's `/v1/chat/completions` to DeepSeek's `/v1/chat/completi
"""
from collections.abc import Coroutine, Mapping, Sequence
from typing import Any, Final, Literal, cast, overload
from typing import Final, Literal, cast, overload
import litellm
from litellm.litellm_core_utils.prompt_templates.common_utils import (
@ -104,7 +104,7 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -116,7 +116,7 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""
DeepSeek vision models accept image_url content blocks in user
messages (https://api-docs.deepseek.com/guides/vision), so those

View file

@ -94,7 +94,7 @@ class DeepSeekAnthropicMessagesConfig(AnthropicMessagesConfig):
return f"{base_url}/v1/messages"
@staticmethod
def _sanitize_tools_for_deepseek(tools: Any) -> Any:
def _sanitize_tools_for_deepseek(tools: Any) -> object:
if not isinstance(tools, list):
return tools

View file

@ -5,7 +5,7 @@ Docker Model Runner API Reference: https://docs.docker.com/ai/model-runner/api-r
"""
from collections.abc import Coroutine
from typing import Any, Final, Literal, overload
from typing import Final, Literal, overload
from litellm.litellm_core_utils.prompt_templates.common_utils import (
handle_messages_with_content_list_to_str_conversion,
@ -27,7 +27,7 @@ class DockerModelRunnerChatConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -39,7 +39,7 @@ class DockerModelRunnerChatConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""
Docker Model Runner is OpenAI-compatible, so we use standard message transformation.
"""

View file

@ -60,7 +60,7 @@ class FalAIBytedanceBaseConfig(FalAIFluxProV11UltraConfig):
return optional_params
def _map_image_size(self, size: Any) -> Any:
def _map_image_size(self, size: Any) -> object:
if isinstance(size, dict):
return size

View file

@ -68,7 +68,7 @@ class FalAIFluxProV11Config(FalAIFluxProV11UltraConfig):
return optional_params
def _map_image_size(self, size: Any) -> Any:
def _map_image_size(self, size: Any) -> object:
if isinstance(size, dict):
return size
if not isinstance(size, str):

View file

@ -65,7 +65,7 @@ class FalAIFluxSchnellConfig(FalAIFluxProV11UltraConfig):
return optional_params
def _map_image_size(self, size: Any) -> Any:
def _map_image_size(self, size: Any) -> object:
if isinstance(size, dict):
return size

View file

@ -92,7 +92,7 @@ class FalAIIdeogramV3Config(FalAIBaseConfig):
return optional_params
def _map_image_size(self, size: Any) -> Any:
def _map_image_size(self, size: Any) -> object:
if isinstance(size, dict):
width = size.get("width")
height = size.get("height")

View file

@ -189,7 +189,7 @@ class FireworksAIConfig(FireworksAIMixin, OpenAIGPTConfig):
top_p=top_p,
response_format=response_format,
)
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -1,3 +1,4 @@
from collections.abc import Mapping
from typing import Final, cast
import litellm
@ -68,7 +69,7 @@ class GoogleAIStudioGeminiConfig(VertexGeminiConfig):
candidate_count: int | None = None,
stop_sequences: list | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -6,6 +6,7 @@ from collections.abc import Mapping, Sequence
from typing import Any, Final
import httpx
from pydantic import TypeAdapter
import litellm
from litellm.constants import DEFAULT_MAX_RECURSE_DEPTH
@ -128,9 +129,12 @@ def is_gemini_image_model(model: str) -> bool:
return "gemini" in base_model
_JSON_VALUE: Final = TypeAdapter(object)
def _parse_image_config_string(raw_image_config: str, model: str) -> object:
try:
return json.loads(raw_image_config)
return _JSON_VALUE.validate_python(json.loads(raw_image_config))
except json.JSONDecodeError as exc:
raise litellm.UnsupportedParamsError(
model=model,

View file

@ -13,7 +13,7 @@ else:
class GoogleAIStudioTokenCounter:
def _clean_contents_for_gemini_api(self, contents: Any) -> Any:
def _clean_contents_for_gemini_api(self, contents: Any) -> object:
"""
Clean up contents to remove unsupported fields for the Gemini API.

View file

@ -2,8 +2,6 @@
Gemini Image Edit Cost Calculator
"""
from typing import Any
from litellm.llms.gemini.image_generation.cost_calculator import (
cost_calculator as image_generation_cost_calculator,
)
@ -12,7 +10,7 @@ from litellm.types.utils import ModelInfo
def cost_calculator(
model: str,
image_response: Any,
image_response: object,
model_info: ModelInfo | None = None,
) -> float:
"""

View file

@ -2,7 +2,7 @@
Google AI Image Generation Cost Calculator
"""
from typing import Any, Final
from typing import Final
from litellm.litellm_core_utils.llm_cost_calc.utils import (
calculate_image_response_cost_from_usage,
@ -14,7 +14,7 @@ from litellm.types.utils import ImageResponse, ModelInfo
def cost_calculator(
model: str,
image_response: Any,
image_response: object,
model_info: ModelInfo | None = None,
) -> float:
"""

View file

@ -1,4 +1,4 @@
from typing import Any, Final
from typing import Final
from litellm.types.utils import ImageUsage, ImageUsageInputTokensDetails
@ -51,7 +51,7 @@ def transform_gemini_image_usage(usage_metadata: dict) -> ImageUsage:
if output_tokens > known_output_tokens:
output_tokens_details.text_tokens += output_tokens - known_output_tokens
usage_payload: Final[dict[str, Any]] = {
usage_payload: Final[dict[str, object]] = {
"input_tokens": usage_metadata.get("promptTokenCount", 0),
"input_tokens_details": input_tokens_details,
"output_tokens": output_tokens,
@ -62,4 +62,4 @@ def transform_gemini_image_usage(usage_metadata: dict) -> ImageUsage:
"completion_tokens_details": output_tokens_details.model_dump(),
"output_tokens_details": output_tokens_details.model_dump(),
}
return ImageUsage(**usage_payload)
return ImageUsage.model_validate(usage_payload)

View file

@ -1,6 +1,7 @@
import json
import os
from typing import TYPE_CHECKING, Any, Final
from collections.abc import Sequence
from typing import TYPE_CHECKING, Final
import httpx
@ -177,8 +178,8 @@ class GithubCopilotConfig(OpenAIConfig):
@staticmethod
def _parse_anthropic_native_content(
content_blocks: list[Any],
) -> tuple[str, list[ChatCompletionToolCallChunk], list[Any] | None]:
content_blocks: list[object],
) -> tuple[str, list[ChatCompletionToolCallChunk], Sequence[object] | None]:
"""
Parse Anthropic-native content blocks into OpenAI-compatible fields.
@ -225,7 +226,7 @@ class GithubCopilotConfig(OpenAIConfig):
content = ""
tool_calls: list[ChatCompletionToolCallChunk] = []
thinking_blocks: list[Any] | None = None
thinking_blocks: Sequence[object] | None = None
raw_content: Final = response_json.get("content")
if isinstance(raw_content, list):
content, tool_calls, thinking_blocks = cls._parse_anthropic_native_content(raw_content)

View file

@ -2,7 +2,7 @@
Translate from OpenAI's `/v1/chat/completions` to Groq's `/v1/chat/completions`
"""
from collections.abc import AsyncIterator, Coroutine, Iterator
from collections.abc import AsyncIterator, Coroutine, Iterator, Mapping
from typing import TYPE_CHECKING, Any, Final, Literal, cast, overload
import httpx
@ -72,7 +72,7 @@ class GroqChatConfig(OpenAILikeChatConfig):
tools: list | None = None,
tool_choice: str | dict | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)
@ -125,7 +125,7 @@ class GroqChatConfig(OpenAILikeChatConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -137,7 +137,7 @@ class GroqChatConfig(OpenAILikeChatConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
for idx, message in enumerate(messages):
"""
1. Don't pass 'null' function_call assistant message to groq - https://github.com/BerriAI/litellm/issues/5839

View file

@ -6,7 +6,7 @@ this is OpenAI compatible - no translation needed / occurs
import os
from collections.abc import Coroutine
from typing import Any, Literal, overload
from typing import Literal, overload
from litellm.litellm_core_utils.prompt_templates.common_utils import (
handle_messages_with_content_list_to_str_conversion,
@ -24,7 +24,7 @@ class HerokuChatConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -36,7 +36,7 @@ class HerokuChatConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""
Heroku does not support content in list format.
See: https://devcenter.heroku.com/articles/heroku-inference-api-v1-chat-completions#content-object

View file

@ -159,7 +159,7 @@ class LangFlowConfig(BaseConfig):
input_value: Final = self._get_last_user_message(messages)
payload: Final[dict[str, Any]] = {
payload: Final[dict[str, object]] = {
"input_value": input_value,
"input_type": optional_params.get("input_type", "chat"),
"output_type": optional_params.get("output_type", "chat"),

View file

@ -135,7 +135,7 @@ class LangGraphConfig(BaseConfig):
return parts[1]
return model
def _convert_messages_to_langgraph_format(self, messages: list[AllMessageValues]) -> list[dict[str, Any]]:
def _convert_messages_to_langgraph_format(self, messages: list[AllMessageValues]) -> list[dict[str, object]]:
"""
Convert OpenAI-format messages to LangGraph format.
@ -144,7 +144,7 @@ class LangGraphConfig(BaseConfig):
Preserves per-message ``metadata`` when present (e.g. A2A ``skillId``).
"""
langgraph_messages: Final[list[dict[str, Any]]] = []
langgraph_messages: Final[list[dict[str, object]]] = []
for msg in messages:
role = msg.get("role", "user")
content = msg.get("content", "")
@ -167,7 +167,7 @@ class LangGraphConfig(BaseConfig):
if not isinstance(content, str):
content = str(content)
langgraph_message: dict[str, Any] = {
langgraph_message: dict[str, object] = {
"role": langgraph_role,
"content": content,
}
@ -202,7 +202,7 @@ class LangGraphConfig(BaseConfig):
assistant_id: Final = self._get_assistant_id(model, optional_params)
langgraph_messages: Final = self._convert_messages_to_langgraph_format(messages)
payload: Final[dict[str, Any]] = {
payload: Final[dict[str, object]] = {
"assistant_id": assistant_id,
"input": {"messages": langgraph_messages},
}

View file

@ -289,7 +289,7 @@ class SkillPromptInjectionHandler:
if len(description) > max_desc_length:
description = description[: max_desc_length - 3] + "..."
input_schema: dict[str, Any] = {
input_schema: dict[str, object] = {
"type": "object",
"properties": {},
"required": [],

View file

@ -6,8 +6,8 @@ Why separate file? Make it easy to see how transformation works
Docs - https://docs.mistral.ai/api/
"""
from collections.abc import AsyncIterator, Coroutine, Iterator
from typing import TYPE_CHECKING, Any, Final, Literal, cast, get_type_hints, overload
from collections.abc import AsyncIterator, Coroutine, Iterator, Mapping
from typing import TYPE_CHECKING, Final, Literal, cast, get_type_hints, overload
import httpx
@ -99,7 +99,7 @@ class MistralConfig(OpenAIGPTConfig):
response_format: dict | None = None,
stop: str | list | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)
@ -229,7 +229,7 @@ class MistralConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]:
) -> Coroutine[object, object, list[AllMessageValues]]:
...
@overload
@ -244,7 +244,7 @@ class MistralConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""
- handles scenario where content is list and not string
- content list is just text, and no images

View file

@ -3,7 +3,7 @@ Translates from OpenAI's `/v1/chat/completions` to ModelScope's `/v1/chat/comple
"""
from collections.abc import Coroutine
from typing import Any, Final, Literal, cast, overload
from typing import Final, Literal, cast, overload
from typing_extensions import override
@ -27,7 +27,7 @@ class ModelScopeChatConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -39,7 +39,7 @@ class ModelScopeChatConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""
Flatten text-only content lists to strings for ModelScope.

View file

@ -3,7 +3,7 @@ Translates from OpenAI's `/v1/chat/completions` to Moonshot AI's `/v1/chat/compl
"""
from collections.abc import Coroutine, Mapping
from typing import Any, Final, Literal, cast, overload
from typing import Final, Literal, cast, overload
import litellm
from litellm.litellm_core_utils.prompt_templates.common_utils import (
@ -29,7 +29,7 @@ class MoonshotChatConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -41,7 +41,7 @@ class MoonshotChatConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""
Moonshot text-only models don't support content in list format.
Multimodal models (kimi-k2.5, kimi-latest, etc.) accept the

View file

@ -250,7 +250,7 @@ class NvidiaRivaAudioTranscription:
message="NvidiaRivaAudioTranscriptionConfig produced an unexpected request payload type.",
)
recognition_config_dict: Final[dict[str, Any]] = request_payload["recognition_config"]
recognition_config_dict: Final[dict[str, object]] = request_payload["recognition_config"]
# The wire format is fixed by our resampler; override anything stale
# the caller passed in so the gRPC config matches the bytes we send.
recognition_config_dict["sample_rate_hertz"] = RIVA_TARGET_SAMPLE_RATE_HZ

View file

@ -2,7 +2,7 @@
Common utilities and exceptions for the NVIDIA Riva STT provider
"""
from typing import Any, Final
from typing import Final
from litellm.llms.base_llm.chat.transformation import BaseLLMException
@ -41,7 +41,7 @@ _GRPC_STATUS_CODE_TO_HTTP: Final[dict] = {
}
def _extract_grpc_status_name(error: Any) -> str | None:
def _extract_grpc_status_name(error: object) -> str | None:
"""
Best-effort extraction of a gRPC StatusCode name from an arbitrary error.
@ -60,7 +60,7 @@ def _extract_grpc_status_name(error: Any) -> str | None:
return None
def _extract_grpc_details(error: Any) -> str | None:
def _extract_grpc_details(error: object) -> str | None:
"""Best-effort extraction of a human-readable detail string from a gRPC error."""
details_fn: Final = getattr(error, "details", None)
if callable(details_fn):

View file

@ -8,7 +8,7 @@ parsing, and streaming chunk parsing for models served with
import datetime
import hashlib
from typing import Any, Final
from typing import Final
import httpx
from pydantic import ValidationError
@ -404,7 +404,7 @@ def handle_generic_stream_chunk(dict_chunk: dict) -> ModelResponseStream:
# same minimal ``{"id", "type", "function": {"name", "arguments"}}``
# shape keeps downstream stream-mergers behaving identically across
# GENERIC and Cohere chunks.
tool_calls: list[dict[str, Any]] | None = None
tool_calls: list[dict[str, object]] | None = None
if typed_chunk.message and typed_chunk.message.toolCalls:
tool_calls = [
{

View file

@ -197,7 +197,7 @@ def _normalize_response_format(selected_params: dict, vendor: OCIVendors) -> Non
if vendor == OCIVendors.COHERE:
# OCI Cohere has no JSON_SCHEMA type; a schema rides on JSON_OBJECT.
payload: Final[dict[str, Any]] = {"type": "JSON_OBJECT"}
payload: Final[dict[str, object]] = {"type": "JSON_OBJECT"}
if json_schema is not None and json_schema.get("schema") is not None:
payload["schema"] = json_schema["schema"]
selected_params["responseFormat"] = payload
@ -212,7 +212,7 @@ def _normalize_response_format(selected_params: dict, vendor: OCIVendors) -> Non
# OCI's ResponseJsonSchema accepts only name/description/schema/isStrict.
# OpenAI sends `strict` instead of `isStrict`; forwarding it (or any
# other extra key) makes OCI reject the whole request with HTTP 400.
oci_schema: Final[dict[str, Any]] = {"name": json_schema.get("name") or "response"}
oci_schema: Final[dict[str, object]] = {"name": json_schema.get("name") or "response"}
if json_schema.get("description") is not None:
oci_schema["description"] = json_schema["description"]
if json_schema.get("schema") is not None:

View file

@ -87,7 +87,7 @@ async def ollama_aembeddings(
prompts: list[str],
model_response: EmbeddingResponse,
optional_params: dict,
logging_obj: Any,
logging_obj: object,
encoding: TokenEncoder | None,
):
if not api_base.endswith("/api/embed"):
@ -114,7 +114,7 @@ def ollama_embeddings(
prompts: list[str],
optional_params: dict,
model_response: EmbeddingResponse,
logging_obj: Any,
logging_obj: object,
encoding: TokenEncoder | None = None,
):
if not api_base.endswith("/api/embed"):

View file

@ -135,7 +135,7 @@ class OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig):
top_p: int | None = None,
response_format: dict | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -235,7 +235,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
def _not_run_reason(
self,
messages: Sequence[dict[str, Any]], # mutable-ok: raw request messages consumed by _extract_inputs
messages: Sequence[Mapping[str, object]],
) -> str | None:
"""Why nothing was scanned, or None when the only unscoped content is images, which this handler never scans."""
texts: Final[list[str]] = [] # mutable-ok: filled by _extract_inputs

View file

@ -12,7 +12,7 @@ Translations handled by LiteLLM:
"""
from collections.abc import Coroutine
from typing import Any, Final, Literal, cast, overload
from typing import Final, Literal, cast, overload
import litellm
from litellm import verbose_logger
@ -126,7 +126,7 @@ class OpenAIOSeriesConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -138,7 +138,7 @@ class OpenAIOSeriesConfig(OpenAIGPTConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""
Handles limitations of O-1 model family.
- modalities: image => drop param (if user opts in to dropping param)

View file

@ -157,7 +157,7 @@ class OpenAITextCompletion(BaseLLM):
status_code: Final = getattr(e, "status_code", 500)
error_headers = getattr(e, "headers", None)
error_text: Final = getattr(e, "text", str(e))
error_response: Final = getattr(e, "response", None)
error_response: Final[object] = getattr(e, "response", None)
if error_headers is None and error_response:
error_headers = getattr(error_response, "headers", None)
raise OpenAIError(status_code=status_code, message=error_text, headers=error_headers)
@ -210,7 +210,7 @@ class OpenAITextCompletion(BaseLLM):
status_code: Final = getattr(e, "status_code", 500)
error_headers = getattr(e, "headers", None)
error_text: Final = getattr(e, "text", str(e))
error_response: Final = getattr(e, "response", None)
error_response: Final[object] = getattr(e, "response", None)
if error_headers is None and error_response:
error_headers = getattr(error_response, "headers", None)
raise OpenAIError(status_code=status_code, message=error_text, headers=error_headers)
@ -248,7 +248,7 @@ class OpenAITextCompletion(BaseLLM):
status_code = getattr(e, "status_code", 500)
error_headers = getattr(e, "headers", None)
error_text = getattr(e, "text", str(e))
error_response = getattr(e, "response", None)
error_response: object = getattr(e, "response", None)
if error_headers is None and error_response:
error_headers = getattr(error_response, "headers", None)
raise OpenAIError(status_code=status_code, message=error_text, headers=error_headers)
@ -315,7 +315,7 @@ class OpenAITextCompletion(BaseLLM):
status_code: Final = getattr(e, "status_code", 500)
error_headers = getattr(e, "headers", None)
error_text: Final = getattr(e, "text", str(e))
error_response: Final = getattr(e, "response", None)
error_response: Final[object] = getattr(e, "response", None)
if error_headers is None and error_response:
error_headers = getattr(error_response, "headers", None)
raise OpenAIError(status_code=status_code, message=error_text, headers=error_headers)

View file

@ -2,6 +2,7 @@
Support for gpt model family
"""
from collections.abc import Mapping
from typing import Final
from litellm.llms.base_llm.completion.transformation import BaseTextCompletionConfig
@ -69,7 +70,7 @@ class OpenAITextCompletionConfig(BaseTextCompletionConfig, OpenAIGPTConfig):
temperature: float | None = None,
top_p: float | None = None,
) -> None:
locals_: Final = locals().copy()
locals_: Final[Mapping[str, object]] = dict(locals())
for key, value in locals_.items():
if key != "self" and value is not None:
setattr(self.__class__, key, value)

View file

@ -549,7 +549,7 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
)
# Create ResponseReasoningItem object from the item data
reasoning_item: Final = ResponseReasoningItem(**item_data)
reasoning_item: Final = ResponseReasoningItem.model_validate(item_data)
# Convert back to dict with exclude_none=True to exclude None fields
dict_reasoning_item: Final = reasoning_item.model_dump(exclude_none=True)

View file

@ -3,7 +3,7 @@ Dynamic configuration class generator for JSON-based providers.
"""
from collections.abc import Coroutine
from typing import TYPE_CHECKING, Any, Final, Literal, overload
from typing import TYPE_CHECKING, Final, Literal, overload
from litellm._logging import verbose_logger
from litellm.litellm_core_utils.prompt_templates.common_utils import (
@ -30,7 +30,7 @@ def create_config_class(provider: SimpleProviderConfig):
@overload
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: Literal[True]
) -> Coroutine[Any, Any, list[AllMessageValues]]: ...
) -> Coroutine[object, object, list[AllMessageValues]]: ...
@overload
def _transform_messages(
@ -42,7 +42,7 @@ def create_config_class(provider: SimpleProviderConfig):
def _transform_messages(
self, messages: list[AllMessageValues], model: str, is_async: bool = False
) -> list[AllMessageValues] | Coroutine[Any, Any, list[AllMessageValues]]:
) -> list[AllMessageValues] | Coroutine[object, object, list[AllMessageValues]]:
"""Transform messages based on special_handling config"""
# Handle content list to string conversion if configured

View file

@ -252,7 +252,7 @@ def build_opencode_config(
extra_skills: Final = (skills_path,) if skills_path else ()
skill_paths: Final = [*(user_skills.get("paths") or ()), *extra_skills] # mutable-ok: opencode config JSON
options: Final = {"baseURL": base_url, "apiKey": "{file:" + token_path + "}"} # mutable-ok: opencode config JSON
models: Final[dict[str, Any]] = {model: {}} # mutable-ok: opencode config JSON
models: Final[dict[str, Mapping[str, object]]] = {model: {}} # mutable-ok: opencode config JSON
provider: Final = { # mutable-ok: opencode config JSON
"npm": OPENCODE_PROVIDER_NPM,
"name": "LiteLLM",

View file

@ -1,4 +1,4 @@
from typing import Any, Final
from typing import Final
import litellm
from litellm.litellm_core_utils.llm_cost_calc.utils import resolve_image_model_info
@ -7,7 +7,7 @@ from litellm.types.utils import ImageResponse, ModelInfo
def cost_calculator(
model: str,
image_response: Any,
image_response: object,
model_info: ModelInfo | None = None,
) -> float:
"""

View file

@ -1,4 +1,4 @@
from typing import Any, Final
from typing import Final
import litellm
from litellm.litellm_core_utils.llm_cost_calc.utils import resolve_image_model_info
@ -7,7 +7,7 @@ from litellm.types.utils import ImageResponse, ModelInfo
def cost_calculator(
model: str,
image_response: Any,
image_response: object,
model_info: ModelInfo | None = None,
) -> float:
"""

Some files were not shown because too many files have changed in this diff Show more