mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
* feat(a2a): support a2a-sdk 1.x proxy routing for 0.3 and 1.0 agents Bump a2a-sdk to 1.x and wire send/stream through compat conversions so the proxy accepts A2A 1.0 JSON-RPC while preserving 0.3 wire clients. Co-authored-by: Cursor <cursoragent@cursor.com> * Add user controlled protocol version in agents * Fix exeception mapping * Fix a2a base url * Add e2e test for a2a * Fix lint * Fix lint * fix(a2a): harden card version detection and header isolation coverage Use protocolVersion when inferring agent card wire format, assert distinct httpx cache keys in the header-isolation test, and suppress targeted basedpyright errors for optional SDK imports. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(a2a): suppress reportArgumentType for SDK compat types and fix streaming trace ID - Add pyright: ignore[reportArgumentType] to SendMessageSuccessResponse id= and result= args in _send_message, and SendStreamingMessageResponse root= in _stream_messages, where a2a-sdk compat types diverge from basedpyright's inferred signature, reducing the reportArgumentType count back within budget. - Fix streaming trace ID in astream_a2a_message to use str(request.id) when available instead of always generating a new uuid4(), restoring JSON-RPC request-ID correlation for observability. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * style(a2a): expand SendStreamingMessageResponse for black formatting Move pyright: ignore comment to the root= argument line so Black accepts the expanded multi-line form. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(a2a): fix 2 reportArgumentType errors without suppression - main.py: narrow logging_obj from object|None to Optional[Logging] via isinstance check before A2AStreamingIterator call, fixing the "Logging | object" argument type mismatch at line 699. - a2a_endpoints.py: extract response_dict with explicit isinstance(dict) guard before passing to normalize_jsonrpc_response, fixing the "LLMResponseTypes | dict[str, Any]" type mismatch at line 835. - Remove spurious pyright: ignore comments added in previous commits that were not suppressing the actual errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(a2a): rewrite upstream URL for 1.0 agent cards in getAuthenticatedExtendedCard 1.0 upstream agent cards store the endpoint URL in supportedInterfaces[0].url rather than a top-level url field. The previous guard only rewrote url when it existed at the top level, so after normalize_agent_card lowered a 1.0 card to 0.3 the upstream internal address leaked into the url field of the 0.3 response. Fix: rewrite both url and supportedInterfaces[0].url to the proxy address before calling normalize_agent_card, ensuring the upstream address is never visible to downstream clients regardless of the upstream card's wire format. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: extend _served_version to all PascalCase methods; add direct httpx-client isolation proof - _served_version now checks `_PASCAL_TO_WIRE` membership instead of two hardcoded names, so GetTask/CancelTask/etc. are promoted to 1.0 wire format alongside SendMessage — prevents mixed wire formats mid-session - test_create_a2a_client_uses_fresh_httpx_client now asserts a2a_client_a._litellm_httpx_client is not a2a_client_b._litellm_httpx_client (direct proof that header bleed cannot occur), in addition to the cache-key inequality check Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: id:0 silently dropped in version_convert; explicit continue in stream retry - version_convert.py: replace `request_id or ""` with `str(request_id) if request_id is not None else ""` in both _send_result_to and _stream_result_to; id=0 is valid JSON-RPC and must not be coerced to "" which breaks response correlation - main.py: add explicit `continue` after the A2ALocalhostURLError retry in _execute_a2a_stream_with_retry so the control flow (retry → next iteration → stream_succeeded guard) is unambiguous Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: preserve a2a retry and discovery card urls * Fix black * Fix test * fix(a2a): avoid KeyError in discovery log after 0.3→1.0 card normalization When a 0.3-style agent card is normalized to 1.0, the top-level url key is replaced by supportedInterfaces; log the already-computed proxy_url instead. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(a2a): preserve taskId when lowering push notification config set params Flatten 1.x create envelope fields before parsing into TaskPushNotificationConfig so 1.0 clients forwarding to 0.3 upstream keep taskId and config. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(a2a): ignore unknown fields in message/send proto fallback ParseDict in _build_message_send_params now matches other inbound paths so 1.0 clients with extra proto fields are not rejected with -32602. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(a2a): normalize tasks/list params and response across protocol versions Convert list task entries on the response path and lower ListTasksRequest params including status filters when forwarding 1.0 clients to 0.3 upstream. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(a2a): avoid reportArgumentType in _lower_list_tasks_params; use local var instead of _parse return Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(a2a): drop private SDK symbol in tasks/list status lowering _lower_list_tasks_params imported _CORE_TO_COMPAT_TASK_STATE, a private a2a-sdk symbol that could disappear on a patch release and silently break status-filter lowering. Derive the 0.3 wire string from the public protobuf enum name instead (TASK_STATE_<NAME> maps to the 0.3 value once the prefix is dropped and underscores become dashes) and validate the result against the 0.3 TaskState enum's own values via a fully-typed pure helper. Behavior is unchanged for every state; unspecified or unrecognized states still drop the filter. Adds parametrized regression tests covering dashed wire values (input-required, auth-required) and the unspecified drop. * fix(a2a): drop redundant push-notification envelope key; unify MessageToDict import _flatten_create_push_notification_params used `config or pushNotificationConfig`, which short-circuits so a co-present pushNotificationConfig key was never popped and leaked into the flattened params. Pop both keys unconditionally and prefer config when present. Adds a regression test on the helper that fails on the old leak. Also import MessageToDict from a2a.compat.v0_3.conversions in _lower_list_tasks_params to match every other conversion helper in the module instead of pulling it straight from google.protobuf.json_format. * fix(a2a): reject invalid message/stream params early with -32602 _handle_stream_message built MessageSendParams lazily inside the stream_response() generator, so malformed 1.0 params surfaced as a generic -32603 after the 200 status line was already committed. The non-streaming path validates up front and returns -32602 (Invalid params). Validate eagerly before returning the StreamingResponse and emit -32602 on failure so both paths reject malformed params identically. Adds a regression test asserting the streamed error code is -32602. * fix(a2a): raise clear error when non-streaming send ends on an update event _send_message fed the SDK iterator's last event straight into SendMessageSuccessResponse, whose result only accepts Message or Task. A non-standard upstream whose final event is a TaskStatusUpdateEvent or TaskArtifactUpdateEvent made the response construction raise an opaque pydantic ValidationError. Guard the converted result and raise a clear RuntimeError instead, consistent with the no-response guard above it. Adds regression tests for the Message happy path and the update-event rejection via an injected fake client. * test(a2a): lock in clean merged agent-card URL without PROXY_BASE_URL Regression coverage proving _build_merged_agent_card produces no double slash in supportedInterfaces[0].url when PROXY_BASE_URL is unset and request.base_url carries a trailing slash. get_custom_url routes through join_paths, which rstrips the base, so the f-string join stays clean. * style(a2a): modernize type annotations to satisfy strict ruff budget After merging the black->ruff-format migration from base, the A2A files owned by this PR still used Optional[X]/quoted annotations that pushed UP037/UP045 over their lowered ceilings. Convert to X | None, drop the now-unnecessary quoted local annotation in _send_message, and remove the imports left unused by the rewrite. Type semantics are unchanged. * style(a2a): type a2a_endpoints dict params as dict[str, Any] The merge with the formatter-migration baseline tightened the reportUnknownArgumentType ceiling; bare dict annotations made every value Unknown and pushed the codebase total over cap. Annotate the JSON-RPC params, body, metadata, and litellm_params dicts as dict[str, Any] so their values are typed, dropping the unknown-argument count back under the ceiling. No behavior change. * fix(a2a): guard localhost retry against a missing agent card handle_a2a_localhost_retry rewrote the card URL and called create_client with whatever agent_card it received. The caller resolves the card from the SDK client (Optional), so a None card reached set_agent_card_url and create_client, surfacing an opaque SDK error instead of a clear one. Add an early RuntimeError guard mirroring the httpx-client check, drop the now always-true card None-check on the stash line, and cover it with a regression test. * style(a2a): disable reportUnknownArgumentType in a2a-sdk boundary modules The lint env type-checks without the optional a2a-sdk/protobuf installed, so every call into the protobuf-generated compat conversions counts as an Unknown-typed argument and the new A2A code pushed the codebase reportUnknownArgumentType total over its ceiling. These three modules are the A2A SDK boundary; turn the rule off file-wide with a documented reason instead of scattering dozens of per-line ignores across every SDK call. * fix(a2a): tolerate unknown fields when lowering 1.0->0.3; align streaming trace id Two issues greptile flagged: version_convert: the 1.0->0.3 lowering paths (_send_result_to, _task_to, _stream_result_to) called ParseDict without ignore_unknown_fields=True, so a 1.0 upstream response carrying vendor extensions raised and best-effort fell back to passing the un-lowered 1.0 shape to a 0.3 client. Set the flag to match the agent-card path and every inbound path; unknown fields are now dropped and the result is correctly lowered. main.py: asend_message_streaming derived X-LiteLLM-Trace-Id from the JSON-RPC request id, unlike asend_message which uses the logging object's litellm_trace_id. Prefer the logging trace id (then request id, then a uuid) so streamed and non-streamed calls correlate under the same trace. Adds regression tests for both, including the stream-event lowering path. * style(a2a): apply ruff format to a2a protocol and proxy modules Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
335 lines
11 KiB
TOML
335 lines
11 KiB
TOML
[project]
|
|
name = "litellm"
|
|
version = "1.91.0"
|
|
description = "Library to easily interface with LLM API providers"
|
|
readme = "README.md"
|
|
requires-python = ">=3.10, <3.14"
|
|
license = "MIT"
|
|
license-files = ["LICENSE"]
|
|
authors = [
|
|
{ name = "BerriAI" },
|
|
]
|
|
dependencies = [
|
|
# Ranges (not exact pins) so SDK consumers can coexist with their other
|
|
# deps. Reproducibility for our Docker/CI comes from `uv.lock`.
|
|
# When changing a floor, verify it installs + imports on every supported
|
|
# Python with: `uv pip install --resolution=lowest-direct .`
|
|
"fastuuid>=0.14.0,<1.0",
|
|
"httpx>=0.28.0,<1.0",
|
|
"openai>=2.20.0,<3.0.0",
|
|
"python-dotenv>=1.0.0,<2.0",
|
|
"tiktoken>=0.8.0,<1.0",
|
|
"importlib-metadata>=8.0.0,<9.0",
|
|
"tokenizers>=0.21.0,<1.0",
|
|
"click>=8.0.0,<9.0",
|
|
"jinja2>=3.1.6,<4.0",
|
|
"aiohttp>=3.10,<4.0",
|
|
"pydantic>=2.10.0,<3.0.0",
|
|
"jsonschema>=4.0.0,<5.0",
|
|
]
|
|
|
|
[project.urls]
|
|
Homepage = "https://litellm.ai"
|
|
Repository = "https://github.com/BerriAI/litellm"
|
|
Documentation = "https://docs.litellm.ai"
|
|
|
|
# Optional extras use compatible ranges (like the core SDK above) so downstream
|
|
# consumers can coexist with other packages and pick up security patches without
|
|
# forking. Reproducibility for our Docker/CI comes from `uv.lock` (images install
|
|
# via `uv sync --frozen`). A few deps stay exact-pinned: litellm's own
|
|
# sub-packages and the opentelemetry trio move in lockstep, and grpcio is
|
|
# supply-chain-pinned to a vetted, aged release.
|
|
[project.optional-dependencies]
|
|
proxy = [
|
|
"gunicorn>=23.0.0,<24.0",
|
|
"uvicorn>=0.33.0,<1.0",
|
|
"granian>=2.7.4,<3.0",
|
|
"uvloop>=0.21.0,<1.0; sys_platform != 'win32'",
|
|
"fastapi>=0.136.3,<1.0",
|
|
"starlette>=1.0.1,<2.0",
|
|
"backoff>=2.2.1,<3.0",
|
|
"pyyaml>=6.0.3,<7.0",
|
|
"rq>=2.7.0,<3.0",
|
|
"orjson>=3.11.6,<4.0",
|
|
"apscheduler>=3.11.2,<4.0",
|
|
"fastapi-sso>=0.19.0,<1.0",
|
|
"PyJWT>=2.13.0,<3.0",
|
|
"python-multipart>=0.0.27,<1.0",
|
|
"cryptography>=48.0.1,<49.0",
|
|
"pynacl>=1.6.2,<2.0",
|
|
"websockets>=15.0.1,<16.0",
|
|
"boto3>=1.43.1,<2.0",
|
|
"azure-identity>=1.25.2,<2.0",
|
|
"azure-storage-blob>=12.28.0,<13.0",
|
|
"mcp>=1.26.0,<2.0",
|
|
"litellm-proxy-extras==0.4.74",
|
|
"litellm-enterprise==0.1.44",
|
|
"RestrictedPython>=8.1,<9.0",
|
|
"rich>=13.9.4,<14.0",
|
|
"polars>=1.38.1,<2.0",
|
|
"soundfile>=0.12.1,<1.0",
|
|
"pyroscope-io>=0.8.16,<1.0; sys_platform != 'win32'",
|
|
"pydantic-settings>=2.14.1,<3.0",
|
|
"expression>=5.6.0,<6.0",
|
|
]
|
|
# Thin client install for the `lite` CLI on developer laptops. The CLI's heavy
|
|
# imports (fastapi, cryptography, ...) are all guarded, so it runs on the base
|
|
# SDK plus just these three; none of the server runtime in `proxy` is pulled in.
|
|
cli = [
|
|
"rich>=13.9.4,<14.0",
|
|
"pyyaml>=6.0.3,<7.0",
|
|
"requests>=2.32.0,<3.0",
|
|
]
|
|
extra_proxy = [
|
|
"prisma>=0.11.0,<1.0",
|
|
"azure-identity>=1.25.2,<2.0",
|
|
"azure-keyvault-secrets>=4.10.0,<5.0",
|
|
# Not in PyPI proxy extra.
|
|
"google-cloud-kms>=2.24.2,<3.0",
|
|
"google-cloud-iam>=2.19.1,<3.0",
|
|
# Not in PyPI proxy extra.
|
|
"resend>=2.23.0,<3.0",
|
|
"redisvl>=0.4.1,<1.0; python_version < '3.14'",
|
|
"a2a-sdk>=1.1.0,<2.0",
|
|
]
|
|
utils = [
|
|
# Not in Docker or PyPI proxy extra.
|
|
"numpydoc>=1.8.0,<2.0",
|
|
]
|
|
caching = ["diskcache>=5.6.3,<6.0"]
|
|
semantic-router = [
|
|
"semantic-router>=0.1.15,<1.0; python_version < '3.14'",
|
|
"aurelio-sdk>=0.0.19,<1.0; python_version < '3.14'",
|
|
]
|
|
mlflow = ["mlflow>=3.11.1,<4.0"]
|
|
grpc = [
|
|
# Newest non-yanked release older than the 30-day cutoff.
|
|
"grpcio==1.78.0",
|
|
]
|
|
stt-nvidia-riva = [
|
|
# NVIDIA Riva STT provider (gRPC). These are imported lazily inside the
|
|
# provider handler so litellm core remains usable without them.
|
|
"nvidia-riva-client>=2.15.0",
|
|
"soundfile>=0.12.1",
|
|
"audioread>=3.0.1",
|
|
"numpy>=1.26.0",
|
|
]
|
|
google = ["google-cloud-aiplatform>=1.133.0,<2.0"]
|
|
proxy-runtime = [
|
|
# Historically bundled in the proxy Docker images via requirements.txt.
|
|
# Keep these in a dedicated extra so uv-based images preserve the same
|
|
# feature surface without forcing the base SDK install to grow.
|
|
"google-cloud-aiplatform>=1.133.0,<2.0",
|
|
"google-genai>=1.37.0,<2.0",
|
|
"anthropic[vertex]>=0.84.0,<1.0",
|
|
"grpcio==1.78.0",
|
|
"prometheus-client>=0.20.0,<1.0",
|
|
"langfuse>=2.59.7,<3.0",
|
|
"opentelemetry-api==1.28.0",
|
|
"opentelemetry-sdk==1.28.0",
|
|
"opentelemetry-exporter-otlp==1.28.0",
|
|
"opentelemetry-instrumentation-fastapi==0.49b0",
|
|
"ddtrace>=2.19.0,<3.0",
|
|
"sentry-sdk>=2.21.0,<3.0",
|
|
"mangum>=0.17.0,<1.0",
|
|
"azure-ai-contentsafety>=1.0.0,<2.0",
|
|
"azure-storage-file-datalake>=12.20.0,<13.0",
|
|
"pypdf>=6.12.0,<7.0; python_version < '3.14'",
|
|
"llm-sandbox>=0.3.39,<1.0",
|
|
"detect-secrets>=1.5.0,<2.0",
|
|
]
|
|
|
|
[project.scripts]
|
|
litellm = "litellm:run_server"
|
|
lite = "litellm.proxy.client.cli:cli"
|
|
litellm-proxy = "litellm.proxy.client.cli:cli"
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"diff-cover==9.7.2",
|
|
"flake8==7.3.0",
|
|
"basedpyright==1.39.7",
|
|
"pytest==9.0.3",
|
|
"pytest-mock==3.15.1",
|
|
"pytest-asyncio==1.3.0",
|
|
"pytest-postgresql==7.0.2",
|
|
# pytest-postgresql imports psycopg v3 during pytest startup. Keep the base
|
|
# package and the binary wheel in the default dev environment so local
|
|
# pytest works without requiring a system libpq install.
|
|
"psycopg==3.3.3",
|
|
"psycopg-binary==3.3.3",
|
|
"pytest-xdist==3.8.0",
|
|
"requests-mock==1.12.1",
|
|
"responses==0.26.0",
|
|
"respx==0.22.0",
|
|
"ruff==0.15.3",
|
|
"types-requests==2.32.4.20260107",
|
|
"types-setuptools==75.8.0.20250225",
|
|
"types-redis==4.6.0.20241004",
|
|
"types-PyYAML==6.0.12.20250915",
|
|
"botocore-stubs==1.43.14",
|
|
"types-boto3[bedrock,bedrock-agent,bedrock-runtime,kms,s3,sagemaker-runtime,sts]==1.43.30",
|
|
"opentelemetry-api==1.28.0",
|
|
"opentelemetry-sdk==1.28.0",
|
|
"opentelemetry-exporter-otlp==1.28.0",
|
|
"opentelemetry-instrumentation-fastapi==0.49b0",
|
|
"langfuse==2.59.7",
|
|
"fastapi-offline==1.7.6",
|
|
"fakeredis==2.34.1",
|
|
"pytest-rerunfailures==15.1",
|
|
"pytest-cov==5.0.0",
|
|
"parameterized==0.9.0",
|
|
"openapi-core==0.22.0; python_version < '3.14'",
|
|
"pytest-timeout==2.4.0",
|
|
"vcrpy==8.2.1",
|
|
"pytest-recording==0.13.4",
|
|
]
|
|
proxy-dev = [
|
|
"prisma==0.11.0",
|
|
"hypercorn==0.17.3",
|
|
"prometheus-client==0.20.0",
|
|
"opentelemetry-api==1.28.0",
|
|
"opentelemetry-sdk==1.28.0",
|
|
"opentelemetry-exporter-otlp==1.28.0",
|
|
"opentelemetry-instrumentation-fastapi==0.49b0",
|
|
"azure-identity==1.25.2",
|
|
"a2a-sdk==1.1.0",
|
|
]
|
|
ci = [
|
|
# These are lazily imported at call sites; keep them out of core deps to
|
|
# avoid bloating the base SDK install (google-generativeai pulls grpcio +
|
|
# protobuf, Pillow is a compiled C extension).
|
|
"tenacity==8.5.0",
|
|
"google-generativeai==0.8.6",
|
|
"Pillow==12.2.0",
|
|
# Azure batch E2E tests still import psycopg2 directly.
|
|
"psycopg2-binary==2.9.11",
|
|
"pytest-codspeed==4.3.0",
|
|
"pytest-retry==1.7.0",
|
|
"pyarrow==23.0.1",
|
|
"langchain==1.3.9",
|
|
"lunary==1.4.36; python_version == '3.10'",
|
|
"lunary==1.4.37; python_version >= '3.11'",
|
|
"logfire==4.6.0",
|
|
"traceloop-sdk==0.33.12",
|
|
"detect-secrets==1.5.0",
|
|
"PyGithub==2.8.1",
|
|
"aiodynamo==24.7",
|
|
"argon2-cffi==25.1.0",
|
|
"assemblyai==0.52.4",
|
|
"jsonlines==4.0.0",
|
|
"anthropic==0.84.0",
|
|
"blockbuster==1.5.26",
|
|
"beautifulsoup4==4.14.3",
|
|
"pylint==4.0.5",
|
|
"langchain-mcp-adapters==0.2.1",
|
|
"langchain-openai==1.1.14",
|
|
"langgraph>=1.2.4,<1.3.0",
|
|
"langgraph-prebuilt>=1.1.0,<1.3.0",
|
|
"claude-agent-sdk==0.1.44",
|
|
]
|
|
healthcheck = [
|
|
"httpx==0.28.1",
|
|
"pyyaml==6.0.3",
|
|
]
|
|
|
|
[build-system]
|
|
requires = ["uv_build==0.11.8"]
|
|
build-backend = "uv_build"
|
|
|
|
[tool.uv]
|
|
constraint-dependencies = [
|
|
"tornado>=6.5.6",
|
|
"aiohttp>=3.14.1,<4.0",
|
|
"packaging>=24.0",
|
|
]
|
|
override-dependencies = [
|
|
# a2a-sdk 1.x requires packaging>=24.0; lunary 1.4.x still caps at <24.0.
|
|
"packaging>=24.0",
|
|
]
|
|
default-groups = ["dev"]
|
|
required-version = ">=0.10.9"
|
|
exclude-newer = "3 days"
|
|
|
|
[tool.uv.sources]
|
|
litellm-proxy-extras = { workspace = true }
|
|
litellm-enterprise = { workspace = true }
|
|
|
|
[tool.uv.workspace]
|
|
members = ["enterprise", "litellm-proxy-extras"]
|
|
|
|
[tool.uv.build-backend]
|
|
module-root = ""
|
|
source-exclude = [
|
|
"litellm/proxy/enterprise",
|
|
"**/__pycache__",
|
|
"**/__pycache__/**",
|
|
"**/.pytest_cache",
|
|
"**/.pytest_cache/**",
|
|
"**/.ruff_cache",
|
|
"**/.ruff_cache/**",
|
|
]
|
|
|
|
[tool.isort]
|
|
profile = "black"
|
|
|
|
[tool.commitizen]
|
|
version = "1.91.0"
|
|
version_files = [
|
|
"pyproject.toml:^version",
|
|
]
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
asyncio_default_fixture_loop_scope = "session"
|
|
markers = [
|
|
"asyncio: mark test as an asyncio test",
|
|
"limit_leaks: mark test with memory limit for leak detection (e.g., '40 MB')",
|
|
"no_parallel: mark test to run sequentially (not in parallel) - typically for memory measurement tests",
|
|
]
|
|
filterwarnings = [
|
|
# Suppress Pydantic serializer warnings from mock server responses (non-critical for memory tests)
|
|
# These occur because the mock server returns a simplified response format
|
|
"ignore:Pydantic serializer warnings:UserWarning",
|
|
"ignore::UserWarning:pydantic.main",
|
|
# Suppress pytest-asyncio event loop deprecation warning (handled automatically by pytest-asyncio)
|
|
"ignore::DeprecationWarning:pytest_asyncio.plugin",
|
|
]
|
|
|
|
[tool.mutmut]
|
|
# Mutation-testing scope. Driven by the manually-triggered workflow at
|
|
# .github/workflows/mutation-test.yml. mutmut is not part of the project's
|
|
# default install; it is pulled in via `uv run --with mutmut==<version>` in CI.
|
|
# `also_copy = ["litellm/"]` is required because mutmut runs in a `mutants/`
|
|
# sandbox and the test conftest imports from across the litellm package.
|
|
paths_to_mutate = [
|
|
"litellm/proxy/management_endpoints/",
|
|
]
|
|
tests_dir = [
|
|
"tests/test_litellm/proxy/management_endpoints/",
|
|
"tests/proxy_behavior/management/",
|
|
]
|
|
also_copy = [
|
|
"litellm/",
|
|
]
|
|
# Run the test suite once before mutation to gather line coverage, then skip
|
|
# mutating lines no test exercises. Those mutants would survive regardless
|
|
# (no test hits the line to kill them), so generating them wastes hours of CI.
|
|
# The score now reads as "mutation score over covered code" — pair with a
|
|
# line-coverage number when reporting.
|
|
mutate_only_covered_lines = true
|
|
# Disable rerun/parallel plugins for mutation runs:
|
|
# - pytest-retry triggers an `INTERNALERROR: no option named 'filtered_exceptions'`
|
|
# when invoked via mutmut's in-process `pytest.main()` call.
|
|
# - rerunning a "failed" test on a mutant would mask which mutants are killed
|
|
# vs. survive, so reruns are wrong for mutation testing regardless.
|
|
# - xdist is unnecessary inside mutmut (mutmut handles its own parallelism).
|
|
pytest_add_cli_args = [
|
|
"-p", "no:retry",
|
|
"-p", "no:rerunfailures",
|
|
"-p", "no:xdist",
|
|
]
|
|
|
|
[tool.coverage.run]
|
|
source = ["litellm"]
|
|
relative_files = true
|