Merge remote-tracking branch 'origin/main' into litellm_mcp_listed_tool_metadata

This commit is contained in:
yucheng 2026-10-01 18:24:14 +00:00
commit 4fc40056ae
371 changed files with 421 additions and 119 deletions

View file

@ -187,7 +187,7 @@ jobs:
- shard: proxy-server
artifact-name: proxy-server
test-path: "tests/test_litellm/proxy/proxy_server"
test-path: "tests/unit/proxy/proxy_server"
workers: 4
reruns: 2
timeout-minutes: 60
@ -196,17 +196,106 @@ jobs:
- shard: proxy-infra
artifact-name: proxy-infra
test-path: >-
tests/test_litellm/proxy/db
tests/test_litellm/proxy/middleware
tests/test_litellm/proxy/spend_tracking
tests/unit/proxy/db
--ignore=tests/unit/proxy/db/db_transaction_queue/test_e2e_pod_lock_manager.py
--ignore=tests/unit/proxy/db/test_update_daily_tag_spend.py
tests/unit/proxy/middleware
--ignore=tests/unit/proxy/middleware/test_request_size_limit_middleware.py
tests/unit/proxy/spend_tracking
--ignore=tests/unit/proxy/spend_tracking/test_search_api_logging.py
tests/unit/proxy/pass_through_endpoints
tests/test_litellm/proxy/pass_through_endpoints
tests/test_litellm/proxy/_experimental
tests/test_litellm/proxy/experimental
tests/test_litellm/proxy/common_utils
tests/test_litellm/proxy/enterprise_billing
tests/test_litellm/proxy/types_utils
tests/test_litellm/proxy/logging_endpoints
tests/test_litellm/proxy/test_*.py
tests/unit/proxy/_experimental
--ignore=tests/unit/proxy/_experimental/mcp_server
tests/unit/proxy/experimental
tests/unit/proxy/common_utils
--ignore=tests/unit/proxy/common_utils/test_cache_aware_routing.py
--ignore=tests/unit/proxy/common_utils/test_check_batch_cost.py
--ignore=tests/unit/proxy/common_utils/test_check_responses_cost.py
--ignore=tests/unit/proxy/common_utils/test_proxy_encrypt_decrypt.py
--ignore=tests/unit/proxy/common_utils/test_realtime_cache.py
tests/unit/proxy/enterprise_billing
tests/unit/proxy/types_utils
tests/unit/proxy/logging_endpoints
tests/unit/proxy/test__types.py
tests/unit/proxy/test_aiohttp_cleanup_closed.py
tests/unit/proxy/test_aiohttp_session_recovery.py
tests/unit/proxy/test_api_key_masking_in_errors.py
tests/unit/proxy/test_audio_speech_prometheus_hooks.py
tests/unit/proxy/test_batch_expiry.py
tests/unit/proxy/test_batch_metadata_none_fix.py
tests/unit/proxy/test_batch_retrieve_bedrock.py
tests/unit/proxy/test_batch_x_litellm_model_encoding.py
tests/unit/proxy/test_blocked_response_usage.py
tests/unit/proxy/test_body_snapshot_callback_params.py
tests/unit/proxy/test_budget_reservation.py
tests/unit/proxy/test_bug_report_config.py
tests/unit/proxy/test_caching_routes.py
tests/unit/proxy/test_chat_completion_metadata.py
tests/unit/proxy/test_claude_code_marketplace.py
tests/unit/proxy/test_collector.py
tests/unit/proxy/test_common_request_processing.py
tests/unit/proxy/test_component_allowlists.py
tests/unit/proxy/test_conftest.py
tests/unit/proxy/test_cors_config.py
tests/unit/proxy/test_custom_proxy.py
tests/unit/proxy/test_dynamic_mcp_route.py
tests/unit/proxy/test_empty_model_list.py
tests/unit/proxy/test_enforce_user_param.py
tests/unit/proxy/test_fallback_management_endpoints.py
tests/unit/proxy/test_fastapi_offline_routes.py
tests/unit/proxy/test_filter_models_by_team_access_group.py
tests/unit/proxy/test_health_check_functions.py
tests/unit/proxy/test_health_check_max_tokens.py
tests/unit/proxy/test_init_litellm_callbacks.py
tests/unit/proxy/test_langfuse_passthrough_security.py
tests/unit/proxy/test_lazy_openapi_snapshot.py
tests/unit/proxy/test_litellm_pre_call_utils.py
tests/unit/proxy/test_max_budget_env_var.py
tests/unit/proxy/test_mcp_asgi_response.py
tests/unit/proxy/test_model_based_routing_files_batches.py
tests/unit/proxy/test_model_deprecations_endpoint.py
tests/unit/proxy/test_model_dump_with_preserved_fields.py
tests/unit/proxy/test_model_id_header_propagation.py
tests/unit/proxy/test_model_info_default_limits.py
tests/unit/proxy/test_model_level_guardrails.py
tests/unit/proxy/test_model_list_aliases.py
tests/unit/proxy/test_model_list_callback_filter.py
tests/unit/proxy/test_model_list_discoverable.py
tests/unit/proxy/test_model_list_healthy_only.py
tests/unit/proxy/test_modify_response_streaming_passthrough.py
tests/unit/proxy/test_native_compaction.py
tests/unit/proxy/test_openai_ws_passthrough_routes.py
tests/unit/proxy/test_openapi_schema_validation.py
tests/unit/proxy/test_plugin_routes.py
tests/unit/proxy/test_pointfive_dashboard_config.py
tests/unit/proxy/test_pointfive_ui_callback.py
tests/unit/proxy/test_pricing_field_strip.py
tests/unit/proxy/test_prisma_engine_watchdog.py
tests/unit/proxy/test_prisma_migration.py
tests/unit/proxy/test_prometheus_cleanup.py
tests/unit/proxy/test_prometheus_metrics_server.py
tests/unit/proxy/test_provider_url_destination_guard.py
tests/unit/proxy/test_proxy_cli.py
tests/unit/proxy/test_proxy_logging_hook_detection.py
tests/unit/proxy/test_proxy_types.py
tests/unit/proxy/test_pyroscope.py
tests/unit/proxy/test_read_model_list.py
tests/unit/proxy/test_redis_auth_cache_flag.py
tests/unit/proxy/test_response_model_sanitization.py
tests/unit/proxy/test_route_a2a_models.py
tests/unit/proxy/test_route_llm_request.py
tests/unit/proxy/test_route_priority.py
tests/unit/proxy/test_sensitive_route_auth.py
tests/unit/proxy/test_shared_health_check.py
tests/unit/proxy/test_spend_log_cleanup.py
tests/unit/proxy/test_swagger_chat_completions.py
tests/unit/proxy/test_team_member_update.py
tests/unit/proxy/test_team_org_move.py
tests/unit/proxy/test_tools_allowlist_enforcement.py
tests/unit/proxy/test_tracing_endpoints.py
tests/unit/proxy/test_update_llm_router_resilience.py
tests/unit/proxy/test_zerobus_dashboard_config.py
tests/unit/proxy/test_proxy_server_endpoints_and_startup.py
tests/unit/proxy/test_proxy_utils_model_creation_and_error_logging.py
unit-flag: proxy-infra

2
.gitignore vendored
View file

@ -104,7 +104,7 @@ litellm_config.yaml
.cursor
litellm/proxy/to_delete_loadtest_work/*
update_model_cost_map.py
tests/test_litellm/proxy/_experimental/mcp_server/test_mcp_server_manager.py
tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py
scripts/test_vertex_ai_search.py
LAZY_LOADING_IMPROVEMENTS.md
STABILIZATION_TODO.md

View file

@ -98,7 +98,7 @@ Add your tests to the [`tests/unit/` directory](https://github.com/BerriAI/litel
The `tests/unit/` directory follows the same structure as `litellm/`:
- `litellm/proxy/caching_routes.py` → `tests/test_litellm/proxy/test_caching_routes.py`
- `litellm/proxy/caching_routes.py` → `tests/unit/proxy/test_caching_routes.py`
- `litellm/utils.py` → `tests/unit/test_utils.py`
### Example Test

View file

@ -324,10 +324,91 @@ test-unit-proxy-guardrails: install-test-deps
$(UV_RUN) pytest tests/unit/proxy/guardrails tests/unit/proxy/management_endpoints tests/unit/proxy/management_helpers --tb=short -vv -n 4 --durations=20
test-unit-proxy-core: install-test-deps
$(UV_RUN) pytest tests/unit/proxy/auth tests/unit/proxy/client tests/test_litellm/proxy/db tests/unit/proxy/hooks tests/unit/proxy/policy_engine --tb=short -vv -n 4 --durations=20
$(UV_RUN) pytest tests/unit/proxy/auth tests/unit/proxy/client tests/unit/proxy/db tests/unit/proxy/hooks tests/unit/proxy/policy_engine --ignore=tests/unit/proxy/db/db_transaction_queue/test_e2e_pod_lock_manager.py --ignore=tests/unit/proxy/db/test_update_daily_tag_spend.py --tb=short -vv -n 4 --durations=20
PROXY_INFRA_ROOT_TESTS := \
tests/unit/proxy/test__types.py \
tests/unit/proxy/test_aiohttp_cleanup_closed.py \
tests/unit/proxy/test_aiohttp_session_recovery.py \
tests/unit/proxy/test_api_key_masking_in_errors.py \
tests/unit/proxy/test_audio_speech_prometheus_hooks.py \
tests/unit/proxy/test_batch_expiry.py \
tests/unit/proxy/test_batch_metadata_none_fix.py \
tests/unit/proxy/test_batch_retrieve_bedrock.py \
tests/unit/proxy/test_batch_x_litellm_model_encoding.py \
tests/unit/proxy/test_blocked_response_usage.py \
tests/unit/proxy/test_body_snapshot_callback_params.py \
tests/unit/proxy/test_budget_reservation.py \
tests/unit/proxy/test_bug_report_config.py \
tests/unit/proxy/test_caching_routes.py \
tests/unit/proxy/test_chat_completion_metadata.py \
tests/unit/proxy/test_claude_code_marketplace.py \
tests/unit/proxy/test_collector.py \
tests/unit/proxy/test_common_request_processing.py \
tests/unit/proxy/test_component_allowlists.py \
tests/unit/proxy/test_conftest.py \
tests/unit/proxy/test_cors_config.py \
tests/unit/proxy/test_custom_proxy.py \
tests/unit/proxy/test_dynamic_mcp_route.py \
tests/unit/proxy/test_empty_model_list.py \
tests/unit/proxy/test_enforce_user_param.py \
tests/unit/proxy/test_fallback_management_endpoints.py \
tests/unit/proxy/test_fastapi_offline_routes.py \
tests/unit/proxy/test_filter_models_by_team_access_group.py \
tests/unit/proxy/test_health_check_functions.py \
tests/unit/proxy/test_health_check_max_tokens.py \
tests/unit/proxy/test_init_litellm_callbacks.py \
tests/unit/proxy/test_langfuse_passthrough_security.py \
tests/unit/proxy/test_lazy_openapi_snapshot.py \
tests/unit/proxy/test_litellm_pre_call_utils.py \
tests/unit/proxy/test_max_budget_env_var.py \
tests/unit/proxy/test_mcp_asgi_response.py \
tests/unit/proxy/test_model_based_routing_files_batches.py \
tests/unit/proxy/test_model_deprecations_endpoint.py \
tests/unit/proxy/test_model_dump_with_preserved_fields.py \
tests/unit/proxy/test_model_id_header_propagation.py \
tests/unit/proxy/test_model_info_default_limits.py \
tests/unit/proxy/test_model_level_guardrails.py \
tests/unit/proxy/test_model_list_aliases.py \
tests/unit/proxy/test_model_list_callback_filter.py \
tests/unit/proxy/test_model_list_discoverable.py \
tests/unit/proxy/test_model_list_healthy_only.py \
tests/unit/proxy/test_modify_response_streaming_passthrough.py \
tests/unit/proxy/test_native_compaction.py \
tests/unit/proxy/test_openai_ws_passthrough_routes.py \
tests/unit/proxy/test_openapi_schema_validation.py \
tests/unit/proxy/test_plugin_routes.py \
tests/unit/proxy/test_pointfive_dashboard_config.py \
tests/unit/proxy/test_pointfive_ui_callback.py \
tests/unit/proxy/test_pricing_field_strip.py \
tests/unit/proxy/test_prisma_engine_watchdog.py \
tests/unit/proxy/test_prisma_migration.py \
tests/unit/proxy/test_prometheus_cleanup.py \
tests/unit/proxy/test_prometheus_metrics_server.py \
tests/unit/proxy/test_provider_url_destination_guard.py \
tests/unit/proxy/test_proxy_cli.py \
tests/unit/proxy/test_proxy_logging_hook_detection.py \
tests/unit/proxy/test_proxy_types.py \
tests/unit/proxy/test_pyroscope.py \
tests/unit/proxy/test_read_model_list.py \
tests/unit/proxy/test_redis_auth_cache_flag.py \
tests/unit/proxy/test_response_model_sanitization.py \
tests/unit/proxy/test_route_a2a_models.py \
tests/unit/proxy/test_route_llm_request.py \
tests/unit/proxy/test_route_priority.py \
tests/unit/proxy/test_sensitive_route_auth.py \
tests/unit/proxy/test_shared_health_check.py \
tests/unit/proxy/test_spend_log_cleanup.py \
tests/unit/proxy/test_swagger_chat_completions.py \
tests/unit/proxy/test_team_member_update.py \
tests/unit/proxy/test_team_org_move.py \
tests/unit/proxy/test_tools_allowlist_enforcement.py \
tests/unit/proxy/test_tracing_endpoints.py \
tests/unit/proxy/test_update_llm_router_resilience.py \
tests/unit/proxy/test_zerobus_dashboard_config.py
test-unit-proxy-misc: install-test-deps
$(UV_RUN) pytest tests/test_litellm/proxy/_experimental tests/unit/proxy/agent_endpoints tests/unit/proxy/anthropic_endpoints tests/test_litellm/proxy/common_utils tests/unit/proxy/discovery_endpoints tests/test_litellm/proxy/experimental tests/unit/proxy/google_endpoints tests/unit/proxy/health_endpoints tests/unit/proxy/image_endpoints tests/test_litellm/proxy/middleware tests/unit/proxy/openai_files_endpoint tests/test_litellm/proxy/pass_through_endpoints tests/unit/proxy/prompts tests/unit/proxy/public_endpoints tests/unit/proxy/response_api_endpoints tests/unit/proxy/shutdown tests/test_litellm/proxy/spend_tracking tests/unit/proxy/ui_crud_endpoints tests/unit/proxy/vector_store_endpoints tests/test_litellm/proxy/test_*.py tests/unit/proxy/test_proxy_server_endpoints_and_startup.py tests/unit/proxy/test_proxy_utils_model_creation_and_error_logging.py tests/unit/proxy/_experimental/mcp_server/test_mcp_server_tool_calls_and_headers.py --ignore=tests/unit/proxy/google_endpoints/test_gemini_agents_endpoints.py --ignore=tests/unit/proxy/google_endpoints/test_google_endpoint_routing.py --ignore=tests/unit/proxy/google_endpoints/test_google_gemini_proxy_request.py --ignore=tests/unit/proxy/public_endpoints/test_blog_posts_endpoint.py --tb=short -vv -n 4 --durations=20
$(UV_RUN) pytest tests/unit/proxy/agent_endpoints tests/unit/proxy/anthropic_endpoints tests/unit/proxy/common_utils --ignore=tests/unit/proxy/common_utils/test_cache_aware_routing.py --ignore=tests/unit/proxy/common_utils/test_check_batch_cost.py --ignore=tests/unit/proxy/common_utils/test_check_responses_cost.py --ignore=tests/unit/proxy/common_utils/test_proxy_encrypt_decrypt.py --ignore=tests/unit/proxy/common_utils/test_realtime_cache.py tests/unit/proxy/discovery_endpoints tests/unit/proxy/experimental tests/unit/proxy/google_endpoints tests/unit/proxy/health_endpoints tests/unit/proxy/image_endpoints tests/unit/proxy/middleware --ignore=tests/unit/proxy/middleware/test_request_size_limit_middleware.py tests/unit/proxy/openai_files_endpoint tests/unit/proxy/pass_through_endpoints tests/test_litellm/proxy/pass_through_endpoints tests/unit/proxy/prompts tests/unit/proxy/public_endpoints tests/unit/proxy/response_api_endpoints tests/unit/proxy/shutdown tests/unit/proxy/spend_tracking --ignore=tests/unit/proxy/spend_tracking/test_search_api_logging.py tests/unit/proxy/ui_crud_endpoints tests/unit/proxy/vector_store_endpoints $(PROXY_INFRA_ROOT_TESTS) tests/unit/proxy/test_proxy_server_endpoints_and_startup.py tests/unit/proxy/test_proxy_utils_model_creation_and_error_logging.py tests/unit/proxy/_experimental/mcp_server/test_mcp_server_tool_calls_and_headers.py --ignore=tests/unit/proxy/google_endpoints/test_gemini_agents_endpoints.py --ignore=tests/unit/proxy/google_endpoints/test_google_endpoint_routing.py --ignore=tests/unit/proxy/google_endpoints/test_google_gemini_proxy_request.py --ignore=tests/unit/proxy/public_endpoints/test_blog_posts_endpoint.py --tb=short -vv -n 4 --durations=20
test-unit-integrations: install-test-deps
$(UV_RUN) pytest tests/unit/integrations --tb=short -vv -n 4 --durations=20

View file

@ -6,7 +6,7 @@ Run from repo root:
uv run python scripts/test_tool_allowlist_script.py
Or run the unit tests:
uv run pytest tests/test_litellm/proxy/test_tools_allowlist_enforcement.py -v
uv run pytest tests/unit/proxy/test_tools_allowlist_enforcement.py -v
"""
import asyncio
@ -148,7 +148,7 @@ def main():
asyncio.run(test_check_tools_allowlist())
print("Done. For full unit tests run:")
print(
" uv run pytest tests/test_litellm/proxy/test_tools_allowlist_enforcement.py -v"
" uv run pytest tests/unit/proxy/test_tools_allowlist_enforcement.py -v"
)

View file

@ -6,7 +6,7 @@ that would catch a regression.
Companion: live suite `test_spend_tracking_e2e.py` + route breadth
`test_spend_routes.py` (this directory). Offline regression suite:
`tests/test_litellm/proxy/spend_tracking/`. Reference PR: BerriAI/litellm#29956.
`tests/unit/proxy/spend_tracking/`. Reference PR: BerriAI/litellm#29956.
Levels: `unit` mocked; `integration` real DB/cost-map; `live` real provider +
proxy + SpendLogs rows. Status: `covered` / `partial` / `gap`.

View file

@ -2,7 +2,7 @@
Behavior tests for the LiteLLM_AutoRouterSession conditional upsert and the benchmarks
aggregate, against a real Postgres. The classification lives in SQL, so these tests are
the ones that exercise it; the builder and flush contracts are unit-tested in
tests/test_litellm/proxy/db/test_autorouter_session_rollup.py.
tests/unit/proxy/db/test_autorouter_session_rollup.py.
"""
import asyncio

View file

@ -1,80 +0,0 @@
import os
import pytest
@pytest.fixture(autouse=True)
def _hermetic_mcp_server_registry():
"""Restore the singleton ``global_mcp_server_manager``'s registry state around every
test, so entries seeded by one test never leak into another on a shared shard."""
from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
global_mcp_server_manager,
)
saved_registry = dict(global_mcp_server_manager.registry)
saved_config_servers = dict(global_mcp_server_manager.config_mcp_servers)
saved_tool_mapping = dict(global_mcp_server_manager.tool_name_to_mcp_server_name_mapping)
saved_oauth_slots = global_mcp_server_manager._oauth_discovery_slots
try:
yield
finally:
global_mcp_server_manager.registry.clear()
global_mcp_server_manager.registry.update(saved_registry)
global_mcp_server_manager.config_mcp_servers.clear()
global_mcp_server_manager.config_mcp_servers.update(saved_config_servers)
global_mcp_server_manager.tool_name_to_mcp_server_name_mapping.clear()
global_mcp_server_manager.tool_name_to_mcp_server_name_mapping.update(saved_tool_mapping)
global_mcp_server_manager._oauth_discovery_slots = saved_oauth_slots
@pytest.fixture(autouse=True)
def _hermetic_server_root_path():
"""Isolate MCP discovery tests from a leaked ``SERVER_ROOT_PATH``.
``tests/test_litellm/proxy/test_custom_proxy.py`` sets ``SERVER_ROOT_PATH`` at import time
(its app mounts under a custom path) and never restores it, so in a shared shard the value
leaks into this process. The discovery routes and the 401 challenges read it, so a leaked
value would silently rewrite every ``resource_metadata`` URL and make these tests depend on
shard ordering. Clearing it here pins the default (root-mounted) deployment; a test that
exercises a sub-path deployment sets the value explicitly within its own body.
"""
saved = os.environ.pop("SERVER_ROOT_PATH", None)
try:
yield
finally:
if saved is not None:
os.environ["SERVER_ROOT_PATH"] = saved
@pytest.fixture
def config_only_mcp_manager_factory():
from litellm.proxy._experimental.mcp_server.mcp_server_manager import MCPServerManager
class ConfigOnlyManager(MCPServerManager):
def initialize_tool_name_to_mcp_server_name_mapping(self):
return None
return ConfigOnlyManager
@pytest.fixture
def _mcp_request_ctx():
def _mcp_request_ctx(**overrides):
from types import SimpleNamespace
from mcp.server.context import ServerRequestContext
kwargs = {
"session": SimpleNamespace(),
"lifespan_context": {},
"protocol_version": "2025-06-18",
"method": "",
"params": None,
"request_id": 1,
"meta": None,
"request": None,
}
kwargs.update(overrides)
return ServerRequestContext(**kwargs)
return _mcp_request_ctx

View file

@ -101,7 +101,7 @@ async def get_spend_logs(session, request_id=None, api_key=None):
@pytest.mark.skip(
reason="Flaky in CI: /spend/logs?request_id=... returns 500 even after a 20s wait for the spend log to be written. Spend-log accuracy is covered by tests/test_litellm/proxy/spend_tracking/ and the proxy_spend_accuracy_tests CircleCI job."
reason="Flaky in CI: /spend/logs?request_id=... returns 500 even after a 20s wait for the spend log to be written. Spend-log accuracy is covered by tests/unit/proxy/spend_tracking/ and the proxy_spend_accuracy_tests CircleCI job."
)
@pytest.mark.asyncio
async def test_spend_logs():
@ -159,7 +159,7 @@ async def generate_team(session: aiohttp.ClientSession, org_id: str) -> dict:
@pytest.mark.skip(
reason="Flaky in CI: /spend/logs?request_id=... returns 500 even after a 20s wait for the spend log to be written. Same write-then-read race against the spend logs DB as test_spend_logs. Spend-log accuracy is covered by tests/test_litellm/proxy/spend_tracking/ and the proxy_spend_accuracy_tests CircleCI job."
reason="Flaky in CI: /spend/logs?request_id=... returns 500 even after a 20s wait for the spend log to be written. Same write-then-read race against the spend logs DB as test_spend_logs. Spend-log accuracy is covered by tests/unit/proxy/spend_tracking/ and the proxy_spend_accuracy_tests CircleCI job."
)
@pytest.mark.asyncio
async def test_spend_logs_with_org_id():

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