mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-24 00:52:24 +00:00
Merge remote-tracking branch 'origin/main' into litellm_migrate_tests_p11
This commit is contained in:
commit
1ca0a662f0
234 changed files with 330 additions and 986 deletions
|
|
@ -85,7 +85,7 @@ def _filter_reserved_headers(
|
|||
|
||||
|
||||
def _request_scoped_runtime_session_id(
|
||||
params: Mapping[str, Any],
|
||||
params: Mapping[str, object],
|
||||
litellm_params: Mapping[str, Any],
|
||||
) -> str | None:
|
||||
context_id: Final = get_session_id_from_a2a_params(params)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class WatsonxOrchestrateA2AConfig(BaseA2AProviderConfig):
|
|||
params: dict[str, Any],
|
||||
api_base: str | None = None,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any]:
|
||||
) -> dict[str, object]:
|
||||
"""Handle a non-streaming A2A request via WXO runs API."""
|
||||
litellm_params: Final = kwargs.get("litellm_params")
|
||||
if not litellm_params:
|
||||
|
|
@ -40,7 +40,7 @@ class WatsonxOrchestrateA2AConfig(BaseA2AProviderConfig):
|
|||
params: dict[str, Any],
|
||||
api_base: str | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterator[dict[str, Any]]:
|
||||
) -> AsyncIterator[dict[str, object]]:
|
||||
"""Handle a streaming A2A request via WXO streaming runs API."""
|
||||
litellm_params: Final = kwargs.get("litellm_params")
|
||||
if not litellm_params:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class A2ARequestUtils:
|
|||
"""Utility class for A2A request/response processing."""
|
||||
|
||||
@staticmethod
|
||||
def extract_text_from_message(message: Any) -> str:
|
||||
def extract_text_from_message(message: object) -> str:
|
||||
"""
|
||||
Extract text content from A2A message parts.
|
||||
|
||||
|
|
@ -142,7 +142,7 @@ class A2ARequestUtils:
|
|||
return prompt_tokens, completion_tokens, total_tokens
|
||||
|
||||
|
||||
def get_session_id_from_a2a_params(params: Mapping[str, Any]) -> str | None:
|
||||
def get_session_id_from_a2a_params(params: Mapping[str, object]) -> str | None:
|
||||
message: Final = params.get("message", {})
|
||||
if isinstance(message, dict):
|
||||
return message.get("contextId")
|
||||
|
|
@ -166,7 +166,7 @@ def scope_session_to_principal(session_id: str, principal: str | None) -> str:
|
|||
|
||||
|
||||
# Backwards compatibility aliases
|
||||
def extract_text_from_a2a_message(message: Any) -> str:
|
||||
def extract_text_from_a2a_message(message: object) -> str:
|
||||
return A2ARequestUtils.extract_text_from_message(message)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -200,8 +200,8 @@ class GitLabTemplateManager:
|
|||
metadata=metadata,
|
||||
)
|
||||
|
||||
def _parse_yaml_basic(self, yaml_str: str) -> dict[str, Any]:
|
||||
result: Final[dict[str, Any]] = {}
|
||||
def _parse_yaml_basic(self, yaml_str: str) -> dict[str, bool | int | float | str]:
|
||||
result: Final[dict[str, bool | int | float | str]] = {}
|
||||
for line in yaml_str.split("\n"):
|
||||
line = line.strip()
|
||||
if ":" in line and not line.startswith("#"):
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class VantageLogger(FocusLogger):
|
|||
raw_interval,
|
||||
)
|
||||
|
||||
destination_config: Final[dict[str, Any]] = {}
|
||||
destination_config: Final[dict[str, str]] = {}
|
||||
if resolved_api_key:
|
||||
destination_config["api_key"] = resolved_api_key
|
||||
if resolved_token:
|
||||
|
|
@ -93,7 +93,7 @@ class VantageLogger(FocusLogger):
|
|||
|
||||
pod_lock_manager = None
|
||||
if proxy_logging_obj is not None:
|
||||
writer: Final = getattr(proxy_logging_obj, "db_spend_update_writer", None)
|
||||
writer: Final[object] = getattr(proxy_logging_obj, "db_spend_update_writer", None)
|
||||
if writer is not None:
|
||||
pod_lock_manager = getattr(writer, "pod_lock_manager", None)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ duplicated. BaseAgentsAPIConfig stays as pure transform code.
|
|||
"""
|
||||
|
||||
from collections.abc import Coroutine, Mapping
|
||||
from typing import Any, Final
|
||||
from typing import Final
|
||||
|
||||
import httpx
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
name: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
extra_body: Mapping[str, object] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: HTTPHandler | None = None,
|
||||
|
|
@ -93,7 +93,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
name: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
extra_body: Mapping[str, object] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: AsyncHTTPHandler | None = None,
|
||||
|
|
@ -141,7 +141,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
agents_api_config: BaseAgentsAPIConfig,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: HTTPHandler | None = None,
|
||||
_is_async: bool = False,
|
||||
|
|
@ -181,7 +181,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
agents_api_config: BaseAgentsAPIConfig,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: AsyncHTTPHandler | None = None,
|
||||
) -> AgentListResponse:
|
||||
|
|
@ -216,7 +216,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
name: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: HTTPHandler | None = None,
|
||||
_is_async: bool = False,
|
||||
|
|
@ -259,7 +259,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
name: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: AsyncHTTPHandler | None = None,
|
||||
) -> AgentCreateResponse:
|
||||
|
|
@ -295,7 +295,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
name: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: HTTPHandler | None = None,
|
||||
_is_async: bool = False,
|
||||
|
|
@ -338,7 +338,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
name: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: AsyncHTTPHandler | None = None,
|
||||
) -> AgentDeleteResult:
|
||||
|
|
@ -374,7 +374,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
name: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: HTTPHandler | None = None,
|
||||
_is_async: bool = False,
|
||||
|
|
@ -417,7 +417,7 @@ class AgentsHTTPHandler(InteractionsHTTPHandler):
|
|||
name: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
extra_headers: dict[str, Any] | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
timeout: float | httpx.Timeout | None = None,
|
||||
client: AsyncHTTPHandler | None = None,
|
||||
) -> AgentVersionsResponse:
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@ def _truncate_base64_in_string(value: str) -> str:
|
|||
return _DATA_URI_RE.sub(_base64_data_uri_replacer, value)
|
||||
|
||||
|
||||
def _truncate_base64_in_value(value: Any) -> Any:
|
||||
def _truncate_base64_in_value(
|
||||
value: str | dict[str, object] | list[object] | None,
|
||||
) -> str | dict[str, object] | list[object] | None:
|
||||
"""Iteratively truncate base64 data URIs in a JSON-like value (str/list/dict).
|
||||
|
||||
Uses an explicit stack instead of recursion to satisfy the project's
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@ def _extract_redirect_url(response: httpx.Response, request_url: str) -> str:
|
|||
return str(httpx.URL(request_url).join(location))
|
||||
|
||||
|
||||
def safe_get(client: Any, url: str, **kwargs: Any) -> httpx.Response:
|
||||
def safe_get(client: _UrlFetcher, url: str, **kwargs: Any) -> httpx.Response:
|
||||
"""
|
||||
Fetch a user-supplied URL with SSRF protection on every redirect hop.
|
||||
|
||||
|
|
@ -461,7 +461,7 @@ def safe_get(client: Any, url: str, **kwargs: Any) -> httpx.Response:
|
|||
raise SSRFError("Too many redirects")
|
||||
|
||||
|
||||
async def async_safe_get(client: Any, url: str, **kwargs: Any) -> httpx.Response:
|
||||
async def async_safe_get(client: _AsyncUrlFetcher, url: str, **kwargs: Any) -> httpx.Response:
|
||||
"""Async version of safe_get."""
|
||||
if not getattr(litellm, "user_url_validation", True):
|
||||
kwargs.setdefault("follow_redirects", True)
|
||||
|
|
|
|||
|
|
@ -596,7 +596,7 @@ class ModelResponseIterator:
|
|||
self.reasoning_content_chunks: list[str] = []
|
||||
|
||||
# Track server tool use inputs and results for code_interpreter_results
|
||||
self._server_tool_inputs: dict[str, Any] = {}
|
||||
self._server_tool_inputs: dict[str, object] = {}
|
||||
self.tool_results: list[dict[str, Any]] = []
|
||||
self._current_server_tool_id: str | None = None
|
||||
self._container_id: str | None = None
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from collections.abc import Callable
|
||||
from typing import Any, Final
|
||||
from typing import Final
|
||||
|
||||
import httpx
|
||||
from openai import AsyncAzureOpenAI, AzureOpenAI
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
|
|
@ -191,7 +192,7 @@ class AzureTextCompletion(BaseAzureLLM):
|
|||
model: str,
|
||||
api_base: str,
|
||||
data: dict,
|
||||
timeout: Any,
|
||||
timeout: float | httpx.Timeout | None,
|
||||
model_response: ModelResponse,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
max_retries: int,
|
||||
|
|
@ -253,7 +254,7 @@ class AzureTextCompletion(BaseAzureLLM):
|
|||
api_version: str,
|
||||
data: dict,
|
||||
model: str,
|
||||
timeout: Any,
|
||||
timeout: float | httpx.Timeout | None,
|
||||
azure_ad_token: str | None = None,
|
||||
client=None,
|
||||
litellm_params: dict = {},
|
||||
|
|
@ -306,7 +307,7 @@ class AzureTextCompletion(BaseAzureLLM):
|
|||
api_version: str,
|
||||
data: dict,
|
||||
model: str,
|
||||
timeout: Any,
|
||||
timeout: float | httpx.Timeout | None,
|
||||
azure_ad_token: str | None = None,
|
||||
client=None,
|
||||
litellm_params: dict = {},
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import asyncio
|
|||
import re
|
||||
import time
|
||||
from collections.abc import Mapping
|
||||
from typing import TYPE_CHECKING, Any, Final
|
||||
from typing import TYPE_CHECKING, Final
|
||||
from urllib.parse import quote
|
||||
|
||||
import httpx
|
||||
|
|
@ -127,7 +127,7 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig):
|
|||
|
||||
def map_ocr_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
non_default_params: Mapping[str, object],
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
) -> dict:
|
||||
|
|
@ -164,7 +164,7 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig):
|
|||
raise UnsupportedParamsError(message=f"{e}", model=model, llm_provider="azure_ai") from e
|
||||
|
||||
@staticmethod
|
||||
def _normalize_pages_param(pages: Any) -> str:
|
||||
def _normalize_pages_param(pages: object) -> str:
|
||||
"""
|
||||
Convert a caller-provided `pages` value to Azure DI's query-string
|
||||
form. Azure expects 1-based page numbers, grammar: `^(\\d+(-\\d+)?)(,\\s*(\\d+(-\\d+)?))*$`.
|
||||
|
|
@ -412,7 +412,7 @@ class AzureDocumentIntelligenceOCRConfig(BaseOCRConfig):
|
|||
raise ValueError("Document URL is required")
|
||||
|
||||
# Build Azure DI request
|
||||
data: Final[dict[str, Any]] = {}
|
||||
data: Final[dict[str, str]] = {}
|
||||
|
||||
# Check if it's a data URI (base64)
|
||||
if document_url.startswith("data:"):
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import re
|
||||
import time
|
||||
from collections.abc import Mapping
|
||||
from typing import TYPE_CHECKING, Any, Final, Literal, cast
|
||||
from typing import TYPE_CHECKING, Final, Literal, cast
|
||||
|
||||
from httpx import Headers, Response
|
||||
from pydantic import TypeAdapter, ValidationError
|
||||
|
|
@ -170,7 +170,7 @@ class BedrockBatchesConfig(BaseAWSLLM, BaseBatchesConfig):
|
|||
create_batch_data: CreateBatchRequest,
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
) -> dict[str, Any]:
|
||||
) -> dict[str, object]:
|
||||
"""
|
||||
Transform the batch creation request to Bedrock format.
|
||||
|
||||
|
|
@ -354,7 +354,7 @@ class BedrockBatchesConfig(BaseAWSLLM, BaseBatchesConfig):
|
|||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_openai_compatible_batch_metadata(metadata: Any) -> dict[str, str]:
|
||||
def _get_openai_compatible_batch_metadata(metadata: object) -> dict[str, str]:
|
||||
"""
|
||||
OpenAI Batch metadata only accepts string values.
|
||||
"""
|
||||
|
|
@ -379,7 +379,7 @@ class BedrockBatchesConfig(BaseAWSLLM, BaseBatchesConfig):
|
|||
batch_id: str,
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
) -> dict[str, Any]:
|
||||
) -> dict[str, object]:
|
||||
"""
|
||||
Transform batch retrieval request for Bedrock.
|
||||
|
||||
|
|
@ -523,7 +523,7 @@ class BedrockBatchesConfig(BaseAWSLLM, BaseBatchesConfig):
|
|||
)
|
||||
|
||||
# Enrich metadata with useful Bedrock fields
|
||||
enriched_metadata_raw: Final[dict[str, Any]] = {
|
||||
enriched_metadata_raw: Final[dict[str, object]] = {
|
||||
"jobName": response_data.get("jobName"),
|
||||
"clientRequestToken": response_data.get("clientRequestToken"),
|
||||
"modelId": response_data.get("modelId"),
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class AmazonTwelveLabsPegasusConfig(AmazonInvokeConfig, BaseConfig):
|
|||
headers: dict,
|
||||
) -> dict:
|
||||
input_prompt: Final = self._convert_messages_to_prompt(messages=messages)
|
||||
request_data: Final[dict[str, Any]] = {"inputPrompt": input_prompt}
|
||||
request_data: Final[dict[str, object]] = {"inputPrompt": input_prompt}
|
||||
|
||||
media_source: Final = self._build_media_source(optional_params)
|
||||
if media_source is not None:
|
||||
|
|
|
|||
|
|
@ -335,10 +335,10 @@ class BytezChatConfig(BaseConfig):
|
|||
|
||||
|
||||
class BytezCustomStreamWrapper(CustomStreamWrapper):
|
||||
def chunk_creator(self, chunk: Any):
|
||||
def chunk_creator(self, chunk: object):
|
||||
try:
|
||||
model_response: Final = self.model_response_creator()
|
||||
response_obj: dict[str, Any] = {}
|
||||
response_obj: dict[str, object] = {}
|
||||
|
||||
response_obj = {
|
||||
"text": chunk,
|
||||
|
|
@ -346,7 +346,7 @@ class BytezCustomStreamWrapper(CustomStreamWrapper):
|
|||
"finish_reason": "",
|
||||
}
|
||||
|
||||
completion_obj: Final[dict[str, Any]] = {"content": chunk}
|
||||
completion_obj: Final[dict[str, object]] = {"content": chunk}
|
||||
|
||||
return self.return_processed_chunk_logic(
|
||||
completion_obj=completion_obj,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import ssl
|
||||
from collections.abc import Callable
|
||||
from collections.abc import AsyncIterable, Callable, Iterable
|
||||
from typing import TYPE_CHECKING, Any, Final, cast
|
||||
|
||||
import aiohttp
|
||||
|
|
@ -212,7 +212,7 @@ class BaseLLMAIOHTTPHandler:
|
|||
litellm_params: dict,
|
||||
stream: bool = False,
|
||||
files: dict | None = None,
|
||||
content: Any = None,
|
||||
content: str | bytes | Iterable[bytes] | AsyncIterable[bytes] | None = None,
|
||||
params: dict | None = None,
|
||||
) -> httpx.Response:
|
||||
max_retry_on_unprocessable_entity_error: Final = provider_config.max_retry_on_unprocessable_entity_error
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ class AlephAlphaConfig:
|
|||
setattr(self.__class__, key, value)
|
||||
|
||||
@classmethod
|
||||
def get_config(cls):
|
||||
def get_config(cls) -> dict[str, object]:
|
||||
return {
|
||||
k: v
|
||||
for k, v in cls.__dict__.items()
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ class LemonadeChatConfig(OpenAILikeChatConfig):
|
|||
model: str,
|
||||
api_base: str | None = None,
|
||||
api_key: str | None = None,
|
||||
) -> Any:
|
||||
) -> dict[str, object]:
|
||||
if model.startswith("lemonade/"):
|
||||
model = model.split("/", 1)[1]
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class OpenAIImageEditConfig(BaseImageEditConfig):
|
|||
def _add_image_to_files(
|
||||
self,
|
||||
files_list: list[tuple[str, Any]],
|
||||
image: Any,
|
||||
image: object,
|
||||
field_name: str,
|
||||
) -> None:
|
||||
"""Add an image to the files list with appropriate content type"""
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class RunwayMLTextToSpeechConfig(BaseTextToSpeechConfig):
|
|||
aspeech: bool,
|
||||
api_base: str | None,
|
||||
api_key: str | None,
|
||||
**kwargs: Any,
|
||||
**kwargs: object,
|
||||
) -> Union[
|
||||
"HttpxBinaryResponseContent",
|
||||
Coroutine[object, object, "HttpxBinaryResponseContent"],
|
||||
|
|
|
|||
|
|
@ -651,7 +651,7 @@ def _openai_batch_jsonl_entry_to_vertex_embeddings_rows(
|
|||
|
||||
def _openai_batch_jsonl_entry_to_vertex_rows(
|
||||
openai_entry: dict[str, Any],
|
||||
map_openai_to_vertex_params: Callable[[dict[str, Any]], dict[str, Any]],
|
||||
map_openai_to_vertex_params: Callable[[dict[str, Any]], dict[str, object]],
|
||||
) -> tuple[Mapping[str, object], ...]:
|
||||
"""
|
||||
Transforms a single OpenAI JSONL batch entry into the Vertex rows it maps to.
|
||||
|
|
@ -774,7 +774,7 @@ class _OpenAIToVertexBatchUploadStream(BaseFileUploadStream):
|
|||
def __init__(
|
||||
self,
|
||||
openai_file_content: FileTypes,
|
||||
map_openai_to_vertex_params: Callable[[dict[str, Any]], dict[str, Any]],
|
||||
map_openai_to_vertex_params: Callable[[dict[str, Any]], dict[str, object]],
|
||||
) -> None:
|
||||
self._openai_file_content = openai_file_content
|
||||
self._map_openai_to_vertex_params = map_openai_to_vertex_params
|
||||
|
|
@ -948,7 +948,7 @@ class VertexAIFilesConfig(VertexBase, BaseFilesConfig):
|
|||
def _map_openai_to_vertex_params(
|
||||
self,
|
||||
openai_request_body: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
) -> dict[str, object]:
|
||||
"""
|
||||
wrapper to call VertexGeminiConfig.map_openai_params
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ Google AI Studio /batchEmbedContents Embeddings Endpoint
|
|||
"""
|
||||
|
||||
import json
|
||||
from typing import TYPE_CHECKING, Any, Final, Literal
|
||||
from typing import TYPE_CHECKING, Final, Literal
|
||||
|
||||
import httpx
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ class GoogleBatchEmbeddings(VertexLLM):
|
|||
)
|
||||
|
||||
### TRANSFORMATION (sync path) ###
|
||||
request_data: Any
|
||||
request_data: VertexAIBatchEmbeddingsRequestBody | dict[str, object]
|
||||
if use_embed_content:
|
||||
resolved_files = {}
|
||||
if api_key:
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ def _get_client_from_cache(client_cache_key: str):
|
|||
return litellm.in_memory_llm_clients_cache.get_cache(client_cache_key)
|
||||
|
||||
|
||||
def _set_client_in_cache(client_cache_key: str, vertex_llm_model: Any):
|
||||
def _set_client_in_cache(client_cache_key: str, vertex_llm_model: object):
|
||||
litellm.in_memory_llm_clients_cache.set_cache(
|
||||
key=client_cache_key,
|
||||
value=vertex_llm_model,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Translates from OpenAI's `/v1/audio/transcriptions` to IBM WatsonX's `/ml/v1/aud
|
|||
WatsonX follows the OpenAI spec for audio transcription.
|
||||
"""
|
||||
|
||||
from typing import Any, Final
|
||||
from typing import Final
|
||||
|
||||
from httpx import Response
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ class IBMWatsonXAudioTranscriptionConfig(IBMWatsonXMixin, OpenAIWhisperAudioTran
|
|||
}
|
||||
|
||||
# Convert TypedDict to regular dict for AudioTranscriptionRequestData
|
||||
form_data_dict: Final[dict[str, Any]] = dict(form_data)
|
||||
form_data_dict: Final[dict[str, object]] = dict(form_data)
|
||||
|
||||
return AudioTranscriptionRequestData(data=form_data_dict, files=files)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ and uses LiteLLM auth.
|
|||
import re
|
||||
from collections.abc import Mapping
|
||||
from copy import deepcopy
|
||||
from typing import Any, Final, Literal
|
||||
from typing import Final, Literal
|
||||
|
||||
SupportedA2AVersion = Literal["0.3", "1.0"]
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ def normalize_protocol_version(version: object) -> SupportedA2AVersion | None:
|
|||
return next((supported for supported in SUPPORTED_A2A_PROTOCOL_VERSIONS if supported == major_minor), None)
|
||||
|
||||
|
||||
def resolve_served_protocol_version(card: Mapping[str, Any] | None) -> str:
|
||||
def resolve_served_protocol_version(card: Mapping[str, object] | None) -> str:
|
||||
"""Return the validated protocol version an agent card pins, else the default."""
|
||||
normalized: Final = normalize_protocol_version(card.get("protocolVersion") if card else None)
|
||||
return normalized if normalized is not None else LITELLM_A2A_PROTOCOL_VERSION
|
||||
|
|
@ -53,7 +53,7 @@ def resolve_served_protocol_version(card: Mapping[str, Any] | None) -> str:
|
|||
# Security scheme exposed by the LiteLLM-fronted agent card. Always replaces
|
||||
# whatever upstream advertised — the client must authenticate to the proxy,
|
||||
# not the upstream agent.
|
||||
LITELLM_SECURITY_SCHEMES: Final[dict[str, dict[str, Any]]] = {
|
||||
LITELLM_SECURITY_SCHEMES: Final[dict[str, dict[str, str]]] = {
|
||||
"LiteLLMKey": {
|
||||
"type": "http",
|
||||
"scheme": "bearer",
|
||||
|
|
@ -112,7 +112,7 @@ _ALLOWED_TOP_LEVEL_KEYS: Final = {
|
|||
"url",
|
||||
}
|
||||
|
||||
_DEFAULT_SKILLS: Final[list[dict[str, Any]]] = [
|
||||
_DEFAULT_SKILLS: Final[list[dict[str, str | list[str]]]] = [
|
||||
{
|
||||
"id": "chat",
|
||||
"name": "Chat",
|
||||
|
|
@ -129,7 +129,7 @@ _DEFAULT_MODES: Final[list[str]] = ["text"]
|
|||
_DEFAULT_AGENT_VERSION: Final = "1.0.0"
|
||||
|
||||
|
||||
def _filter_capabilities(upstream_capabilities: Any) -> dict[str, Any]:
|
||||
def _filter_capabilities(upstream_capabilities: object) -> dict[str, object]:
|
||||
"""Return a capabilities dict containing only allowlisted, truthy keys."""
|
||||
if not isinstance(upstream_capabilities, dict):
|
||||
return {}
|
||||
|
|
@ -143,13 +143,13 @@ def _default_litellm_provider(proxy_base_url: str) -> dict[str, str]:
|
|||
|
||||
|
||||
def merge_agent_card(
|
||||
upstream_card: Mapping[str, Any] | None,
|
||||
upstream_card: Mapping[str, object] | None,
|
||||
*,
|
||||
proxy_url: str,
|
||||
proxy_base_url: str,
|
||||
name: str | None = None,
|
||||
description: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
) -> dict[str, object]:
|
||||
"""
|
||||
Build the LiteLLM-fronted agent card.
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ def merge_agent_card(
|
|||
A dict suitable for serving as the proxy's agent card. Only keys in
|
||||
the v1.0 AgentCard schema (plus ``supportedInterfaces``) are emitted.
|
||||
"""
|
||||
base: Final[dict[str, Any]] = deepcopy(dict(upstream_card)) if upstream_card else {}
|
||||
base: Final[dict[str, object]] = deepcopy(dict(upstream_card)) if upstream_card else {}
|
||||
|
||||
# Keep the upstream ``url`` on the stored card: the runtime A2A
|
||||
# invocation path reads it from ``agent_card_params`` to know where to
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from collections.abc import Mapping
|
||||
from typing import Any, Final
|
||||
|
||||
import requests
|
||||
|
|
@ -69,8 +70,8 @@ class CredentialsManagementClient:
|
|||
def create(
|
||||
self,
|
||||
credential_name: str,
|
||||
credential_info: dict[str, Any],
|
||||
credential_values: dict[str, Any],
|
||||
credential_info: Mapping[str, object],
|
||||
credential_values: Mapping[str, object],
|
||||
return_request: bool = False,
|
||||
) -> dict[str, Any] | requests.Request:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import json
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, Final, Literal, TypedDict
|
||||
from typing import TYPE_CHECKING, Final, Literal, TypedDict
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
|
@ -181,7 +181,7 @@ class GuardrailsAI(CustomGuardrail):
|
|||
): # raise exception if invalid, return a str for the user to receive - if rejected, or return a modified dictionary for passing into litellm
|
||||
return await self.process_input(data=data, call_type=call_type)
|
||||
|
||||
async def async_logging_hook(self, kwargs: dict, result: Any, call_type: str) -> tuple[dict, Any]:
|
||||
async def async_logging_hook(self, kwargs: dict, result: object, call_type: str) -> tuple[dict, object]:
|
||||
if call_type == "acompletion" or call_type == "completion":
|
||||
kwargs = await self.process_input(data=kwargs, call_type=call_type)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ from litellm.llms.custom_httpx.http_handler import (
|
|||
)
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.types.guardrails import GuardrailEventHooks
|
||||
from litellm.types.llms.openai import AllMessageValues
|
||||
from litellm.types.llms.openai import AllMessageValues, ChatCompletionToolCallChunk
|
||||
from litellm.types.proxy.guardrails.guardrail_hooks.base import (
|
||||
GuardrailConfigModel,
|
||||
)
|
||||
|
|
@ -36,7 +36,7 @@ from litellm.types.proxy.guardrails.guardrail_hooks.singulr import (
|
|||
ToolCall,
|
||||
ToolCallFunction,
|
||||
)
|
||||
from litellm.types.utils import CallTypes, GenericGuardrailAPIInputs
|
||||
from litellm.types.utils import CallTypes, ChatCompletionMessageToolCall, GenericGuardrailAPIInputs
|
||||
|
||||
_DEFAULT_API_BASE: Final = "http://localhost:8003"
|
||||
_GUARD_ENDPOINT: Final = "/api/v1/ai-gateway/litellm-v2"
|
||||
|
|
@ -339,7 +339,7 @@ class SingulrGuardrail(CustomGuardrail):
|
|||
return inputs
|
||||
|
||||
@staticmethod
|
||||
def _build_tool_call(tool_call: Mapping[str, Any]) -> "ToolCall | None":
|
||||
def _build_tool_call(tool_call: ChatCompletionToolCallChunk | ChatCompletionMessageToolCall) -> "ToolCall | None":
|
||||
tool_call_id: Final = tool_call.get("id")
|
||||
fun: Final = tool_call.get("function")
|
||||
if not tool_call_id or not fun:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pass/fail actions (allow, block, next, modify_response) and data forwarding.
|
|||
import copy
|
||||
import time
|
||||
from collections.abc import Callable, Mapping, Sequence
|
||||
from typing import TYPE_CHECKING, Any, Final, Literal, TypeVar
|
||||
from typing import TYPE_CHECKING, Final, Literal, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
|
@ -314,11 +314,11 @@ class PipelineExecutor:
|
|||
steps: list[PipelineStep],
|
||||
mode: str,
|
||||
data: dict,
|
||||
user_api_key_dict: Any,
|
||||
user_api_key_dict: "UserAPIKeyAuth",
|
||||
call_type: str,
|
||||
policy_name: str,
|
||||
raw_request_snapshot: dict | None = None, # mutable-ok: same request-payload shape as data
|
||||
streaming_chunks: list[Any] | None = None, # mutable-ok: shared buffered-stream chunks, read per step
|
||||
streaming_chunks: list[object] | None = None, # mutable-ok: shared buffered-stream chunks, read per step
|
||||
endpoint_translation: "BaseTranslation | None" = None,
|
||||
) -> PipelineExecutionResult:
|
||||
"""
|
||||
|
|
@ -490,10 +490,10 @@ class PipelineExecutor:
|
|||
step: PipelineStep,
|
||||
mode: str,
|
||||
data: dict,
|
||||
user_api_key_dict: Any,
|
||||
user_api_key_dict: "UserAPIKeyAuth",
|
||||
call_type: str,
|
||||
raw_request_snapshot: dict | None = None, # mutable-ok: same request-payload shape as data
|
||||
streaming_chunks: list[Any] | None = None, # mutable-ok: shared buffered-stream chunks, read per step
|
||||
streaming_chunks: list[object] | None = None, # mutable-ok: shared buffered-stream chunks, read per step
|
||||
endpoint_translation: "BaseTranslation | None" = None,
|
||||
) -> tuple[
|
||||
Literal["pass", "fail", "error"],
|
||||
|
|
@ -722,7 +722,7 @@ def _extract_error_message(e: Exception) -> str:
|
|||
if isinstance(e, ModifyResponseException):
|
||||
return str(e)
|
||||
if HTTPException is not None and isinstance(e, HTTPException):
|
||||
detail: Final = getattr(e, "detail", None)
|
||||
detail: Final[object] = getattr(e, "detail", None)
|
||||
if detail:
|
||||
return str(detail)
|
||||
return str(e)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ def _resolve_session_key(kwargs: dict[str, Any]) -> str | None:
|
|||
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def _last_user_content(messages: list[dict[str, Any]] | None) -> str | None:
|
||||
def _last_user_content(messages: Sequence[Mapping[str, object]] | None) -> str | None:
|
||||
if not messages:
|
||||
return None
|
||||
for msg in reversed(messages):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ Auto-Routing Strategy that works with a Semantic Router Config
|
|||
"""
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Mapping
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import TYPE_CHECKING, Any, Final, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
|
@ -158,7 +158,7 @@ class AutoRouter(CustomLogger):
|
|||
return await asyncio.shield(build_task)
|
||||
|
||||
@staticmethod
|
||||
def _extract_text_from_messages(messages: list[dict[str, Any]]) -> str:
|
||||
def _extract_text_from_messages(messages: Sequence[Mapping[str, object]]) -> str:
|
||||
"""
|
||||
Extract text content from the last user message for routing.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,158 +0,0 @@
|
|||
import json
|
||||
import os
|
||||
from unittest.mock import Mock, patch
|
||||
import pytest
|
||||
|
||||
|
||||
import litellm
|
||||
from litellm.llms.custom_httpx.http_handler import HTTPHandler, AsyncHTTPHandler
|
||||
|
||||
# Mock response for Bedrock image generation
|
||||
mock_image_response = {"images": ["base64_encoded_image_data"], "error": None}
|
||||
|
||||
|
||||
class TestBedrockImageGeneration:
|
||||
def test_image_generation_with_api_key_bearer_token(self):
|
||||
"""Test image generation with bearer token authentication"""
|
||||
test_api_key = "test-bearer-token-12345"
|
||||
model = "bedrock/stability.sd3-large-v1:0"
|
||||
prompt = "A cute baby sea otter"
|
||||
|
||||
with patch(
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration.image_generation"
|
||||
) as mock_bedrock_image_gen:
|
||||
# Setup mock response
|
||||
mock_image_response_obj = litellm.ImageResponse()
|
||||
mock_image_response_obj.data = [{"url": "https://example.com/image.jpg"}]
|
||||
mock_bedrock_image_gen.return_value = mock_image_response_obj
|
||||
|
||||
response = litellm.image_generation(
|
||||
model=model,
|
||||
prompt=prompt,
|
||||
aws_region_name="us-west-2",
|
||||
api_key=test_api_key,
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert len(response.data) > 0
|
||||
|
||||
mock_bedrock_image_gen.assert_called_once()
|
||||
for call in mock_bedrock_image_gen.call_args_list:
|
||||
if "headers" in call.kwargs:
|
||||
headers = call.kwargs["headers"]
|
||||
if (
|
||||
"Authorization" in headers
|
||||
and headers["Authorization"] == f"Bearer {test_api_key}"
|
||||
):
|
||||
break
|
||||
|
||||
def test_image_generation_with_env_variable_bearer_token(self, monkeypatch):
|
||||
"""Test image generation with bearer token from environment variable"""
|
||||
test_api_key = "env-bearer-token-12345"
|
||||
model = "bedrock/stability.sd3-large-v1:0"
|
||||
prompt = "A cute baby sea otter"
|
||||
|
||||
# Mock the environment variable
|
||||
with (
|
||||
patch.dict(os.environ, {"AWS_BEARER_TOKEN_BEDROCK": test_api_key}),
|
||||
patch(
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration.image_generation"
|
||||
) as mock_bedrock_image_gen,
|
||||
):
|
||||
|
||||
mock_image_response_obj = litellm.ImageResponse()
|
||||
mock_image_response_obj.data = [{"url": "https://example.com/image.jpg"}]
|
||||
mock_bedrock_image_gen.return_value = mock_image_response_obj
|
||||
|
||||
response = litellm.image_generation(
|
||||
model=model, prompt=prompt, aws_region_name="us-west-2"
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert len(response.data) > 0
|
||||
|
||||
mock_bedrock_image_gen.assert_called_once()
|
||||
for call in mock_bedrock_image_gen.call_args_list:
|
||||
if "headers" in call.kwargs:
|
||||
headers = call.kwargs["headers"]
|
||||
if (
|
||||
"Authorization" in headers
|
||||
and headers["Authorization"] == f"Bearer {test_api_key}"
|
||||
):
|
||||
break
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_image_generation_with_bearer_token(self):
|
||||
"""Test async image generation with bearer token authentication"""
|
||||
test_api_key = "async-bearer-token-12345"
|
||||
model = "bedrock/stability.sd3-large-v1:0"
|
||||
prompt = "A cute baby sea otter"
|
||||
|
||||
with patch(
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration.async_image_generation"
|
||||
) as mock_async_bedrock_image_gen:
|
||||
mock_image_response_obj = litellm.ImageResponse()
|
||||
mock_image_response_obj.data = [{"url": "https://example.com/image.jpg"}]
|
||||
mock_async_bedrock_image_gen.return_value = mock_image_response_obj
|
||||
|
||||
# Call async image generation with api_key parameter
|
||||
response = await litellm.aimage_generation(
|
||||
model=model,
|
||||
prompt=prompt,
|
||||
aws_region_name="us-west-2",
|
||||
api_key=test_api_key,
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert len(response.data) > 0
|
||||
|
||||
mock_async_bedrock_image_gen.assert_called_once()
|
||||
for call in mock_async_bedrock_image_gen.call_args_list:
|
||||
if "headers" in call.kwargs:
|
||||
headers = call.kwargs["headers"]
|
||||
if (
|
||||
"Authorization" in headers
|
||||
and headers["Authorization"] == f"Bearer {test_api_key}"
|
||||
):
|
||||
break
|
||||
|
||||
def test_image_generation_with_sigv4(self):
|
||||
"""Test image generation falls back to SigV4 auth when no bearer token is provided"""
|
||||
model = "bedrock/stability.sd3-large-v1:0"
|
||||
prompt = "A cute baby sea otter"
|
||||
|
||||
with patch(
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration.image_generation"
|
||||
) as mock_bedrock_image_gen:
|
||||
mock_image_response_obj = litellm.ImageResponse()
|
||||
mock_image_response_obj.data = [{"url": "https://example.com/image.jpg"}]
|
||||
mock_bedrock_image_gen.return_value = mock_image_response_obj
|
||||
|
||||
response = litellm.image_generation(
|
||||
model=model, prompt=prompt, aws_region_name="us-west-2"
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert len(response.data) > 0
|
||||
mock_bedrock_image_gen.assert_called_once()
|
||||
|
||||
|
||||
def test_image_generation_bearer_token_never_runs_the_sigv4_credential_chain(monkeypatch):
|
||||
"""The deployment's AWS profile does not exist, so resolving SigV4 credentials
|
||||
raises; a bearer-token deployment must still sign the request with the
|
||||
bearer token alone."""
|
||||
from litellm.llms.bedrock.image_generation.image_handler import BedrockImageGeneration
|
||||
|
||||
monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "env-bearer-token-12345")
|
||||
|
||||
request = BedrockImageGeneration()._prepare_request(
|
||||
model="amazon.nova-canvas-v1:0",
|
||||
prompt="A cute baby sea otter",
|
||||
optional_params={"aws_region_name": "us-west-2", "aws_profile_name": "litellm-no-such-aws-profile"},
|
||||
api_base=None,
|
||||
extra_headers=None,
|
||||
api_key=None,
|
||||
logging_obj=Mock(),
|
||||
)
|
||||
|
||||
assert request.prepped.headers["Authorization"] == "Bearer env-bearer-token-12345"
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
"""
|
||||
Test SSL verification for hosted_vllm provider.
|
||||
|
||||
This test ensures that the ssl_verify parameter is properly passed through
|
||||
to the HTTP client when using the hosted_vllm provider.
|
||||
|
||||
Issue: ssl_verify parameter was being ignored because hosted_vllm fell through
|
||||
to the OpenAI catch-all path in main.py, which doesn't pass ssl_verify to the HTTP client.
|
||||
"""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
import litellm
|
||||
|
||||
|
||||
class TestHostedVLLMSSLVerify:
|
||||
"""Test suite for SSL verification in hosted_vllm provider."""
|
||||
|
||||
@patch("litellm.llms.custom_httpx.llm_http_handler._get_httpx_client")
|
||||
def test_hosted_vllm_ssl_verify_false_sync(self, mock_get_httpx_client):
|
||||
"""Test that ssl_verify=False is passed to the HTTP client for sync calls."""
|
||||
# Setup mock client
|
||||
mock_client = MagicMock()
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.headers = {"content-type": "application/json"}
|
||||
mock_response.json.return_value = {
|
||||
"id": "chatcmpl-test",
|
||||
"object": "chat.completion",
|
||||
"created": 1234567890,
|
||||
"model": "test-model",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Test response",
|
||||
},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 5,
|
||||
"total_tokens": 15,
|
||||
},
|
||||
}
|
||||
mock_response.text = '{"id": "chatcmpl-test", "object": "chat.completion", "created": 1234567890, "model": "test-model", "choices": [{"index": 0, "message": {"role": "assistant", "content": "Test response"}, "finish_reason": "stop"}], "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}}'
|
||||
mock_client.post.return_value = mock_response
|
||||
mock_get_httpx_client.return_value = mock_client
|
||||
|
||||
try:
|
||||
litellm.completion(
|
||||
model="hosted_vllm/test-model",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
api_base="https://test-vllm.example.com/v1",
|
||||
ssl_verify=False,
|
||||
)
|
||||
except Exception:
|
||||
# Even if the response parsing fails, we just need to verify
|
||||
# that the mock was called with the correct ssl_verify parameter
|
||||
pass
|
||||
|
||||
# Verify _get_httpx_client was called with ssl_verify=False
|
||||
mock_get_httpx_client.assert_called()
|
||||
call_args = mock_get_httpx_client.call_args
|
||||
|
||||
# Check that params contains ssl_verify=False
|
||||
if call_args[0]:
|
||||
# Positional argument
|
||||
params = call_args[0][0]
|
||||
else:
|
||||
# Keyword argument
|
||||
params = call_args[1].get("params", {})
|
||||
|
||||
assert (
|
||||
params.get("ssl_verify") is False
|
||||
), f"Expected ssl_verify=False in params, got {params}"
|
||||
|
||||
@patch("litellm.llms.custom_httpx.llm_http_handler.get_async_httpx_client")
|
||||
@pytest.mark.asyncio
|
||||
async def test_hosted_vllm_ssl_verify_false_async(
|
||||
self, mock_get_async_httpx_client
|
||||
):
|
||||
"""Test that ssl_verify=False is passed to the HTTP client for async calls."""
|
||||
# Setup mock async client
|
||||
mock_client = MagicMock()
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.headers = {"content-type": "application/json"}
|
||||
mock_response.json.return_value = {
|
||||
"id": "chatcmpl-test",
|
||||
"object": "chat.completion",
|
||||
"created": 1234567890,
|
||||
"model": "test-model",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Test response",
|
||||
},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 5,
|
||||
"total_tokens": 15,
|
||||
},
|
||||
}
|
||||
mock_response.text = '{"id": "chatcmpl-test", "object": "chat.completion", "created": 1234567890, "model": "test-model", "choices": [{"index": 0, "message": {"role": "assistant", "content": "Test response"}, "finish_reason": "stop"}], "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}}'
|
||||
|
||||
async def mock_post(*args, **kwargs):
|
||||
return mock_response
|
||||
|
||||
mock_client.post = mock_post
|
||||
mock_get_async_httpx_client.return_value = mock_client
|
||||
|
||||
try:
|
||||
await litellm.acompletion(
|
||||
model="hosted_vllm/test-model",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
api_base="https://test-vllm.example.com/v1",
|
||||
ssl_verify=False,
|
||||
)
|
||||
except Exception:
|
||||
# Even if the response parsing fails, we just need to verify
|
||||
# that the mock was called with the correct ssl_verify parameter
|
||||
pass
|
||||
|
||||
# Verify get_async_httpx_client was called with ssl_verify=False
|
||||
mock_get_async_httpx_client.assert_called()
|
||||
call_kwargs = mock_get_async_httpx_client.call_args[1]
|
||||
|
||||
# Check that params contains ssl_verify=False
|
||||
params = call_kwargs.get("params", {})
|
||||
assert (
|
||||
params.get("ssl_verify") is False
|
||||
), f"Expected ssl_verify=False in params, got {params}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v", "-s"])
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
"""
|
||||
Test SSL verification for hosted_vllm provider embeddings.
|
||||
|
||||
This test ensures that the ssl_verify parameter is properly passed through
|
||||
to the HTTP client when using the hosted_vllm provider for embeddings.
|
||||
|
||||
Issue: ssl_verify parameter was being ignored because hosted_vllm fell through
|
||||
to the openai_like catch-all path in main.py, which doesn't pass ssl_verify to the HTTP client.
|
||||
"""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
import litellm
|
||||
|
||||
|
||||
class TestHostedVLLMEmbeddingSSLVerify:
|
||||
"""Test suite for SSL verification in hosted_vllm provider embeddings."""
|
||||
|
||||
@patch("litellm.llms.custom_httpx.llm_http_handler._get_httpx_client")
|
||||
def test_hosted_vllm_embedding_ssl_verify_false_sync(self, mock_get_httpx_client):
|
||||
"""Test that ssl_verify=False is passed to the HTTP client for sync embedding calls."""
|
||||
# Setup mock client
|
||||
mock_client = MagicMock()
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.headers = {"content-type": "application/json"}
|
||||
mock_response.json.return_value = {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"object": "embedding",
|
||||
"index": 0,
|
||||
"embedding": [0.1, 0.2, 0.3, 0.4, 0.5],
|
||||
}
|
||||
],
|
||||
"model": "text-embedding-model",
|
||||
"usage": {
|
||||
"prompt_tokens": 5,
|
||||
"total_tokens": 5,
|
||||
},
|
||||
}
|
||||
mock_response.text = '{"object": "list", "data": [{"object": "embedding", "index": 0, "embedding": [0.1, 0.2, 0.3, 0.4, 0.5]}], "model": "text-embedding-model", "usage": {"prompt_tokens": 5, "total_tokens": 5}}'
|
||||
mock_client.post.return_value = mock_response
|
||||
mock_get_httpx_client.return_value = mock_client
|
||||
|
||||
try:
|
||||
litellm.embedding(
|
||||
model="hosted_vllm/text-embedding-model",
|
||||
input=["hello world"],
|
||||
api_base="https://test-vllm.example.com/v1",
|
||||
ssl_verify=False,
|
||||
)
|
||||
except Exception:
|
||||
# Even if the response parsing fails, we just need to verify
|
||||
# that the mock was called with the correct ssl_verify parameter
|
||||
pass
|
||||
|
||||
# Verify _get_httpx_client was called with ssl_verify=False
|
||||
mock_get_httpx_client.assert_called()
|
||||
call_args = mock_get_httpx_client.call_args
|
||||
|
||||
# Check that params contains ssl_verify=False
|
||||
if call_args[0]:
|
||||
# Positional argument
|
||||
params = call_args[0][0]
|
||||
else:
|
||||
# Keyword argument
|
||||
params = call_args[1].get("params", {})
|
||||
|
||||
assert (
|
||||
params.get("ssl_verify") is False
|
||||
), f"Expected ssl_verify=False in params, got {params}"
|
||||
|
||||
@patch("litellm.llms.custom_httpx.llm_http_handler.get_async_httpx_client")
|
||||
@pytest.mark.asyncio
|
||||
async def test_hosted_vllm_embedding_ssl_verify_false_async(
|
||||
self, mock_get_async_httpx_client
|
||||
):
|
||||
"""Test that ssl_verify=False is passed to the HTTP client for async embedding calls."""
|
||||
# Setup mock async client
|
||||
mock_client = MagicMock()
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.headers = {"content-type": "application/json"}
|
||||
mock_response.json.return_value = {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"object": "embedding",
|
||||
"index": 0,
|
||||
"embedding": [0.1, 0.2, 0.3, 0.4, 0.5],
|
||||
}
|
||||
],
|
||||
"model": "text-embedding-model",
|
||||
"usage": {
|
||||
"prompt_tokens": 5,
|
||||
"total_tokens": 5,
|
||||
},
|
||||
}
|
||||
mock_response.text = '{"object": "list", "data": [{"object": "embedding", "index": 0, "embedding": [0.1, 0.2, 0.3, 0.4, 0.5]}], "model": "text-embedding-model", "usage": {"prompt_tokens": 5, "total_tokens": 5}}'
|
||||
|
||||
async def mock_post(*args, **kwargs):
|
||||
return mock_response
|
||||
|
||||
mock_client.post = mock_post
|
||||
mock_get_async_httpx_client.return_value = mock_client
|
||||
|
||||
try:
|
||||
await litellm.aembedding(
|
||||
model="hosted_vllm/text-embedding-model",
|
||||
input=["hello world"],
|
||||
api_base="https://test-vllm.example.com/v1",
|
||||
ssl_verify=False,
|
||||
)
|
||||
except Exception:
|
||||
# Even if the response parsing fails, we just need to verify
|
||||
# that the mock was called with the correct ssl_verify parameter
|
||||
pass
|
||||
|
||||
# Verify get_async_httpx_client was called with ssl_verify=False
|
||||
mock_get_async_httpx_client.assert_called()
|
||||
call_kwargs = mock_get_async_httpx_client.call_args[1]
|
||||
|
||||
# Check that params contains ssl_verify=False
|
||||
params = call_kwargs.get("params", {})
|
||||
assert (
|
||||
params.get("ssl_verify") is False
|
||||
), f"Expected ssl_verify=False in params, got {params}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v", "-s"])
|
||||
0
tests/unit/__init__.py
Normal file
0
tests/unit/__init__.py
Normal file
0
tests/unit/integrations/__init__.py
Normal file
0
tests/unit/integrations/__init__.py
Normal file
0
tests/unit/integrations/levo/__init__.py
Normal file
0
tests/unit/integrations/levo/__init__.py
Normal file
0
tests/unit/integrations/litellm_agent/__init__.py
Normal file
0
tests/unit/integrations/litellm_agent/__init__.py
Normal file
0
tests/unit/integrations/mavvrik_focus/__init__.py
Normal file
0
tests/unit/integrations/mavvrik_focus/__init__.py
Normal file
0
tests/unit/integrations/opik/__init__.py
Normal file
0
tests/unit/integrations/opik/__init__.py
Normal file
0
tests/unit/integrations/pointfive/__init__.py
Normal file
0
tests/unit/integrations/pointfive/__init__.py
Normal file
0
tests/unit/litellm_core_utils/__init__.py
Normal file
0
tests/unit/litellm_core_utils/__init__.py
Normal file
0
tests/unit/litellm_core_utils/audio_utils/__init__.py
Normal file
0
tests/unit/litellm_core_utils/audio_utils/__init__.py
Normal file
0
tests/unit/llms/__init__.py
Normal file
0
tests/unit/llms/__init__.py
Normal file
0
tests/unit/llms/a2a/__init__.py
Normal file
0
tests/unit/llms/a2a/__init__.py
Normal file
0
tests/unit/llms/a2a/chat/__init__.py
Normal file
0
tests/unit/llms/a2a/chat/__init__.py
Normal file
0
tests/unit/llms/anthropic/__init__.py
Normal file
0
tests/unit/llms/anthropic/__init__.py
Normal file
0
tests/unit/llms/anthropic/batches/__init__.py
Normal file
0
tests/unit/llms/anthropic/batches/__init__.py
Normal file
0
tests/unit/llms/base_llm/__init__.py
Normal file
0
tests/unit/llms/base_llm/__init__.py
Normal file
0
tests/unit/llms/base_llm/batches/__init__.py
Normal file
0
tests/unit/llms/base_llm/batches/__init__.py
Normal file
0
tests/unit/llms/base_llm/realtime/__init__.py
Normal file
0
tests/unit/llms/base_llm/realtime/__init__.py
Normal file
0
tests/unit/llms/baseten/__init__.py
Normal file
0
tests/unit/llms/baseten/__init__.py
Normal file
0
tests/unit/llms/baseten/chat/__init__.py
Normal file
0
tests/unit/llms/baseten/chat/__init__.py
Normal file
0
tests/unit/llms/bedrock/__init__.py
Normal file
0
tests/unit/llms/bedrock/__init__.py
Normal file
0
tests/unit/llms/bedrock/chat/__init__.py
Normal file
0
tests/unit/llms/bedrock/chat/__init__.py
Normal file
0
tests/unit/llms/bedrock/chat/agentcore/__init__.py
Normal file
0
tests/unit/llms/bedrock/chat/agentcore/__init__.py
Normal file
0
tests/unit/llms/bedrock/chat/mantle/__init__.py
Normal file
0
tests/unit/llms/bedrock/chat/mantle/__init__.py
Normal file
0
tests/unit/llms/bedrock/count_tokens/__init__.py
Normal file
0
tests/unit/llms/bedrock/count_tokens/__init__.py
Normal file
0
tests/unit/llms/bedrock/files/__init__.py
Normal file
0
tests/unit/llms/bedrock/files/__init__.py
Normal file
0
tests/unit/llms/bedrock/image/__init__.py
Normal file
0
tests/unit/llms/bedrock/image/__init__.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from unittest.mock import Mock
|
||||
|
||||
def test_image_generation_bearer_token_never_runs_the_sigv4_credential_chain(monkeypatch):
|
||||
"""The deployment's AWS profile does not exist, so resolving SigV4 credentials
|
||||
raises; a bearer-token deployment must still sign the request with the
|
||||
bearer token alone."""
|
||||
from litellm.llms.bedrock.image_generation.image_handler import BedrockImageGeneration
|
||||
|
||||
monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "env-bearer-token-12345")
|
||||
|
||||
request = BedrockImageGeneration()._prepare_request(
|
||||
model="amazon.nova-canvas-v1:0",
|
||||
prompt="A cute baby sea otter",
|
||||
optional_params={"aws_region_name": "us-west-2", "aws_profile_name": "litellm-no-such-aws-profile"},
|
||||
api_base=None,
|
||||
extra_headers=None,
|
||||
api_key=None,
|
||||
logging_obj=Mock(),
|
||||
)
|
||||
|
||||
assert request.prepped.headers["Authorization"] == "Bearer env-bearer-token-12345"
|
||||
|
|
@ -11,7 +11,8 @@ def test_bedrock_image_prepare_request_with_arn() -> None:
|
|||
|
||||
with (
|
||||
patch(
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration._get_boto_credentials_from_optional_params"
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration."
|
||||
"_get_boto_credentials_from_optional_params"
|
||||
),
|
||||
patch(
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration.get_request_headers"
|
||||
|
|
@ -31,7 +32,8 @@ def test_bedrock_image_prepare_request_with_arn() -> None:
|
|||
|
||||
assert (
|
||||
request.endpoint_url
|
||||
== "https://bedrock-runtime.test.com/model/arn%3Aaws%3Abedrock%3Aus-east-1%3A123456789012%3Aapplication-inference-profile%2Fabcdefghi123/invoke"
|
||||
== "https://bedrock-runtime.test.com/model/arn%3Aaws%3Abedrock%3Aus-east-1%3A123456789012"
|
||||
"%3Aapplication-inference-profile%2Fabcdefghi123/invoke"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -41,7 +43,8 @@ def test_bedrock_image_prepare_request_without_arn() -> None:
|
|||
|
||||
with (
|
||||
patch(
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration._get_boto_credentials_from_optional_params"
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration."
|
||||
"_get_boto_credentials_from_optional_params"
|
||||
),
|
||||
patch(
|
||||
"litellm.llms.bedrock.image_generation.image_handler.BedrockImageGeneration.get_request_headers"
|
||||
0
tests/unit/llms/bedrock/image_edit/__init__.py
Normal file
0
tests/unit/llms/bedrock/image_edit/__init__.py
Normal file
0
tests/unit/llms/bedrock/invoke_agent/__init__.py
Normal file
0
tests/unit/llms/bedrock/invoke_agent/__init__.py
Normal file
0
tests/unit/llms/bedrock/passthrough/__init__.py
Normal file
0
tests/unit/llms/bedrock/passthrough/__init__.py
Normal file
|
|
@ -1072,7 +1072,8 @@ class TestDeAnonymizeConverseStream:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reasoning_text_delta_de_anonymized(self):
|
||||
"""Reasoning deltas carry model output; their text must be guardrailed while the reasoning signature is left untouched."""
|
||||
"""Reasoning deltas carry model output; their text must be guardrailed while the
|
||||
reasoning signature is left untouched."""
|
||||
stream_bytes = (
|
||||
_build_event_stream_frame("messageStart", {"role": "assistant"})
|
||||
+ _build_event_stream_frame(
|
||||
|
|
@ -1105,7 +1106,8 @@ class TestDeAnonymizeConverseStream:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tool_use_input_delta_de_anonymized(self):
|
||||
"""toolUse.input deltas carry model-generated tool arguments and must be guardrailed instead of being forwarded raw."""
|
||||
"""toolUse.input deltas carry model-generated tool arguments and must be
|
||||
guardrailed instead of being forwarded raw."""
|
||||
stream_bytes = _build_event_stream_frame(
|
||||
"contentBlockDelta",
|
||||
{"contentBlockIndex": 0, "delta": {"toolUse": {"input": '{"q":"<PERSON_1>"}'}}},
|
||||
|
|
@ -1154,7 +1156,8 @@ class TestDeAnonymizeConverseStream:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_text_and_reasoning_deltas_de_anonymized_independently(self):
|
||||
"""Distinct delta kinds must each be guardrailed and written back into their own field without bleeding the de-anonymized text across kinds."""
|
||||
"""Distinct delta kinds must each be guardrailed and written back into their own
|
||||
field without bleeding the de-anonymized text across kinds."""
|
||||
captured = {}
|
||||
|
||||
async def mock_hook(data, user_api_key_dict, response):
|
||||
|
|
@ -1192,7 +1195,8 @@ class TestDeAnonymizeConverseStream:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reasoning_signature_only_frame_left_unmodified(self):
|
||||
"""A reasoning delta carrying only a signature has no guardrailable text; it must be forwarded untouched and the guardrail must not run."""
|
||||
"""A reasoning delta carrying only a signature has no guardrailable text; it must
|
||||
be forwarded untouched and the guardrail must not run."""
|
||||
stream_bytes = _build_event_stream_frame(
|
||||
"contentBlockDelta",
|
||||
{"contentBlockIndex": 0, "delta": {"reasoningContent": {"signature": "sig"}}},
|
||||
|
|
@ -367,8 +367,6 @@ def test_bedrock_passthrough_region_extraction_from_inference_profile_arn():
|
|||
assert (
|
||||
"us-west-2" in api_base
|
||||
), f"Expected region 'us-west-2' from ARN in base URL, but got: {api_base}"
|
||||
|
||||
|
||||
def test_bedrock_passthrough_model_id_arn_encoding():
|
||||
"""
|
||||
Test that model_id ARNs are properly URL-encoded when used in endpoints.
|
||||
|
|
@ -421,7 +419,9 @@ def test_bedrock_passthrough_model_id_arn_encoding():
|
|||
), f"ARN slash should be encoded, but found unencoded version in: {url_str}"
|
||||
|
||||
# Verify the complete expected URL structure
|
||||
expected_encoded_model_id = "arn:aws:bedrock:us-east-1:590183661440:application-inference-profile%2Fb943q2qbl3m7"
|
||||
expected_encoded_model_id = (
|
||||
"arn:aws:bedrock:us-east-1:590183661440:application-inference-profile%2Fb943q2qbl3m7"
|
||||
)
|
||||
expected_url = f"https://bedrock-runtime.us-east-1.amazonaws.com/model/{expected_encoded_model_id}/converse"
|
||||
assert url_str == expected_url, f"Expected {expected_url}, but got: {url_str}"
|
||||
|
||||
|
|
@ -517,7 +517,10 @@ def test_bedrock_passthrough_model_id_without_arn():
|
|||
def _event_frame(event_type: str, payload: dict) -> bytes:
|
||||
def header(name: str, value: str) -> bytes:
|
||||
name_b, value_b = name.encode(), value.encode()
|
||||
return struct.pack("!B", len(name_b)) + name_b + struct.pack("!B", 7) + struct.pack("!H", len(value_b)) + value_b
|
||||
return (
|
||||
struct.pack("!B", len(name_b)) + name_b
|
||||
+ struct.pack("!B", 7) + struct.pack("!H", len(value_b)) + value_b
|
||||
)
|
||||
|
||||
payload_b = json.dumps(payload, separators=(",", ":")).encode()
|
||||
headers_b = (
|
||||
|
|
@ -591,7 +594,9 @@ def _feed(collector: PassthroughStreamCollector, stream: bytes, chunk_size: int
|
|||
|
||||
def test_converse_stream_collector_keeps_usage_without_retaining_the_stream():
|
||||
texts = [f"tok{i} " for i in range(4000)]
|
||||
stream = _event_frame("messageStart", {"role": "assistant"}) + _text_block(0, texts) + _stream_tail("end_turn", 4000)
|
||||
stream = (
|
||||
_event_frame("messageStart", {"role": "assistant"}) + _text_block(0, texts) + _stream_tail("end_turn", 4000)
|
||||
)
|
||||
_feed(_converse_stream_collector(), stream)
|
||||
|
||||
tracemalloc.start()
|
||||
0
tests/unit/llms/bedrock/realtime/__init__.py
Normal file
0
tests/unit/llms/bedrock/realtime/__init__.py
Normal file
|
|
@ -18,6 +18,21 @@ from litellm.llms.bedrock.realtime.handler import BedrockRealtime
|
|||
from litellm.llms.bedrock.realtime.transformation import BedrockRealtimeConfig
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _isolate_host_aws_config(monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("AWS_SHARED_CREDENTIALS_FILE", str(tmp_path / "credentials"))
|
||||
monkeypatch.setenv("AWS_CONFIG_FILE", str(tmp_path / "config"))
|
||||
monkeypatch.setenv("AWS_EC2_METADATA_DISABLED", "true")
|
||||
for env_var in (
|
||||
"AWS_PROFILE",
|
||||
"AWS_DEFAULT_PROFILE",
|
||||
"AWS_BEARER_TOKEN_BEDROCK",
|
||||
"AWS_REGION_NAME",
|
||||
"AWS_DEFAULT_REGION",
|
||||
):
|
||||
monkeypatch.delenv(env_var, raising=False)
|
||||
|
||||
|
||||
class FakePayloadPart:
|
||||
def __init__(self, bytes_):
|
||||
self.bytes_ = bytes_
|
||||
0
tests/unit/llms/bedrock/rerank/__init__.py
Normal file
0
tests/unit/llms/bedrock/rerank/__init__.py
Normal file
|
|
@ -15,6 +15,21 @@ from litellm.llms.bedrock.base_aws_llm import Boto3CredentialsInfo
|
|||
from litellm.llms.bedrock.rerank.handler import BedrockRerankHandler
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _isolate_host_aws_config(monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("AWS_SHARED_CREDENTIALS_FILE", str(tmp_path / "credentials"))
|
||||
monkeypatch.setenv("AWS_CONFIG_FILE", str(tmp_path / "config"))
|
||||
monkeypatch.setenv("AWS_EC2_METADATA_DISABLED", "true")
|
||||
for env_var in (
|
||||
"AWS_PROFILE",
|
||||
"AWS_DEFAULT_PROFILE",
|
||||
"AWS_BEARER_TOKEN_BEDROCK",
|
||||
"AWS_REGION_NAME",
|
||||
"AWS_DEFAULT_REGION",
|
||||
):
|
||||
monkeypatch.delenv(env_var, raising=False)
|
||||
|
||||
# Mock response for Bedrock rerank
|
||||
# Format based on Bedrock rerank API response structure
|
||||
bedrock_rerank_response = {
|
||||
|
|
@ -30,7 +45,8 @@ bedrock_rerank_response = {
|
|||
test_query = "What is the capital of the United States?"
|
||||
test_documents = [
|
||||
"Carson City is the capital city of the American state of Nevada.",
|
||||
"The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean. Its capital is Saipan.",
|
||||
"The Commonwealth of the Northern Mariana Islands is a group of islands in the Pacific Ocean. "
|
||||
"Its capital is Saipan.",
|
||||
"Washington, D.C. is the capital of the United States.",
|
||||
]
|
||||
|
||||
0
tests/unit/llms/bedrock/vector_stores/__init__.py
Normal file
0
tests/unit/llms/bedrock/vector_stores/__init__.py
Normal file
|
|
@ -46,7 +46,8 @@ def test_transform_search_request_encodes_vector_store_id():
|
|||
|
||||
assert (
|
||||
url
|
||||
== "https://bedrock-agent-runtime.us-west-2.amazonaws.com/knowledgebases/..%2F..%2Fknowledgebases%2Fother%3Fx%3D1%23frag/retrieve"
|
||||
== "https://bedrock-agent-runtime.us-west-2.amazonaws.com/knowledgebases/..%2F..%2Fknowledgebases%2Fother"
|
||||
"%3Fx%3D1%23frag/retrieve"
|
||||
)
|
||||
assert body["retrievalQuery"].get("text") == "hello"
|
||||
|
||||
0
tests/unit/llms/bedrock_mantle/__init__.py
Normal file
0
tests/unit/llms/bedrock_mantle/__init__.py
Normal file
0
tests/unit/llms/bedrock_mantle/passthrough/__init__.py
Normal file
0
tests/unit/llms/bedrock_mantle/passthrough/__init__.py
Normal file
|
|
@ -109,7 +109,13 @@ def test_region_falls_back_to_the_mantle_default_without_any_hint(no_ambient_aws
|
|||
({}, {"AWS_BEARER_TOKEN_BEDROCK": "aws-env-key"}, "aws-env-key"),
|
||||
],
|
||||
)
|
||||
def test_sign_request_uses_the_deployment_bearer_token(no_ambient_aws, monkeypatch, litellm_params, env, expected_bearer):
|
||||
def test_sign_request_uses_the_deployment_bearer_token(
|
||||
no_ambient_aws,
|
||||
monkeypatch,
|
||||
litellm_params,
|
||||
env,
|
||||
expected_bearer,
|
||||
):
|
||||
for name, value in env.items():
|
||||
monkeypatch.setenv(name, value)
|
||||
headers, body = BedrockMantlePassthroughConfig().sign_request(
|
||||
0
tests/unit/llms/black_forest_labs/__init__.py
Normal file
0
tests/unit/llms/black_forest_labs/__init__.py
Normal file
0
tests/unit/llms/black_forest_labs/image_edit/__init__.py
Normal file
0
tests/unit/llms/black_forest_labs/image_edit/__init__.py
Normal file
0
tests/unit/llms/bytez/__init__.py
Normal file
0
tests/unit/llms/bytez/__init__.py
Normal file
0
tests/unit/llms/bytez/chat/__init__.py
Normal file
0
tests/unit/llms/bytez/chat/__init__.py
Normal file
|
|
@ -8,6 +8,32 @@ from litellm.llms.bytez.chat.transformation import BytezChatConfig, API_BASE, ve
|
|||
TEST_API_KEY = "MOCK_BYTEZ_API_KEY"
|
||||
TEST_MODEL_NAME = "google/gemma-3-4b-it"
|
||||
TEST_MODEL = f"bytez/{TEST_MODEL_NAME}"
|
||||
CAT_IMAGE_URL = (
|
||||
"https://images.squarespace-cdn.com/content/v1/5452d441e4b0c188b51fef1a/1615326541809-TW01PVTOJ4PXQUX"
|
||||
"VRLHI/male-orange-tabby-cat.jpg"
|
||||
)
|
||||
KAGGLE_AUDIO_URL = (
|
||||
"https://storage.googleapis.com/kagglesdsdata/datasets/1736753/2838478/dataset/dataset/B_ANI01_MC_FN_"
|
||||
"SIM01_101.wav?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=databundle-worker-v2%40kaggle-1616"
|
||||
"07.iam.gserviceaccount.com%2F20250711%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20250711T192905Z&"
|
||||
"X-Goog-Expires=345600&X-Goog-SignedHeaders=host&X-Goog-Signature=812b4bd6fcf9296f8e34f67664d900a81cf"
|
||||
"81a4c8a4f439ce12befc89b4bef07c2645cab20ce5ba8f6b311dffa85aa05b70b4efbe53bced50a43a5e7622ea1ee0d8cc39"
|
||||
"0679cdc6a6aae2c27f75debc1ce2361c595b3c9e1b8c88e2756ffc6b4f290af7f3dfa7232dc69ccc9a2181be756e0d538250"
|
||||
"f9761a8b05ba1ac6c6b5d946f97a16aa14a5609ae62a2c4713c2077fcd34d129dbcdac6bb543ae547507b1a424e4fd09f817"
|
||||
"000943c11507e0a74c514ec212b17427b7fc9e2ce87a250db1258645e4862a4261e3790fd99c9186148ad0653acd2b6a9468"
|
||||
"adbeb94f17b5a685551037fd2cc9fe72fa405a006c0bd42d03be1e4c0dc4023ed3a77171edff3"
|
||||
)
|
||||
KAGGLE_VIDEO_URL = (
|
||||
"https://storage.googleapis.com/kagglesdsdata/datasets/3957252/6888743/dog1.mp4?X-Goog-Algorithm=GOOG"
|
||||
"4-RSA-SHA256&X-Goog-Credential=databundle-worker-v2%40kaggle-161607.iam.gserviceaccount.com%2F202507"
|
||||
"11%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20250711T193025Z&X-Goog-Expires=345600&X-Goog-Signed"
|
||||
"Headers=host&X-Goog-Signature=810961d9abcbc2437954fdf19ef216deb65d3977eb354ec10af0d4644627cc6b143a5f"
|
||||
"c6450996bae1787c09d26334de7cd6ff887510a5ac2a6eed3cfcc6673a47686c84c1f2b0bf543009388d83f2cd9551ad5f72"
|
||||
"084513c6a7acd2c718849a4ebe951ccc5631bed014b0d115225c048b9f5de68673a37db24a98ad39cf3d0ba16fb764bf38eb"
|
||||
"90c78c295c21a4ddac08c3c661b65efd511ccb86bacb87a2e2a97a06f53ea1c64d5dcf274001a61bc20867802549601301d9"
|
||||
"99f5a5b2e49fd444b7db860c68e1c67df6e8edd5ad97171eaafb4fa1462453924ea4d78733be411cb6b5c910d4f829cd7189"
|
||||
"c28dc1b22c8ae2a4da844a0d202e9e64bc7fb17947"
|
||||
)
|
||||
TEST_MESSAGES = [{"role": "user", "content": "Hello"}]
|
||||
|
||||
|
||||
|
|
@ -148,7 +174,7 @@ class TestBytezChatConfig:
|
|||
"What color is this cat?",
|
||||
{
|
||||
"type": "image_url",
|
||||
"url": "https://images.squarespace-cdn.com/content/v1/5452d441e4b0c188b51fef1a/1615326541809-TW01PVTOJ4PXQUXVRLHI/male-orange-tabby-cat.jpg",
|
||||
"url": CAT_IMAGE_URL,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -160,7 +186,7 @@ class TestBytezChatConfig:
|
|||
{"type": "text", "text": "What color is this cat?"},
|
||||
{
|
||||
"type": "image",
|
||||
"url": "https://images.squarespace-cdn.com/content/v1/5452d441e4b0c188b51fef1a/1615326541809-TW01PVTOJ4PXQUXVRLHI/male-orange-tabby-cat.jpg",
|
||||
"url": CAT_IMAGE_URL,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -174,7 +200,7 @@ class TestBytezChatConfig:
|
|||
{"type": "text", "text": "What color is this cat?"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"url": "https://images.squarespace-cdn.com/content/v1/5452d441e4b0c188b51fef1a/1615326541809-TW01PVTOJ4PXQUXVRLHI/male-orange-tabby-cat.jpg",
|
||||
"url": CAT_IMAGE_URL,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -186,7 +212,7 @@ class TestBytezChatConfig:
|
|||
{"type": "text", "text": "What color is this cat?"},
|
||||
{
|
||||
"type": "image",
|
||||
"url": "https://images.squarespace-cdn.com/content/v1/5452d441e4b0c188b51fef1a/1615326541809-TW01PVTOJ4PXQUXVRLHI/male-orange-tabby-cat.jpg",
|
||||
"url": CAT_IMAGE_URL,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -200,7 +226,7 @@ class TestBytezChatConfig:
|
|||
{"type": "text", "text": "What kind of cat meow is this?"},
|
||||
{
|
||||
"type": "input_audio",
|
||||
"url": "https://storage.googleapis.com/kagglesdsdata/datasets/1736753/2838478/dataset/dataset/B_ANI01_MC_FN_SIM01_101.wav?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=databundle-worker-v2%40kaggle-161607.iam.gserviceaccount.com%2F20250711%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20250711T192905Z&X-Goog-Expires=345600&X-Goog-SignedHeaders=host&X-Goog-Signature=812b4bd6fcf9296f8e34f67664d900a81cf81a4c8a4f439ce12befc89b4bef07c2645cab20ce5ba8f6b311dffa85aa05b70b4efbe53bced50a43a5e7622ea1ee0d8cc390679cdc6a6aae2c27f75debc1ce2361c595b3c9e1b8c88e2756ffc6b4f290af7f3dfa7232dc69ccc9a2181be756e0d538250f9761a8b05ba1ac6c6b5d946f97a16aa14a5609ae62a2c4713c2077fcd34d129dbcdac6bb543ae547507b1a424e4fd09f817000943c11507e0a74c514ec212b17427b7fc9e2ce87a250db1258645e4862a4261e3790fd99c9186148ad0653acd2b6a9468adbeb94f17b5a685551037fd2cc9fe72fa405a006c0bd42d03be1e4c0dc4023ed3a77171edff3",
|
||||
"url": KAGGLE_AUDIO_URL,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -212,7 +238,7 @@ class TestBytezChatConfig:
|
|||
{"type": "text", "text": "What kind of cat meow is this?"},
|
||||
{
|
||||
"type": "audio",
|
||||
"url": "https://storage.googleapis.com/kagglesdsdata/datasets/1736753/2838478/dataset/dataset/B_ANI01_MC_FN_SIM01_101.wav?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=databundle-worker-v2%40kaggle-161607.iam.gserviceaccount.com%2F20250711%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20250711T192905Z&X-Goog-Expires=345600&X-Goog-SignedHeaders=host&X-Goog-Signature=812b4bd6fcf9296f8e34f67664d900a81cf81a4c8a4f439ce12befc89b4bef07c2645cab20ce5ba8f6b311dffa85aa05b70b4efbe53bced50a43a5e7622ea1ee0d8cc390679cdc6a6aae2c27f75debc1ce2361c595b3c9e1b8c88e2756ffc6b4f290af7f3dfa7232dc69ccc9a2181be756e0d538250f9761a8b05ba1ac6c6b5d946f97a16aa14a5609ae62a2c4713c2077fcd34d129dbcdac6bb543ae547507b1a424e4fd09f817000943c11507e0a74c514ec212b17427b7fc9e2ce87a250db1258645e4862a4261e3790fd99c9186148ad0653acd2b6a9468adbeb94f17b5a685551037fd2cc9fe72fa405a006c0bd42d03be1e4c0dc4023ed3a77171edff3",
|
||||
"url": KAGGLE_AUDIO_URL,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -226,7 +252,7 @@ class TestBytezChatConfig:
|
|||
{"type": "text", "text": "What kind of dog is this?"},
|
||||
{
|
||||
"type": "video_url",
|
||||
"url": "https://storage.googleapis.com/kagglesdsdata/datasets/3957252/6888743/dog1.mp4?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=databundle-worker-v2%40kaggle-161607.iam.gserviceaccount.com%2F20250711%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20250711T193025Z&X-Goog-Expires=345600&X-Goog-SignedHeaders=host&X-Goog-Signature=810961d9abcbc2437954fdf19ef216deb65d3977eb354ec10af0d4644627cc6b143a5fc6450996bae1787c09d26334de7cd6ff887510a5ac2a6eed3cfcc6673a47686c84c1f2b0bf543009388d83f2cd9551ad5f72084513c6a7acd2c718849a4ebe951ccc5631bed014b0d115225c048b9f5de68673a37db24a98ad39cf3d0ba16fb764bf38eb90c78c295c21a4ddac08c3c661b65efd511ccb86bacb87a2e2a97a06f53ea1c64d5dcf274001a61bc20867802549601301d999f5a5b2e49fd444b7db860c68e1c67df6e8edd5ad97171eaafb4fa1462453924ea4d78733be411cb6b5c910d4f829cd7189c28dc1b22c8ae2a4da844a0d202e9e64bc7fb17947",
|
||||
"url": KAGGLE_VIDEO_URL,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -238,7 +264,7 @@ class TestBytezChatConfig:
|
|||
{"type": "text", "text": "What kind of dog is this?"},
|
||||
{
|
||||
"type": "video",
|
||||
"url": "https://storage.googleapis.com/kagglesdsdata/datasets/3957252/6888743/dog1.mp4?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=databundle-worker-v2%40kaggle-161607.iam.gserviceaccount.com%2F20250711%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20250711T193025Z&X-Goog-Expires=345600&X-Goog-SignedHeaders=host&X-Goog-Signature=810961d9abcbc2437954fdf19ef216deb65d3977eb354ec10af0d4644627cc6b143a5fc6450996bae1787c09d26334de7cd6ff887510a5ac2a6eed3cfcc6673a47686c84c1f2b0bf543009388d83f2cd9551ad5f72084513c6a7acd2c718849a4ebe951ccc5631bed014b0d115225c048b9f5de68673a37db24a98ad39cf3d0ba16fb764bf38eb90c78c295c21a4ddac08c3c661b65efd511ccb86bacb87a2e2a97a06f53ea1c64d5dcf274001a61bc20867802549601301d999f5a5b2e49fd444b7db860c68e1c67df6e8edd5ad97171eaafb4fa1462453924ea4d78733be411cb6b5c910d4f829cd7189c28dc1b22c8ae2a4da844a0d202e9e64bc7fb17947",
|
||||
"url": KAGGLE_VIDEO_URL,
|
||||
},
|
||||
],
|
||||
}
|
||||
0
tests/unit/llms/cerebras/__init__.py
Normal file
0
tests/unit/llms/cerebras/__init__.py
Normal file
0
tests/unit/llms/chat/__init__.py
Normal file
0
tests/unit/llms/chat/__init__.py
Normal file
|
|
@ -106,7 +106,10 @@ class TestBedrockRegionInModelPath:
|
|||
), f"modelId mismatch for {model!r}: got {model_id!r}, expected {expected_model_id!r}"
|
||||
assert (
|
||||
optional_params.get("aws_region_name") == expected_region
|
||||
), f"region mismatch for {model!r}: got {optional_params.get('aws_region_name')!r}, expected {expected_region!r}"
|
||||
), (
|
||||
f"region mismatch for {model!r}: "
|
||||
f"got {optional_params.get('aws_region_name')!r}, expected {expected_region!r}"
|
||||
)
|
||||
|
||||
def test_explicit_aws_region_name_not_overridden(self):
|
||||
"""
|
||||
0
tests/unit/llms/chatgpt/__init__.py
Normal file
0
tests/unit/llms/chatgpt/__init__.py
Normal file
0
tests/unit/llms/chatgpt/chat/__init__.py
Normal file
0
tests/unit/llms/chatgpt/chat/__init__.py
Normal file
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue