mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
added support for metadata
This commit is contained in:
parent
f0efa5777d
commit
ba20699312
6 changed files with 96 additions and 48 deletions
|
|
@ -33,24 +33,17 @@ commands:
|
|||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r requirements.txt
|
||||
pip install "pytest-mock==3.12.0"
|
||||
pip install "pytest==7.3.1"
|
||||
pip install "pytest-retry==1.6.3"
|
||||
pip install "pytest-cov==5.0.0"
|
||||
pip install "pytest-asyncio==0.21.1"
|
||||
pip install "respx==0.22.0"
|
||||
pip install "hypercorn==0.17.3"
|
||||
pip install "pydantic==2.11.0"
|
||||
pip install "mcp==1.25.0"
|
||||
pip install "requests-mock>=1.12.1"
|
||||
pip install "responses==0.25.7"
|
||||
pip install "pytest-xdist==3.6.1"
|
||||
pip install "pytest-timeout==2.2.0"
|
||||
pip install "semantic_router==0.1.10"
|
||||
pip install "fastapi-offline==1.7.3"
|
||||
pip install "a2a"
|
||||
python -m pip install --upgrade pip uv
|
||||
# Use uv for the heavy requirements.txt (10-100x faster than pip)
|
||||
uv pip install --system -r requirements.txt
|
||||
# Use pip for test deps (small set, avoids uv strict-resolution
|
||||
# conflicts with transitive dep pins like openai<2 and pydantic>=2.11.5)
|
||||
pip install "pytest-mock==3.12.0" "pytest==7.3.1" "pytest-retry==1.6.3" \
|
||||
"pytest-asyncio==0.21.1" "respx==0.22.0" "hypercorn==0.17.3" \
|
||||
"pydantic==2.11.0" "mcp==1.26.0" "requests-mock>=1.12.1" \
|
||||
"responses==0.25.7" "pytest-xdist==3.6.1" "pytest-timeout==2.2.0" \
|
||||
"pytest-cov==5.0.0" "semantic_router==0.1.10" "fastapi-offline==1.7.3" \
|
||||
"a2a" "parameterized>=0.9.0"
|
||||
- setup_litellm_enterprise_pip
|
||||
- save_cache:
|
||||
paths:
|
||||
|
|
@ -1367,7 +1360,8 @@ jobs:
|
|||
pip install "pytest-asyncio==0.21.1"
|
||||
pip install "respx==0.22.0"
|
||||
pip install "pydantic==2.11.0"
|
||||
pip install "mcp==1.25.0"
|
||||
pip install "mcp==1.26.0"
|
||||
pip install "pytest-xdist==3.6.1"
|
||||
# Run pytest and generate JUnit XML report
|
||||
- run:
|
||||
name: Run tests
|
||||
|
|
@ -1874,7 +1868,7 @@ jobs:
|
|||
pip install "respx==0.22.0"
|
||||
pip install "hypercorn==0.17.3"
|
||||
pip install "pydantic==2.11.0"
|
||||
pip install "mcp==1.25.0"
|
||||
pip install "mcp==1.26.0"
|
||||
pip install "requests-mock>=1.12.1"
|
||||
pip install "responses==0.25.7"
|
||||
pip install "pytest-xdist==3.6.1"
|
||||
|
|
@ -2234,7 +2228,7 @@ jobs:
|
|||
pip install "pytest-asyncio==0.21.1"
|
||||
pip install "pytest-cov==5.0.0"
|
||||
pip install "tomli==2.2.1"
|
||||
pip install "mcp==1.25.0"
|
||||
pip install "mcp==1.26.0"
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ google-cloud-aiplatform==1.43.0
|
|||
google-cloud-iam==2.19.1
|
||||
fastapi-sso==0.16.0
|
||||
uvloop==0.21.0
|
||||
mcp==1.25.0 # for MCP server
|
||||
mcp==1.26.0 # for MCP server
|
||||
semantic_router==0.1.10 # for auto-routing with litellm
|
||||
fastuuid==0.12.0
|
||||
responses==0.25.7 # for proxy client tests
|
||||
|
|
|
|||
|
|
@ -169,6 +169,32 @@ if MCP_AVAILABLE:
|
|||
mcp_info: Optional[MCPInfo] = None
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
def _normalize_resource_contents(contents: list) -> List[ReadResourceContents]:
|
||||
"""Normalize ResourceContents to ReadResourceContents, preserving meta (MCP 1.26.0+)."""
|
||||
normalized: List[ReadResourceContents] = []
|
||||
for content in contents:
|
||||
meta = getattr(content, "meta", None)
|
||||
if meta is None and hasattr(content, "model_dump"):
|
||||
d = content.model_dump()
|
||||
meta = d.get("meta") or d.get("_meta")
|
||||
if isinstance(content, TextResourceContents):
|
||||
normalized.append(
|
||||
ReadResourceContents(
|
||||
content=content.text,
|
||||
mime_type=content.mimeType,
|
||||
meta=meta,
|
||||
)
|
||||
)
|
||||
elif isinstance(content, BlobResourceContents):
|
||||
normalized.append(
|
||||
ReadResourceContents(
|
||||
content=content.blob,
|
||||
mime_type=None,
|
||||
meta=meta,
|
||||
)
|
||||
)
|
||||
return normalized
|
||||
|
||||
########################################################
|
||||
############ Initialize the MCP Server #################
|
||||
########################################################
|
||||
|
|
@ -623,26 +649,7 @@ if MCP_AVAILABLE:
|
|||
raw_headers=raw_headers,
|
||||
)
|
||||
|
||||
normalized_contents: List[ReadResourceContents] = []
|
||||
for content in read_resource_result.contents:
|
||||
if isinstance(content, TextResourceContents):
|
||||
text_content: TextResourceContents = content
|
||||
normalized_contents.append(
|
||||
ReadResourceContents(
|
||||
content=text_content.text,
|
||||
mime_type=text_content.mimeType,
|
||||
)
|
||||
)
|
||||
elif isinstance(content, BlobResourceContents):
|
||||
blob_content: BlobResourceContents = content
|
||||
normalized_contents.append(
|
||||
ReadResourceContents(
|
||||
content=blob_content.blob,
|
||||
mime_type=None,
|
||||
)
|
||||
)
|
||||
|
||||
return normalized_contents
|
||||
return _normalize_resource_contents(read_resource_result.contents)
|
||||
|
||||
########################################################
|
||||
############ End of MCP Server Routes ##################
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ pynacl = {version = "^1.5.0", optional = true}
|
|||
websockets = {version = "^15.0.1", optional = true}
|
||||
boto3 = { version = "^1.40.76", optional = true }
|
||||
redisvl = {version = "^0.4.1", optional = true, markers = "python_version >= '3.9' and python_version < '3.14'"}
|
||||
mcp = {version = ">=1.25.0,<2.0.0", optional = true, python = ">=3.10"}
|
||||
mcp = {version = ">=1.26.0,<2.0.0", optional = true, python = ">=3.10"}
|
||||
a2a-sdk = {version = "^0.3.22", optional = true, python = ">=3.10"}
|
||||
litellm-proxy-extras = {version = "^0.4.53", optional = true}
|
||||
rich = {version = "^13.7.1", optional = true}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ google-cloud-aiplatform==1.133.0 # for vertex ai calls
|
|||
google-cloud-iam==2.19.1 # for GCP IAM Redis authentication
|
||||
google-genai==1.37.0
|
||||
anthropic[vertex]==0.54.0
|
||||
mcp==1.25.0 ; python_version >= "3.10" # for MCP server
|
||||
mcp==1.26.0 ; python_version >= "3.10" # for MCP server
|
||||
# google-generativeai removed - deprecated, replaced by google-genai (line 21)
|
||||
async_generator==1.10.0 # for async ollama calls
|
||||
langfuse==2.59.7 # for langfuse self-hosted logging
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
|
@ -413,6 +412,57 @@ async def test_mcp_read_resource_success():
|
|||
assert result is read_result
|
||||
|
||||
|
||||
def test_normalize_resource_contents_passes_metadata():
|
||||
"""Test that _normalize_resource_contents preserves meta from ResourceContents (MCP 1.26.0+)."""
|
||||
try:
|
||||
from litellm.proxy._experimental.mcp_server.server import (
|
||||
_normalize_resource_contents,
|
||||
)
|
||||
except ImportError:
|
||||
pytest.skip("MCP server not available")
|
||||
|
||||
meta = {"version": "1.0", "source": "test"}
|
||||
contents = [
|
||||
TextResourceContents(
|
||||
uri="https://example.com/resource",
|
||||
text="hello world",
|
||||
mimeType="text/plain",
|
||||
meta=meta,
|
||||
)
|
||||
]
|
||||
|
||||
result = _normalize_resource_contents(contents)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].content == "hello world"
|
||||
assert result[0].mime_type == "text/plain"
|
||||
assert result[0].meta == meta
|
||||
|
||||
|
||||
def test_normalize_resource_contents_without_metadata():
|
||||
"""Test that _normalize_resource_contents works when meta is absent (backward compat)."""
|
||||
try:
|
||||
from litellm.proxy._experimental.mcp_server.server import (
|
||||
_normalize_resource_contents,
|
||||
)
|
||||
except ImportError:
|
||||
pytest.skip("MCP server not available")
|
||||
|
||||
contents = [
|
||||
TextResourceContents(
|
||||
uri="https://example.com/resource",
|
||||
text="hello",
|
||||
mimeType="text/plain",
|
||||
)
|
||||
]
|
||||
|
||||
result = _normalize_resource_contents(contents)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].content == "hello"
|
||||
assert result[0].meta is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mcp_read_resource_multiple_servers_error():
|
||||
try:
|
||||
|
|
@ -707,8 +757,6 @@ async def test_concurrent_initialize_session_managers():
|
|||
"""Test that concurrent calls to initialize_session_managers don't cause race conditions."""
|
||||
try:
|
||||
from litellm.proxy._experimental.mcp_server.server import (
|
||||
_INITIALIZATION_LOCK,
|
||||
_SESSION_MANAGERS_INITIALIZED,
|
||||
initialize_session_managers,
|
||||
)
|
||||
except ImportError:
|
||||
|
|
@ -1426,7 +1474,6 @@ async def test_list_tools_with_team_tool_permissions_inheritance():
|
|||
)
|
||||
from litellm.proxy._types import (
|
||||
LiteLLM_ObjectPermissionTable,
|
||||
LiteLLM_TeamTable,
|
||||
UserAPIKeyAuth,
|
||||
)
|
||||
except ImportError:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue