diff --git a/.circleci/config.yml b/.circleci/config.yml index c3643b20c4..1f2795bfca 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -420,6 +420,9 @@ jobs: chmod +x docker/entrypoint.sh ./docker/entrypoint.sh set -e + - run: + name: Generate Prisma Client + command: uv run --no-sync python -m prisma generate # Run pytest and generate JUnit XML report - run: name: Run tests @@ -2161,7 +2164,7 @@ jobs: - run: name: Start outputting logs for second container - command: docker logs -f my-app-2 + command: docker logs -f my-app-3 background: true - run: @@ -2962,8 +2965,8 @@ jobs: rm -f /tmp/uv-install.sh echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$BASH_ENV" export PATH="$HOME/.local/bin:$PATH" - uv run --with 'coverage[toml]==7.10.6' coverage combine realtime_translation_coverage ocr_coverage search_coverage mcp_coverage litellm_mcps_tests_coverage logging_coverage audio_coverage local_testing_part1_coverage local_testing_part2_coverage pass_through_unit_tests_coverage batches_coverage guardrails_coverage redis_caching_coverage - uv run --with 'coverage[toml]==7.10.6' coverage xml + uv tool run --from 'coverage[toml]==7.10.6' coverage combine realtime_translation_coverage ocr_coverage search_coverage mcp_coverage litellm_mcps_tests_coverage logging_coverage audio_coverage local_testing_part1_coverage local_testing_part2_coverage pass_through_unit_tests_coverage batches_coverage guardrails_coverage redis_caching_coverage + uv tool run --from 'coverage[toml]==7.10.6' coverage xml - codecov/upload: file: ./coverage.xml diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index 40d00adeb0..9495c9bbd8 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -58,6 +58,21 @@ router = APIRouter(prefix="/v1/mcp", tags=["mcp"]) MCP_AVAILABLE: bool = True TEMPORARY_MCP_SERVER_TTL_SECONDS = 300 + + +def does_mcp_server_exist( + mcp_server_records: Iterable[Any], mcp_server_id: str +) -> bool: + """ + Check if the mcp server with the given id exists in the iterable of mcp servers. + + Defined at module level (outside ``if MCP_AVAILABLE``) so it can be imported + on Python < 3.10 where the ``mcp`` package is unavailable. + """ + for mcp_server_record in mcp_server_records: + if mcp_server_record.server_id == mcp_server_id: + return True + return False DEFAULT_MCP_REGISTRY_VERSION = "1.0.0" LITELLM_MCP_SERVER_NAME = "litellm-mcp-server" LITELLM_MCP_SERVER_DESCRIPTION = "MCP Server for LiteLLM" @@ -503,17 +518,6 @@ if MCP_AVAILABLE: ) return prisma_client - def does_mcp_server_exist( - mcp_server_records: Iterable[LiteLLM_MCPServerTable], mcp_server_id: str - ) -> bool: - """ - Check if the mcp server with the given id exists in the iterable of mcp servers - """ - for mcp_server_record in mcp_server_records: - if mcp_server_record.server_id == mcp_server_id: - return True - return False - # Router to fetch all MCP tools available for the current key @router.get( diff --git a/ruff.toml b/ruff.toml index 55d008a7dd..6c854b7ad0 100644 --- a/ruff.toml +++ b/ruff.toml @@ -18,3 +18,4 @@ exclude = ["litellm/types/*", "litellm/__init__.py", "litellm/proxy/example_conf "litellm/proxy/guardrails/guardrail_hooks/guardrail_benchmarks/test_eval.py" = ["PLR0915"] "litellm/responses/streaming_iterator.py" = ["PLR0915"] "litellm/files/main.py" = ["PLR0915"] +"litellm/llms/litellm_proxy/skills/sandbox_executor.py" = ["PLR0915"] diff --git a/tests/test_budget_management.py b/tests/test_budget_management.py index 5863cea9d3..ad1e07ce99 100644 --- a/tests/test_budget_management.py +++ b/tests/test_budget_management.py @@ -73,13 +73,15 @@ async def test_create_budget_with_duration(budget_setup): ), "The budget_reset_at field should not be None" # Calculate the expected reset time: created_at + 1 day. - expected_reset_at_date = datetime.fromisoformat( - budget_setup["created_at"] - ) + timedelta(days=1) + # Replace trailing 'Z' with '+00:00' for Python 3.9 compat (fromisoformat + # only learned to accept 'Z' in Python 3.11). + created_at_str = budget_setup["created_at"].replace("Z", "+00:00") + expected_reset_at_date = datetime.fromisoformat(created_at_str) + timedelta(days=1) # Allow for a small tolerance in seconds for the timestamp calculation. tolerance_seconds = 3 - actual_reset_at_date = datetime.fromisoformat(budget_setup["budget_reset_at"]) + reset_at_str = budget_setup["budget_reset_at"].replace("Z", "+00:00") + actual_reset_at_date = datetime.fromisoformat(reset_at_str) time_difference = abs( (actual_reset_at_date - expected_reset_at_date).total_seconds() )