mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
fix(mcp): resolve SDK2 wire-shape regressions in guardrail, arize, and benchmark paths
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
7410d00d2e
commit
4a7d8bbffa
4 changed files with 31 additions and 7 deletions
4
.github/workflows/codspeed.yml
vendored
4
.github/workflows/codspeed.yml
vendored
|
|
@ -69,7 +69,7 @@ jobs:
|
|||
uv run --frozen --no-default-groups
|
||||
--with pytest==8.3.5
|
||||
--with pytest-codspeed==4.3.0
|
||||
--with "mcp>=1.26.0,<2.0"
|
||||
--with "mcp>=2.2.0,<3.0"
|
||||
--with "a2a-sdk>=1.1.0,<2.0"
|
||||
pytest
|
||||
-p pytest_codspeed.plugin
|
||||
|
|
@ -86,7 +86,7 @@ jobs:
|
|||
uv run --frozen --no-default-groups
|
||||
--with pytest==8.3.5
|
||||
--with pytest-codspeed==4.3.0
|
||||
--with "mcp>=1.26.0,<2.0"
|
||||
--with "mcp>=2.2.0,<3.0"
|
||||
--with "a2a-sdk>=1.1.0,<2.0"
|
||||
pytest
|
||||
-p pytest_codspeed.plugin
|
||||
|
|
|
|||
|
|
@ -1139,7 +1139,10 @@ def _set_mcp_tool_output(span: "Span", coerced_response_obj: object) -> None:
|
|||
safe_set_attribute(span, SpanAttributes.OUTPUT_MIME_TYPE, OpenInferenceMimeTypeValues.TEXT.value)
|
||||
return
|
||||
|
||||
structured: Final[object] = coerced_response_obj.get("structuredContent")
|
||||
structured: Final[object] = coerced_response_obj.get(
|
||||
"structured_content",
|
||||
coerced_response_obj.get("structuredContent"), # pyright: ignore[reportUnknownMemberType] # tolerant dual-spelling lookup on untyped payloads
|
||||
)
|
||||
payload: Final[object] = content if content else structured if structured is not None else content
|
||||
if payload is None:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ while preserving the existing public import path.
|
|||
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Final, Optional
|
||||
from typing import TYPE_CHECKING, Final, Optional, cast
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
|
@ -45,6 +45,24 @@ def _serialize_mcp_content_item(item: object) -> dict[str, object]:
|
|||
return {"type": "text", "text": str(item)}
|
||||
|
||||
|
||||
def _coerce_pair_list_source(source: object) -> object:
|
||||
if not isinstance(source, list):
|
||||
return source
|
||||
try:
|
||||
return dict(cast("Sequence[tuple[str, object]]", source)) # pyright: ignore[reportUnknownArgumentType] # response_obj arrives untyped; dict() rejects non-pair shapes
|
||||
except (TypeError, ValueError):
|
||||
return source
|
||||
|
||||
|
||||
def _source_field(source: object, key: str, snake_key: str) -> object:
|
||||
if isinstance(source, dict):
|
||||
for candidate in (key, snake_key):
|
||||
if candidate in source:
|
||||
return source[candidate] # pyright: ignore[reportUnknownVariableType] # dict-shaped sources arrive untyped
|
||||
return None
|
||||
return getattr(source, snake_key, None)
|
||||
|
||||
|
||||
class _CiscoAIDefenseMcpMixin:
|
||||
"""MCP-specific instance methods for ``CiscoAIDefenseGuardrail``.
|
||||
|
||||
|
|
@ -508,9 +526,10 @@ class _CiscoAIDefenseMcpMixin:
|
|||
content: Sequence[object],
|
||||
source: object = None,
|
||||
) -> dict[str, object]:
|
||||
source_map: Final[object] = _coerce_pair_list_source(source)
|
||||
result: Final[dict[str, object]] = {"content": [_serialize_mcp_content_item(item) for item in content]}
|
||||
for key, snake_key in (("structuredContent", "structured_content"), ("isError", "is_error")):
|
||||
value = source.get(key) if isinstance(source, dict) else getattr(source, snake_key, None)
|
||||
value = _source_field(source_map, key, snake_key)
|
||||
if value is not None and (key != "isError" or isinstance(value, bool)):
|
||||
result[key] = value
|
||||
return result
|
||||
|
|
@ -551,7 +570,7 @@ class _CiscoAIDefenseMcpMixin:
|
|||
and all(isinstance(item, tuple) and len(item) == 2 and isinstance(item[0], str) for item in response_obj)
|
||||
):
|
||||
for index, item in enumerate(response_obj):
|
||||
if item[0] == "structuredContent":
|
||||
if item[0] in ("structuredContent", "structured_content"):
|
||||
response_obj[index] = (item[0], replacement)
|
||||
replaced = True
|
||||
elif hasattr(response_obj, "structured_content"):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
import re
|
||||
from collections.abc import Awaitable, Callable, Mapping
|
||||
|
|
@ -6,13 +8,13 @@ from typing import TYPE_CHECKING, Any, Final, Literal
|
|||
from urllib.parse import urlsplit
|
||||
|
||||
import httpx
|
||||
import httpx2
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from litellm.types.llms.base import HiddenParams
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import httpx2
|
||||
from mcp.types import EmbeddedResource as MCPEmbeddedResource
|
||||
from mcp.types import ImageContent as MCPImageContent
|
||||
from mcp.types import TextContent as MCPTextContent
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue