Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions litellm/integrations/otel/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from contextlib import contextmanager
from datetime import datetime
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, Final, cast
from typing import TYPE_CHECKING, Final, cast

from opentelemetry.context import Context, attach, get_current
from opentelemetry.sdk._logs import LoggerProvider
Expand All @@ -21,6 +21,7 @@
use_span,
)
from opentelemetry.trace import TracerProvider as ApiTracerProvider
from typing_extensions import TypedDict, Unpack

import litellm
from litellm._logging import verbose_logger
Expand Down Expand Up @@ -140,6 +141,10 @@ def _request_trace_links(context: Context | None) -> tuple[Link, ...] | None:
return (Link(anchor),) if anchor.is_valid else None


class _CustomLoggerOptions(TypedDict, total=False, extra_items=object):
pass


class _LLMCallSpan:
"""The state carried from the ``pre_call`` boundary to span close.

Expand Down Expand Up @@ -179,7 +184,7 @@ def __init__(
tracer_provider: TracerProvider | None = None,
logger_provider: LoggerProvider | None = None,
meter_provider: "MeterProvider | None" = None,
**kwargs: Any,
**kwargs: Unpack[_CustomLoggerOptions],
) -> None:
super().__init__(**kwargs)
self.config: OpenTelemetryV2Config = config or OpenTelemetryV2Config(**kwargs)
Expand Down
5 changes: 3 additions & 2 deletions litellm/llms/bedrock/chat/agentcore/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import json
from collections.abc import AsyncGenerator
from typing import TYPE_CHECKING, Any, Final, Optional, Union, cast
from typing import TYPE_CHECKING, Any, Final, Optional, Union
from urllib.parse import quote

import httpx
Expand All @@ -31,6 +31,7 @@
from litellm.types.utils import (
Choices,
Delta,
LlmProviders,
Message,
ModelResponse,
ModelResponseStream,
Expand Down Expand Up @@ -872,7 +873,7 @@ async def get_async_custom_stream_wrapper(
)

if client is None or not isinstance(client, AsyncHTTPHandler):
client = get_async_httpx_client(llm_provider=cast(Any, "bedrock"), params={})
client = get_async_httpx_client(llm_provider=LlmProviders.BEDROCK, params={})

verbose_logger.debug("Making async streaming request to: %s", api_base)

Expand Down
11 changes: 8 additions & 3 deletions litellm/proxy/guardrails/guardrail_hooks/alice/alice.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@
from itertools import islice
from typing import (
TYPE_CHECKING,
Any, # noqa: TID251 # **kwargs forwards verbatim to CustomGuardrail.__init__; see ruff-strict.toml
Final,
Literal,
Optional,
)

import httpx
from typing_extensions import NotRequired, ReadOnly, TypedDict
from typing_extensions import NotRequired, ReadOnly, TypedDict, Unpack

from litellm._logging import verbose_proxy_logger
from litellm.exceptions import GuardrailRaisedException, Timeout
Expand Down Expand Up @@ -92,6 +91,10 @@ class AliceVerdict(TypedDict):
replacements: ReadOnly[NotRequired["tuple[AliceReplacement, ...]"]]


class _CustomGuardrailOptions(TypedDict, total=False, extra_items=object):
pass


class AliceGuardrailMissingSecrets(Exception):
"""Raised when the Alice API key is not configured."""

Expand Down Expand Up @@ -144,7 +147,9 @@ def __init__(
api_key: str | None = None,
api_base: str | None = None,
unreachable_fallback: Literal["fail_closed", "fail_open"] = "fail_closed",
**kwargs: Any, # kwargs-ok: forwarded verbatim to CustomGuardrail.__init__, whose param list is wide and evolving
**kwargs: Unpack[ # kwargs-ok: forwarded verbatim to CustomGuardrail.__init__, whose param list is wide and evolving
_CustomGuardrailOptions
],
) -> None:
self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback)

Expand Down
10 changes: 7 additions & 3 deletions litellm/proxy/guardrails/guardrail_hooks/grayswan/grayswan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import os
import time
from typing import TYPE_CHECKING, Any, Final, Literal, Optional, Protocol
from typing import TYPE_CHECKING, Final, Literal, Optional, Protocol

from fastapi import HTTPException
from typing_extensions import NotRequired, ReadOnly, TypedDict
from typing_extensions import NotRequired, ReadOnly, TypedDict, Unpack

from litellm._logging import verbose_proxy_logger
from litellm.integrations.custom_guardrail import (
Expand Down Expand Up @@ -38,6 +38,10 @@ class _GraySwanMonitorResponse(TypedDict):
ipi: ReadOnly[NotRequired[bool | None]]


class _CustomGuardrailOptions(TypedDict, total=False, extra_items=object):
pass


class _GraySwanMonitorHTTPResponse(Protocol):
def raise_for_status(self) -> object: ...

Expand Down Expand Up @@ -103,7 +107,7 @@ def __init__(
streaming_sampling_rate: int = 5,
fail_open: bool | None = True,
guardrail_timeout: float | None = 30.0,
**kwargs: Any,
**kwargs: Unpack[_CustomGuardrailOptions],
) -> None:
self.async_handler: _GraySwanMonitorHTTPClient = get_async_httpx_client(
llm_provider=httpxSpecialProvider.GuardrailCallback
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
"""

import os
from typing import TYPE_CHECKING, Any, Final, Literal, Optional, TypedDict
from typing import TYPE_CHECKING, Final, Literal, Optional, TypedDict

from typing_extensions import ReadOnly
from typing_extensions import ReadOnly, Unpack
from typing_extensions import TypedDict as ExtraItemsTypedDict

from litellm._logging import verbose_proxy_logger
from litellm.exceptions import GuardrailRaisedException
Expand Down Expand Up @@ -53,6 +54,10 @@ class PromptGuardHTTPView(TypedDict):
guard_response: ReadOnly[PromptGuardGuardAPIResponse]


class _CustomGuardrailOptions(ExtraItemsTypedDict, total=False, extra_items=object):
supported_event_hooks: ReadOnly[list[GuardrailEventHooks] | None]


class PromptGuardMissingCredentials(Exception):
pass

Expand All @@ -63,7 +68,7 @@ def __init__(
api_key: str | None = None,
api_base: str | None = None,
block_on_error: bool | None = None,
**kwargs: Any,
**kwargs: Unpack[_CustomGuardrailOptions],
) -> None:
self.api_key = api_key or os.environ.get(
"PROMPTGUARD_API_KEY",
Expand Down Expand Up @@ -92,9 +97,12 @@ def __init__(
llm_provider=httpxSpecialProvider.GuardrailCallback,
)

kwargs.setdefault("supported_event_hooks", list(self.get_supported_event_hooks()))
options: Final[_CustomGuardrailOptions] = {
"supported_event_hooks": list(self.get_supported_event_hooks()),
**kwargs,
}

super().__init__(**kwargs)
super().__init__(**options)

@staticmethod
def get_config_model() -> type["GuardrailConfigModel"] | None:
Expand Down
11 changes: 6 additions & 5 deletions litellm/proxy/guardrails/guardrail_hooks/singulr/singulr.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
)
from litellm.proxy._types import UserAPIKeyAuth
from litellm.types.guardrails import GuardrailEventHooks
from litellm.types.llms.openai import AllMessageValues
from litellm.types.proxy.guardrails.guardrail_hooks.base import (
GuardrailConfigModel,
)
Expand All @@ -40,7 +41,7 @@
_DEFAULT_API_BASE: Final = "http://localhost:8003"
_GUARD_ENDPOINT: Final = "/api/v1/ai-gateway/litellm-v2"
_DEFAULT_TIMEOUT: Final = 30.0
_EMPTY_MAPPING: Final[Mapping[str, Any]] = MappingProxyType({})
_EMPTY_MAPPING: Final[Mapping[str, Mapping[str, object]]] = MappingProxyType({})
_MCP_MODEL_PREFIX: Final = "MCP:"


Expand Down Expand Up @@ -159,7 +160,7 @@ def _build_metadata(cls, request_data: Mapping[str, Any]) -> Mapping[str, str] |
return {key: value for key, value in resolved if value} # mutable-ok: short-lived JSON payload dict

@staticmethod
def _build_user_message(text: str) -> Mapping[str, Any]:
def _build_user_message(text: str) -> Mapping[str, str]:
return {"role": "user", "content": text} # mutable-ok: short-lived JSON payload dict

def _build_headers(self) -> Mapping[str, str]:
Expand Down Expand Up @@ -224,7 +225,7 @@ async def _apply_guardrail_on_request(
self,
inputs: GenericGuardrailAPIInputs,
texts: Sequence[str],
structured_messages: Sequence[Any],
structured_messages: Sequence[AllMessageValues],
request_data: Mapping[str, Any],
) -> GenericGuardrailAPIInputs:
messages: Final = (
Expand Down Expand Up @@ -271,12 +272,12 @@ def _mcp_tool_name(request_data: Mapping[str, Any]) -> str | None:
return request_data.get("mcp_tool_name") or request_data.get("name")

@staticmethod
def _mcp_arguments(request_data: Mapping[str, Any]) -> object:
def _mcp_arguments(request_data: Mapping[str, object]) -> object:
arguments: Final = request_data.get("mcp_arguments")
return arguments if arguments is not None else request_data.get("arguments")

@staticmethod
def _is_mcp_call(request_data: Mapping[str, Any], logging_obj: LiteLLMLoggingObj | None) -> bool:
def _is_mcp_call(request_data: Mapping[str, object], logging_obj: LiteLLMLoggingObj | None) -> bool:
call_type: Final = logging_obj.call_type if logging_obj is not None else request_data.get("call_type")
if call_type is not None:
return call_type == CallTypes.call_mcp_tool.value
Expand Down
Loading