Skip to content

chore(techdebt): clear fresh tech debt from the last 24 hours (rolling, 2026-09-06 to 2026-09-24) - #42710

Merged
mateo-berri merged 34 commits into
mainfrom
litellm_techdebt_20260923
Sep 24, 2026
Merged

mateo-berri merged 34 commits into
mainfrom
litellm_techdebt_20260923

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Daily runs keep finding fresh suppressions, Any and slop on main
  • Two cleanup PRs were open; Mateo wants exactly one rolling PR
  • 2026-09-24: an inert mutable-ok, an Any alias behind a pyright ignore, two coarse dict locals, a narrating comment

How it solves it:

User Flow

Refactor only, no user-visible behavior changes. Every run in this PR moves, removes or narrows annotations and comments; the few statements it touches resolve to the same value as before. The per-run notes below say where a run did more than that

Pre-Submission checklist

  • I have added meaningful tests (none needed: no behavior change)
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

No runtime behavior is meant to change, so there is no before=fail leg to show. The after leg is a live proxy booted from bd480fd (worktree, port 43127, one openai/gpt-4o-mini deployment, real OpenAI calls) exercising the surfaces this run touched; 9d5f5f4 since then only retypes a parameter and wraps headers that every caller already passes as httpx.Headers

$ curl -s -w '\nHTTP %{http_code}\n' localhost:43127/health/liveliness
"I'm alive!"
HTTP 200

$ curl -s -w '\nHTTP %{http_code}\n' localhost:43127/v1/chat/completions -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
    -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Say hi"}],"max_tokens":5}'
{"id":"chatcmpl-ERYv5wdSM7lvCqwE3hCVEolAFlf0T","created":1790237795,"model":"gpt-4o-mini","object":"chat.completion",...,"choices":[{"finish_reason":"length","index":0,"message":{"content":"Hi there! How can","role":"assistant",...}}],"usage":{"completion_tokens":5,"prompt_tokens":9,"total_tokens":14,...}}
HTTP 200

$ curl -s -w '\nHTTP %{http_code}\n' localhost:43127/v1/responses -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
    -d '{"model":"gpt-4o-mini","input":"Say hi","max_output_tokens":16}'
{"id":"resp_uzZH5Omd...","created_at":1790237796,"model":"gpt-4o-mini","object":"response","output":[{"content":[{"text":"Hi there! How can I help you today?","type":"output_text"}],"role":"assistant","status":"completed","type":"message"}],"status":"completed","usage":{"input_tokens":9,"output_tokens":11,"total_tokens":20}}
HTTP 200

$ curl -s -o /tmp/openapi.json -w 'HTTP %{http_code}\n' localhost:43127/openapi.json; jq -r '.paths | keys[] | select(startswith("/v1/responses"))' /tmp/openapi.json
HTTP 200
/v1/responses
/v1/responses/compact
/v1/responses/input_tokens
/v1/responses/{response_id}
/v1/responses/{response_id}/cancel
/v1/responses/{response_id}/input_items

The /openapi.json listing shows the retyped _ResponseDocSchemas still feeds fastapi's responses= on every Responses route. The static claim is the per-file count from python3 scripts/check_type_discipline.py <file> at the merge base with main (09ebb28) versus bd480fd for the files this run touched: litellm/passthrough/main.py 45 to 44, litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py 215 to 214, litellm/proxy/response_api_endpoints/endpoints.py 86 to 85 (line 52 no longer reported), litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py 67 to 67 with the LIT013 report on line 1040 gone, litellm/proxy/utils.py 636 to 636. make lint and make check pass on the tip, and the 12 mapped test files pass locally

Findings by run date

2026-09-24 run (window 2026-09-23 07:43 UTC to 2026-09-24 07:43 UTC, 106 commits)

  1. 8e74bb0: litellm/proxy/response_api_endpoints/endpoints.py:52 introduced _ResponseDocSchemas = dict[int | str, dict[str, Any]] behind # pyright: ignore[reportExplicitAny], which LIT010 also reported as an unannotated alias. It is now _ResponseDocSchemas: TypeAlias = dict[int | str, dict[str, object]]; fastapi's responses kwarg accepts it unchanged and the ignore is gone
  2. 014d7b8: litellm/passthrough/main.py:543 introduced _streaming_request_data: dict. It only feeds is_streaming_request, so it is Final[dict[str, object]]
  3. 1175559: litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py:3905 introduced data: dict[str, Any] with a prose comment standing in for a suppression. _read_request_body returns a string-keyed dict and every later store narrows on isinstance, so it is Final[dict[str, object]] and the comment is gone
  4. 08b61f4: litellm/proxy/utils.py:2392 added # Execute guardrail pipelines before the normal callback loop, which restates the if not skip_guardrails line under it. Deleted
  5. Branch-only: the # mutable-ok on refusal_delta: Final[MessageDelta] in streaming_iterator.py:1040, carried from refactor: clear fresh tech debt from the last 24 hours (2026-09-06, 2026-09-09, 2026-09-10, 2026-09-13, 2026-09-16, 2026-09-17, 2026-09-18, 2026-09-19, 2026-09-20, 2026-09-22) #40030, is a Final TypedDict literal, so LIT013 on current main reports it as inert and the gate failed. Deleted
  6. Branch-only (9d5f5f4): the 2026-09-16 retype of get_error_class in litellm/llms/vercel_ai_gateway/embedding/transformation.py to headers: dict[str, str] | httpx.Headers added a LIT001 the base did not have (29 to 30). It is now Mapping[str, str] | httpx.Headers, the same shape as the Mistral configs, and the value is normalized to httpx.Headers before it reaches BaseLLMException, which is what every caller in llm_http_handler.py already passes

Deliberately left alone: pending = asyncio.ensure_future(...) in sse_keepalive.py (1857f5d) and revoked = False in session_endpoints.py (bc3b5b1) are genuinely rebound, so the honest fix is a restructure rather than a rebind-ok. _apply_v3(request_data: dict) in straiker.py (76b0b10) forwards to _build_envelope, _fail, _block and _record, which all take and mutate a bare dict, so narrowing one signature needs the whole v3 path retyped. _normalize_response_format(value: Any) in the TwelveLabs Pegasus transformation (2471602) and the bare dict params on _merge_pipeline_metadata_bucket / _merge_pipeline_metadata_writes in proxy/utils.py (996019c) are the same shape. The getattr reads in straiker.py, types_utils/utils.py and rust_bridge/secret_manager.py resolve dynamic attribute names. The new multi-line comments in otel, ollama, bedrock responses, mcp_server and the UI session endpoints explain contracts rather than restating code. No budget ceiling was raised (the only budget change adds LIT013 at limit 0), no type: ignore landed, no bare TODO or FIXME landed

2026-09-23 run (window 2026-09-22 07:40 UTC to 2026-09-23 07:40 UTC, 133 commits)

fc00554 feat: add configurable provider affinity header mapping (#41033): litellm/litellm_core_utils/provider_affinity.py carried a # mutable-ok on the def add_provider_affinity_header( line, which suppresses nothing; the return annotation and return statements already carry their own. Removed the inert one

cb2f225 feat(proxy): opt-in include_guardrail_response (#42462): litellm/proxy/litellm_pre_call_utils.py put # rebind-ok on the ] = ( continuation line of a subscript assignment, where LIT011 cannot see it. Flattened the target onto one line so the suppression sits on the statement

989d7b8 fix(proxy): attribute provider and model_info on pre_call_hook rejections (#42079): litellm/proxy/utils.py had a # mutable-ok on litellm_params["metadata"] = {}, which is a parameter mutation (LIT011) as well as a mutable literal (LIT002), so it now carries # rebind-ok for the store and # mutable-ok for the seeded literal. The same commit added a comment-only # mutable-ok line above a **({...} if ... else {}) spread; that line covered nothing, so it is gone and the suppression sits on the two literals instead

b0ac23d feat(logger): dispatch Python logging through the Rust diagnostics processor (#42440): litellm/rust_bridge/logger.py placed # mutable-ok on the closing }, of the extra={...} dict, where LIT002 does not look. Moved it to the extra={ line at first; main has since shipped LIT013, which reports that trailer as inert, so 5364e9f removes it entirely

7056151 fix(gemini): simplify model version check (#42465): litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py introduced model_name = ... without Final while the neighbouring local had it. Added Final

b673ee6 feat(arize): per-team success and error sampling rates (#42447): litellm/integrations/arize/arize.py introduced rate = float(value) without Final. Added Final

Left alone on purpose: # rebind-ok on data[...] in litellm_pre_call_utils.py is genuinely required because pre-call hooks share one request dict, and the # mutable-ok on dict(single_deployment.get("model_info")) in proxy/utils.py is required because the router reads a mutable model-info payload. New Any and bare dict usage in litellm/proxy/_experimental/mcp_server/bridge_token_flow.py, litellm/experimental_mcp_client/client.py and the rust fixture generator are behavior-adjacent and too invasive for a same-day cleanup

Absorbed from #40030 on 2026-09-24

Absorbed from #40030 on 2026-09-24: its full diff re-applied on this branch via merge 75bcfaf. Dropped while absorbing: its type-discipline-budget.json edits (main's ratchet already carries them), its rebind-ok moves in baseline_accounting.py and autorouter_baseline_cache.py where main already had the same placement, and the mutable-ok on refusal_delta as described above

2026-09-22 (window 2026-09-21 07:43 UTC to 2026-09-22 07:43 UTC, 339 commits, 1,186 files)

  1. 8795be0 refactor(types): replace Any with proven types in 32 files: initialize_mavvrik_focus_export_job in litellm/integrations/mavvrik_focus/mavvrik_focus_logger.py gained a one-use _PodLockManager Protocol wrapping getattr(proxy_logging_obj, "db_spend_update_writer", None) and getattr(writer, "pod_lock_manager", None). proxy_logging_obj is declared ProxyLogging, whose __init__ always assigns db_spend_update_writer: DBSpendUpdateWriter, which always assigns pod_lock_manager: PodLockManager, so the lock manager is now read directly under a Final and the Protocol is deleted
  2. 537e8ac feat(cost): warn and count $0 cost on billable requests: _audio_tokens(details: object) in litellm/litellm_core_utils/llm_cost_calc/zero_cost_diagnostic.py read getattr(details, "audio_tokens", None). Its only two callers pass Usage.prompt_tokens_details and Usage.completion_tokens_details, both declared wrappers with an audio_tokens: int | None field, so the parameter is PromptTokensDetailsWrapper | CompletionTokensDetailsWrapper | None and the read is a plain attribute read behind a None check
  3. 8795be0 same commit: get_error_class in litellm/llms/vercel_ai_gateway/embedding/transformation.py was rewritten with headers: Any. The base BaseConfig.get_error_class declares headers: dict | httpx.Headers; the override takes dict[str, str] | httpx.Headers, which is what httpx.Response.headers carries and what VercelAIGatewayException accepts. httpx was already imported
  4. e7cd97c fix(proxy): release unclaimed budget reservations at request end: budget_reservation_from_metadata in litellm/litellm_core_utils/core_helpers.py returned a bare dict | None. The reservation is a string-keyed payload, so it is dict[str, object] | None; its callers in proxy_track_cost_callback.py accept dict | None unchanged

Follow-up on the 2026-09-20 next_cursor item: CI's test_mcp_client_unit.py::test_list_tools feeds a MagicMock result, whose truthy mock cursor sent the walk into a second page and an empty list. 018a6a9 keeps the typed result.next_cursor read and restores the isinstance(next_cursor, str) guard so a non-string cursor still ends the walk, matching the pre-cleanup behavior

Deliberately left alone: the **kwargs: Any and the seven noqa: PLC0415 in litellm/proxy/_experimental/mcp_server/operations.py (1098604) are lines moved out of server.py, not new code. _get_config_value(...) -> Any | None in generic_guardrail_api/__init__.py (8795be0) reads an arbitrary attribute by name, so a narrower type needs a typed config model, not a one-line change. should_report_bug in bug_report.py reads status_code off a BaseException that does not declare it. The section-header comments in litellm/litellm_core_utils/tokenizer.py (0abd926) follow a pattern used in several hundred places in the tree, so removing five is taste. The #### banner in user_api_key_auth_mcp.py (82eef2f) matches the sibling banners in the same function. The multi-line comments added in the TinyFish, MCP OAuth, parallel-request-limiter and pass-through timeout code document billing, authorization and timeout contracts rather than restating code. No budget ceiling was raised in the window, no type: ignore was added, and no bare TODO or FIXME landed

2026-09-20 (window 2026-09-19 07:43 UTC to 2026-09-20 07:43 UTC, 205 commits, 1,118 files)

Suppression trailers that suppress nothing. scripts/check_type_discipline.py honours a *-ok trailer only on the exact line it reports, which for a multi-line statement is the first line. Each trailer below sat on the closing line, so the violation it meant to justify still counted against type-discipline-budget.json while the comment did nothing

  1. ed40241 fix(proxy): estimate auto-router baseline costs from durable cache history: # mutable-ok: spend-log JSON serialization requires plain mappings on the closing } of the autorouter_savings_estimate literal in litellm/litellm_core_utils/litellm_logging.py. Moved onto the opening { the gate reports
  2. ed40241: # rebind-ok: keyset pagination advances after each complete timestamp group on the cursor = page[-1].started_at rebinding in litellm/proxy/db/baseline_accounting.py, while LIT010 fires on the cursor: float | None = None declaration. Moved onto the declaration
  3. ed40241: # rebind-ok: drain under lock on the closing ] of the client.baseline_accounting_transactions = ...[32:] slice in baseline_accounting.py. Moved onto the assignment line
  4. ed40241: # noqa: BLE001 # unknown acknowledgements can be replayed safely on except (Exception, asyncio.CancelledError) as error: in baseline_accounting.py. Ruff does not raise BLE001 there because the handler re-raises, so the trailer is inert. Deleted
  5. ed40241: # rebind-ok: request-owned retry marker and # rebind-ok: capture uncertainty for failure logging on the closing lines of the two logging_obj.* assignments in litellm/proxy/hooks/autorouter_baseline_cache.py. Moved onto the assignment lines; the second reason is shortened to uncertain capture for failure logs so the line fits the 120-column formatter
  6. 82e3f39 refactor(auth): resolve org identity through an auth_checks helper: # noqa: BLE001 # only a DB outage may fail auth here, ... on except Exception as e: in litellm/proxy/auth/auth_checks.py. Ruff exempts the handler because it logs with exc_info=True, so the trailer is inert. Deleted
  7. 99b1a32 feat(guardrails): add headroom guardrail for message compression: _hoisted_top_level_system_message(self, data: dict) in litellm/llms/anthropic/chat/guardrail_translation/handler.py only calls data.get, and its # mutable-ok: API message payload sat on the return-type line where LIT001 never looks. Typed as Mapping[str, object] and the trailer deleted
  8. 5dc0131 refactor(mcp): port MCP client and server helpers to MCP SDK 2: next_cursor = getattr(result, "next_cursor", None) plus an isinstance(next_cursor, str) guard in litellm/experimental_mcp_client/tools.py. mcp.types.ListToolsResult.next_cursor is declared str | None on the pinned mcp>=2.2.0, so the read is result.next_cursor and the guard is if not next_cursor

Net effect on the gates at the tip: type-discipline violations in the six touched files drop from 1057 to 1051, basedpyright errors in them from 2070 to 2065, ruff-strict unchanged at 309. No budget file was edited (the working-tree counts sit under every ceiling and AGENTS.md asks not to touch budgets on a PR branch)

Deliberately left alone: video_tokens=getattr(response_api_usage.input_tokens_details, "video_tokens", None) in litellm/responses/utils.py matches nine sibling getattr reads that predate the window, so swapping one line would be a drive-by. tool_call_map: Final[dict[_ToolCallKey, dict[str, Any]]] in streaming_chunk_builder_utils.py and extract_file_data(cast(Any, ...)) in files_endpoints.py need a typed tool-call payload model, not a one-line change. The MCP getattr/setattr reads in mcp_server/utils.py, sampling_handler.py and elicitation_handler.py deliberately accept both mapping and SDK-object shapes, so direct access would change what a mapping result does. prisma_client: Final[PrismaClient] = getattr(managed_files_obj, "prisma_client") reads a field BaseFileEndpoints does not declare. The getattr(_native, ...) lookups in rust_bridge/fork_guard.py probe an optional native extension. The new multi-line comments in encrypt_decrypt_utils.py, experimental_mcp_client/client.py, proxy_cli.py and the OpenAI Responses guardrail handler explain compatibility or forking contracts. No budget ceiling was raised in the window, no type: ignore was added, and no bare TODO or FIXME landed

2026-09-19 (window 2026-09-18 07:43 UTC to 2026-09-19 07:43 UTC, 245 commits, 881 files)

  1. 1c40e60 fix(a2a): Entra credentials own the chat route bearer over a stored api_key or authorization header: _registry_headers in litellm/llms/a2a/chat/transformation.py returned dict[str, Any] | None for a filtered copy of a Mapping[str, object]. Typed as dict[str, object] | None
  2. b03957b fix(proxy): unpin cost-map pricing copied into model_info and report pricing overrides: echoed_cost_map_pricing_fields and pricing_override_fields in litellm/types/utils.py took Mapping[str, Any] but only read keys and compare values to None. Typed as Mapping[str, object]
  3. b3d9ba9 fix(policy_engine): deliver guardrail text rewrites on multi-choice, unfinished, and envelope-less streams: _spread_text_rewrite_over_stream_events in litellm/llms/openai/responses/guardrail_translation/handler.py took stream_events: Sequence[Any] and only passes each event to stream_item_field(item: object, ...). Typed as Sequence[object]
  4. 1adbfbf fix: strip eager_input_streaming for non-Claude providers next to input_examples: _without_anthropic_only_tool_keys(tool: dict) -> dict and _drop_anthropic_only_tool_keys(tools: list[dict] | None) in litellm/main.py were bare. Typed as dict[str, object] and list[dict[str, object]]
  5. 7cb01cf fix(proxy): exchange role default, logged rejections, gated grant listing: the rewritten _build_aggregate_authorization_server_response signature in litellm/proxy/_experimental/mcp_server/discoverable_endpoints.py kept a bare -> dict. Typed as dict[str, object]; the sibling _build_aggregate_protected_resource_response -> dict predates the window and is left alone
  6. 5ef05b9 feat(websearch): let the model emit objective + multi-query search shape for providers that support it: tool_args: dict | None in litellm/integrations/websearch_interception/handler.py. Typed as dict[str, object] | None, keeping its mutable-ok
  7. c8a2d8c feat(proxy): add LiteLLM_DailyGlobalSpend key-free rollup for the usage dashboard: pending_days in litellm/proxy/spend_tracking/daily_global_spend_rollup.py had no caller in litellm/, enterprise/, tests/ or docs/. Deleted

Deliberately left alone: generation_config: dict[str, Any] | None in litellm/llms/gemini/interactions/transformation.py, because the popped image_config is spread into a dict and typing it as object needs an isinstance guard that would change what a non-dict value does. The getattr reads on incomplete_details, fallback_headers_adopted, param_value and guardrail_name read fields their declared types (ModelResponse, a response union, an Any config row, object) do not carry. The LoggingSurface getattr/setattr on private _defer_async_logging and _native_pending_logging in legacy_callbacks.py is the Rust bridge's adapter contract, not a same-day cleanup. Every new noqa, pyright: ignore, mutable-ok and rebind-ok in the window names one rule and carries a reason. No budget ceiling was raised, no type: ignore, and no bare TODO or FIXME was added. Merging main also dropped the 2026-09-18 finding 1: main rewrote legacy_callbacks.py and callbacks_needed with its litellm_request_debug read no longer exists, so the branch now carries main's version of that file unchanged

2026-09-18 (window 2026-09-17 07:43 UTC to 2026-09-18 07:43 UTC, 149 commits, 680 files)

  1. 63d994a refactor(rust): run OCR through a route-neutral callback contract and a legacy Logging adapter: callbacks_needed in litellm/rust_bridge/legacy_callbacks.py read getattr(logger, "litellm_request_debug", False). Logging.litellm_request_debug is declared with a False default (the 2026-09-13 run fixed the same read in lifecycle.py before it moved here), so it is a plain attribute read
  2. 9617312 add PublicDispatch and c01db25 bring x-litellm-rust: RequestT, NativeT, ResultT in litellm/rust_bridge/dispatch.py and ResultT in litellm/rust_bridge/response_metadata.py were module-level TypeVars without : Final (LIT010). Annotated
  3. 9617312 and 0a66328 fix(router): validate Jev classifier probabilities: NativeHook in dispatch.py and JevProbability in litellm/router_strategy/complexity_router/jev_classifier.py were implicit aliases assigned at module level. Declared with : TypeAlias
  4. 648373a feat(management_v1): bulk update team member budgets: member_budget_patch in litellm/proxy/management_endpoints/common_utils.py returned dict[str, Any], _upsert_budget_and_membership took budget_patch: dict[str, Any], and its source and create_data locals were Any-valued. The patch is only read, so it is Mapping[str, object] on both ends and the locals are object-valued; the mutable-ok on create_data stays because the Prisma payload is still built as a dict

Deliberately left alone: the dict[str, object] parameters on MetadataUpdater and finalize in legacy_callbacks.py, because update_response_metadata writes into that kwargs dict, so Mapping would misdescribe the contract. The getattr reads in litellm_logging.py (_hidden_params), ocr/main.py (file_input.name), rust_bridge/ocr/route_host.py (error.headers, ocr_request_format_error) and model_management_endpoints.py (iterated field names) read fields their declared types do not carry. The requested_query_params: dict | None rebinding in pass_through_endpoints.py is pre-existing legacy flow that the window only extended. The multi-line crash-recovery comment in autoroute/commands.py and the inherited-window comment in common_utils.py explain non-obvious behavior rather than restating code. Every new pyright: ignore in the window names one rule and carries a reason. No budget ceiling was raised in the window, no type: ignore, and no bare TODO or FIXME was added

2026-09-17 (window 2026-09-16 04:19 UTC to 2026-09-17 07:43 UTC, 372 commits since the first-parent base a8979fe)

Every suppression added in the window (98 in non-test files) was stripped and the matching gate re-run on its file, as the 2026-09-16 run did. Two produced no violation without their comment; the other 96 reproduce their violation and stay. All seven new pyright: ignore trailers list exactly the one rule that fires, so nothing to narrow

  1. 4cb4493 fix(proxy): let the OTel trace id fallback fill a null litellm_trace_id: # rebind-ok: metadata is the request's own out-param dict on metadata["trace_id"] = trace_id in litellm/proxy/litellm_pre_call_utils.py suppressed nothing (LIT011 only flags the data[...] store two lines up, whose marker stays). Removed
  2. ee676d5 fix(mcp): preserve browser OAuth for unrelated bearer tokens: in litellm/proxy/_experimental/mcp_server/bridge_token_flow.py the # noqa: PLC0415 # envelope imports bridge types sat on the is_envelope, name inside a parenthesized import, but ruff reports PLC0415 on the from line, so the import was unsuppressed. Moved the trailer onto the from line
  3. 47117d8 fix(guardrails): don't add post_call output scan for MCP-only Presidio modes: initialize_presidio in litellm/proxy/guardrails/guardrail_initializers.py read getattr(litellm_params, "presidio_filter_scope", None). LitellmParams inherits PresidioConfigModel, which declares the field with a None default, so it is a plain attribute read
  4. 2b6184d fix(proxy): resolve rate-limit fallbacks after model normalization and retry from a client-request snapshot: _resolve_fallback_models in litellm/proxy/common_request_processing.py returned bare list | None; get_fallback_model_group already types the chain as list[str] | None, so the return is now list[str] | None. The fallbacks: list parameter stays because get_fallback_model_group itself takes list[Any]

Deliberately left alone: the getattr(chunk, "choices", ()) added by 884e969 in streaming_chunk_builder_utils.py looks redundant against its declared ModelResponse | ModelResponseStream union, but test_stream_chunk_builder_tolerates_trailing_chunk_without_choices feeds a BaseLiteLLMOpenAIResponseObject with no choices, so the annotation is narrower than the runtime input and the getattr is load-bearing; widening the annotation is a follow-up for that module. Also left: the # Check RBAC and router cooldown comments and the data: Any in safe_json_dumps.py, which the diff shows as added but are moved lines that already existed at the base; the bare Sequence in prometheus._set_customer_budget_metrics and the dict params in gen_ai_semconv and converse_transformation, which mirror the untyped shapes they receive; the three data: dict seeds in proxy_server.py, which are rebound in legacy handlers. No budget ceiling moved, no type: ignore, and the only new TODO links LIT-5722

2026-09-16 (window 2026-09-15 07:43 UTC to 2026-09-16 07:43 UTC, 173 commits)

Every suppression added in the window was stripped one at a time and the matching gate re-run on the file (scripts/check_type_discipline.py, ruff check --config ruff-strict.toml, basedpyright with the type_check_gate.py invocation). The ones below produced no violation without their comment, so the comment was a dead marker and is removed. Every other suppression in the window (164, including every noqa) reproduces its violation when stripped and stays

  1. cccff65 fix(guardrails): scan the Anthropic top-level system prompt and tool_use arguments: eight # mutable-ok: guardrails rewrite the caller's request payload in place trailers in litellm/llms/anthropic/chat/guardrail_translation/handler.py sat on item stores (block["input"] = ..., content[i]["text"] = ...) that LIT001/LIT002 never flag, and one on a list comprehension bound to Final. Removed; ruff format collapsed the parenthesized continuations back to single lines
  2. 69be041 feat(proxy): add /nvidia_nim passthrough route for NIM object detection and OCR /v1/infer: the # mutable-ok on the returned dict literal in litellm/llms/nvidia_nim/passthrough/transformation.py (line 113) suppressed nothing. Removed; the two on the signature and the dict(headers) call are needed and stay
  3. ff5b59b feat(proxy): add POST /user/bulk_new for batched user and team membership creation: in litellm/proxy/management_helpers/bulk_user_creation.py a # pyright: ignore[reportUnknownArgumentType] on the _set_object_permission call and a # mutable-ok on a list literal already wrapped in tuple(...) were dead. Removed. The ignore on team.metadata listed reportUnknownArgumentType alongside reportUnknownMemberType; only the latter fires, so the code list is narrowed
  4. cd9c399 feat(proxy): add POST /user/bulk_delete and POST /team/bulk_member_delete: # pyright: ignore[reportUnknownArgumentType] # HTTPException.detail is untyped in litellm/proxy/management_helpers/bulk_user_deletion.py (line 190) sat on str(exc.detail), which type-checks. Removed; the neighbouring ignore two lines up still fires and stays
  5. a6a58b3 feat(router): add Switchyard capability classifier: in litellm/router_strategy/complexity_router/complexity_router.py a # mutable-ok on a dict literal already annotated Final[StandardLoggingRoutingDecision] (which LIT002 exempts) and one on a .append(...) call (method calls are out of the checker's reach) were dead. Removed
  6. 7cc07d4 fix(responses): build the billed terminal response immutably and guard the cache dump: the ignore in litellm/responses/streaming_iterator.py listed reportUnknownArgumentType, reportArgumentType; only reportArgumentType fires, so the list is narrowed
  7. Reason wording: # rebind-ok: the counter update reads the stamp off the shared dict on budget_reservation["finalized"] = True in litellm/proxy/hooks/proxy_track_cost_callback.py described a read, while the marker suppresses an in-place parameter store. Reworded to stamps the caller's shared dict for the counter update
  8. Rolling-branch fix, not a window finding: the 2026-09-06 rewrite of _with_refusal_stop_details (cdea9c9) read processed_chunk["usage"] on a NotRequired key, which raised KeyError for a message_delta without usage and pushed reportTypedDictNotRequiredAccess two over its ceiling on this branch (main sits exactly at the limit). The chunk is now rebuilt as refusal_chunk: Final[MessageBlockDelta] = {**processed_chunk, "delta": refusal_delta}, which is what the pre-rewrite code did with a cast, so usage and context_management pass through whether or not they are present

Deliberately left alone: the dict[str, Any] / Mapping[str, Any] parameters across litellm/proxy/guardrails/guardrail_hooks/singulr/singulr.py and litellm/llms/openai/chat/guardrail_translation/handler.py (raw request payloads with no typed shape to read through), and the getattr reads in exception_mapping_utils.py, reset_budget_job.py and the pass-through handlers, which read fields the declared types do not carry. No budget ceiling was raised in the window, no type: ignore, and no bare TODO or FIXME was added

2026-09-13 (window 2026-09-12 07:43 UTC to 2026-09-13 07:43 UTC, 65 commits)

  1. 45fad44 fix(cost): bill request-level OCR pricing on direct SDK calls: _deployment_model_info and _ocr_model_info in litellm/cost_calculator.py read getattr(litellm_logging_obj, "litellm_params", None). Logging.__init__ always assigns litellm_params, so both are plain attribute reads now; the None guard on the logging object stays. Because the field is a non-optional dict, the follow-up check is if not litellm_params, which returns None for the same inputs (an empty dict never yielded a model_info anyway)
  2. 94e4dd7 fix(mcp): require admission for delegated OAuth: litellm/proxy/_experimental/mcp_server/mcp_server_manager.py filtered the registry with getattr(server, "auth_type", None). MCPServer.auth_type is a declared field, so it is server.auth_type now
  3. 347b642 refactor(ocr): complete native lifecycle and preserve Azure auth: callbacks_needed in litellm/rust_bridge/lifecycle.py read getattr(logger, "litellm_request_debug", False). Logging.litellm_request_debug is declared with a False default, so it is a plain read
  4. eddfb5f fix(responses): preserve hosted web search calls: _web_search_calls_by_call_id in litellm/responses/litellm_completion_transformation/transformation.py read getattr(choice.message, "provider_specific_fields", None). Message.provider_specific_fields is declared, so it is a plain read; the isinstance(..., Mapping) guard still handles None
  5. 76cb0fe fix(model_management): stop persisting cost map pricing as a deployment override: without_server_derived_pricing in litellm/types/utils.py takes Mapping[str, Any]. Started as finding 5 and reverted (1bc2438): the object signature forces the /model/new caller to build ModelInfo through model_validate instead of ** unpacking, which reviewers cannot verify at a glance, so it stays as reported debt

Also committed: ui/litellm-dashboard/src/lib/http/schema.d.ts loses two stale soft_budget doc lines. That is the generator output for the merged tip (the docstring was removed on the base branch without regenerating), and make check fails until it is in the tree

Deliberately left alone: getattr(logger, "logger_fn", None) in the same callbacks_needed function and getattr(logging_obj, "call_type", None) plus getattr(logging_obj, "_native_pending_logging", None) in common_request_processing.py (347b642): logger_fn is not declared on Logging, logging_obj is typed Any on a pre-existing signature, and _native_pending_logging is set from Rust (litellm-rust/crates/python-bridge/src/lifecycle/bindings.rs), so there is no static type to read through. getattr(updated_patch.litellm_params, field) in model_management_endpoints.py (76cb0fe) iterates over field names, so the string access is inherent. getattr(event, "output_index", None) and getattr(tool_call, "id", None) in the Responses bridge (eddfb5f) read fields the base object/BaseLiteLLMOpenAIResponseObject types do not declare. The mutable-ok on the web search call index, the rebind-ok on the OCR release signal and the two reportPrivateUsage ignores in lifecycle.py all carry reasons and have no static replacement. The multi-line OAuth and admission comments added in mcp_server/server.py, budget_reservation.py, key_management_endpoints.py, user_api_key_cache.py and proxy_cli.py document security and rollout contracts rather than restating code. The # Run the coroutine in its original context comment in logging_worker.py predates the window (only re-indented). .github/scripts/*.py gained Any but sits outside the typed litellm/ tree. No budget ceiling was raised in the window, no type: ignore, and no bare TODO/FIXME was added

2026-09-10 (window 2026-09-09 07:17 UTC to 2026-09-10 07:43 UTC, 225 commits)

  1. 2bca7ff fix(anthropic): replay OpenAI encrypted reasoning byte for byte behind /v1/messages: _thinking_block_from_reasoning_item and _assistant_group_to_input_items in litellm/llms/anthropic/experimental_pass_through/responses_adapters/transformation.py returned dict[str, Any]. The values are only ever serialized, so both are now dict[str, object]; the mutable-ok trailers stay because the payloads are still built as dicts
  2. 29af8b7 fix(streaming_handler): replay a cached completion with no choices as an empty stream: litellm/litellm_core_utils/streaming_handler.py read getattr(cached_choice.delta, "tool_calls", None). Delta.tool_calls is a declared field, so it is a plain attribute read now
  3. e27a018 fix(router): pin bridge-replayed encrypted reasoning to the deployment that minted it: _anthropic_content_blocks in litellm/router_utils/pre_call_checks/encrypted_content_affinity_check.py re-implemented the message walk that _anthropic_content_lists in litellm/litellm_core_utils/prompt_templates/common_utils.py already does (2bca7ff). The helper is now public as anthropic_content_lists and the router check iterates through it; the module already imported from common_utils, so no new import edge

Deliberately left alone: three candidates were started and then reverted (2d0ee09) once git log -S showed they were pre-existing lines only re-indented or moved in the window, so out of scope: getattr(response_obj, "usage", None) (249a999) and getattr(usage_obj, "cost", None) (9e25dd7) in litellm/responses/streaming_iterator.py, and the # Use completion_cost comment in litellm/proxy/management_endpoints/cost_tracking_settings.py. The usage read also cannot become a plain attribute without breaking test_responses_streaming_does_not_reset_prior_completion_start_time, which drives the iterator with Mock(spec=ResponsesAPIResponse). The remaining mutable-ok and cast-ok trailers added in the window sit on untyped client JSON or on API message payloads that are built as dicts and have no static replacement. No budget ceiling was raised in the window and no bare TODO/FIXME or type: ignore was added

2026-09-09 (window 2026-09-08 07:43 UTC to 2026-09-09 07:43 UTC, 87 commits)

  1. d82eb33 feat(otel): typed semconv-aligned OpenTelemetry instrumentation: _maybe_construct_otel_v2 in litellm/litellm_core_utils/litellm_logging.py used getattr(callback, "callback_name", None) right after isinstance(callback, OpenTelemetryV2). OpenTelemetryV2.__init__ always sets callback_name, so it is now a plain attribute read
  2. 754a2af feat(mcp): add schema discovery proxy mode: _mcp_proxy_identity in litellm/proxy/_experimental/mcp_server/tool_search.py carried # mutable-ok: absent metadata default on (tool.meta or {}).get(...). Now None if tool.meta is None else tool.meta.get(...), same result for None and for an empty mapping
  3. 754a2af same commit: the same function returned a dict literal under # mutable-ok: TypedDict identity payload. It is now assigned to resolved: Final[MCPProxyToolIdentity] and returned, which the LIT001 gate recognizes as a TypedDict build without a suppression
  4. dc09d9e feat(bedrock): add TwelveLabs Marengo Embed 3.0 embeddings: litellm/types/llms/bedrock.py gained a # TwelveLabs Marengo Embed types section header above two aliases whose names already say that. Deleted

Deliberately left alone: _group_deployments in litellm/router_strategy/complexity_router/complexity_router.py (0175c7d) reads get_model_list through getattr because the existing tests inject fake routers without that method, so a direct call fails them. attribute_of in litellm/proxy/common_utils/openai_error_payload.py and the getattr(guardrail_to_apply, name, default) in unified_guardrail.py take dynamic attribute names, so there is no static replacement. The rebind-ok loop accumulators in litellm/batches/batch_utils.py need a functional rewrite of the whole loop to remove, which is not a same-day cleanup. The cast-ok casts in litellm/proxy/utils.py preserve the legacy callback tuple contract. No budget ceilings were raised in the window (every limit moved down) and no bare TODO/FIXME or type: ignore was added

2026-09-06 (window 2026-09-05 07:43 UTC to 2026-09-06 07:43 UTC, 108 commits)

  1. 7399b38 fix(anthropic): satisfy lint budget gates for refusal translation: litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py imported cast under # noqa: TID251 and used two cast-ok casts in _with_refusal_stop_details to rebuild the refusal message_delta. Replaced with processed_chunk["type"] != "message_delta" narrowing, a MessageDelta typed delta, and a Final[MessageBlockDelta] spread of the chunk with the new delta (the explicit MessageBlockDelta(...) constructor from the first pass read usage unconditionally; see 2026-09-16 finding 8). The cast import is gone from the file
  2. 79d4778 fix(anthropic): stream the refusal text on bridged /v1/messages calls: _mapping_field in litellm/llms/anthropic/experimental_pass_through/messages/utils.py cast container to Mapping[str, object] right after isinstance(container, Mapping). Dropped the cast; container.get(key) type-checks as is
  3. 6ec53f2 style(vertex-ai): satisfy Lyria quality gates: _is_audio_predict_response and _get_audio_prediction_count in litellm/proxy/pass_through_endpoints/llm_provider_handlers/vertex_passthrough_logging_handler.py took json_response: dict # mutable-ok while only reading it. Now Mapping[str, object], no suppression. _handle_audio_predict_response keeps dict because it stores the response into StandardPassThroughResponseObject

Deliberately left alone: the repeated is_non_conversational_call_type(...) and not self.inspect_embeddings skip in the AIM and Cato hooks (6e05ac5): the predicate is already shared, a wrapper would save one line per hook. truncate_base64_in_messages_async's str | list | dict | None mirrors the sync helper's contract. getattr(mcp_server, "per_server_oauth_discovery", False) follows the surrounding pattern for the loosely typed server object. No budget ceilings were raised in the window (every limit moved down)

Type

🧹 Refactoring

Caveats (if any)

Low

Adversarial caveat pass at bd480fd raised one medium finding, the vercel LIT001, fixed in 9d5f5f4. It also flagged the missing live after leg, now pasted above, and two dev-venv basedpyright differences, rebutted with the gate env above

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Link to Devin session: https://app.devin.ai/sessions/6bd2a65cef194d41924fa36d6a940997
Open in Devin Desktop: https://app.devin.ai/desktop/session/6bd2a65cef194d41924fa36d6a940997?variant=devin

mateo-berri and others added 29 commits September 6, 2026 08:05
…026-09-06)

Drop the TID251 cast import and both cast-ok casts from the refusal
message_delta rebuild by narrowing the TypedDict union on its type literal,
drop the redundant Mapping cast after the isinstance check in
_mapping_field, and type the Lyria predict read-only helpers as
Mapping[str, object] instead of a bare dict with mutable-ok.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…read out of the cleanup

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…-16)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…0906

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/rust_bridge/lifecycle.py
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…0906

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/rust_bridge/legacy_callbacks.py
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… the cleanup

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…0906

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/router_strategy/complexity_router/jev_classifier.py
#	litellm/types/utils.py
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…22 changes

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…e literal

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot requested a review from a team September 23, 2026 08:06
@CLAassistant

CLAassistant commented Sep 23, 2026 •

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ mateo-berri
❌ devin-ai-integration[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@greptile-apps

greptile-apps Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge because there are no new changes since the previous review and the full diff contains no confirmed outstanding issue.

Summary

This PR performs repository-wide type-discipline and lint cleanup without intending user-visible behavior changes.

  • Narrows broad annotations such as Any and bare dict to concrete mappings, object-valued dictionaries, and explicit type aliases.
  • Removes or relocates inert lint suppressions and redundant comments.
  • Reuses the shared Anthropic content-list helper and preserves optional fields while rebuilding refusal-streaming chunks.
  • Retains MCP pagination behavior, including rejection of non-string cursors.
  • Normalizes Vercel AI Gateway headers before constructing provider exceptions.

Reviews (5) · Last reviewed commit: "refactor(vercel): take a read-only heade..."

@codecov

codecov Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.47368% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...integrations/mavvrik_focus/mavvrik_focus_logger.py 33.33% 2 Missing ⚠️
litellm/proxy/db/baseline_accounting.py 0.00% 2 Missing ⚠️
litellm/passthrough/main.py 0.00% 1 Missing ⚠️
...llm/proxy/management_helpers/bulk_user_deletion.py 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_techdebt_20260923 (9d5f5f4) with main (04f3ade)

Open in CodSpeed

…0923

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/proxy/litellm_pre_call_utils.py
#	litellm/proxy/utils.py
…lags as inert

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…litellm_techdebt_20260923

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	litellm/llms/anthropic/chat/guardrail_translation/handler.py
#	litellm/proxy/db/baseline_accounting.py
#	litellm/proxy/hooks/autorouter_baseline_cache.py
…the 24h window

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration devin-ai-integration Bot changed the title chore(techdebt): fix inert suppressions and missing Final in 2026-09-22 changes chore(techdebt): clear fresh tech debt from the last 24 hours (rolling, 2026-09-06 to 2026-09-24) Sep 24, 2026
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

1 similar comment
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 9d5f5f4. Configure here.

@devin-ai-integration
devin-ai-integration Bot marked this pull request as draft September 24, 2026 10:37
@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review September 24, 2026 10:38

@mateo-berri mateo-berri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mateo-berri
mateo-berri merged commit d17c0d7 into main Sep 24, 2026
107 of 108 checks passed
@mateo-berri
mateo-berri deleted the litellm_techdebt_20260923 branch September 24, 2026 11:17
devin-ai-integration Bot added a commit that referenced this pull request Sep 24, 2026
* refactor: clear fresh tech debt from the last 24 hours (2026-09-05, 2026-09-06)

Drop the TID251 cast import and both cast-ok casts from the refusal
message_delta rebuild by narrowing the TypedDict union on its type literal,
drop the redundant Mapping cast after the isinstance check in
_mapping_field, and type the Lyria predict read-only helpers as
Mapping[str, object] instead of a bare dict with mutable-ok.


* refactor: clear fresh tech debt from the last 24 hours (2026-09-09)


* refactor: clear fresh tech debt from the last 24 hours (2026-09-10)


* refactor: keep the pre-existing cost-estimate comment and usage cost read out of the cleanup


* refactor: clear fresh tech debt from the last 24 hours (2026-09-13)


* refactor: keep the model info pricing helper out of the cleanup


* refactor: drop suppressions that no longer suppress anything (2026-09-16)


* refactor: keep the rebind-ok reason inside the line limit


* fix(anthropic): rebuild the refusal message_delta by spreading the chunk


* refactor: clear fresh tech debt from the last 24 hours (2026-09-17)


* refactor: clear fresh tech debt from the last 24 hours (2026-09-18)


* refactor: clear fresh tech debt from the last 24 hours (2026-09-19)


* refactor: keep the pre-existing protected-resource return type out of the cleanup


* refactor: clear fresh tech debt from the last 24 hours (2026-09-20)


* refactor: type fresh getattr, Any, and bare dict debt from 2026-09-22


* fix(mcp): keep the string guard on tools/list next_cursor


* refactor(vercel_ai_gateway): type the embedding error headers dict


* chore(techdebt): fix inert suppressions and missing Final in 2026-09-22 changes


* chore(techdebt): shorten suppression reason to fit line length


* chore(techdebt): format provider spread so its suppression sits on the literal


* chore(techdebt): drop the logger extras suppression that LIT013 now flags as inert


* chore(techdebt): clear fresh suppressions, Any aliases and slop from the 24h window


* refactor(vercel): take a read-only headers mapping in get_error_class


---------

Co-authored-by: mateo <mateo@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

This branch is waiting to be deployed

1 waiting deployment
e2e-changed — 9d5f5f4c Waiting Sep 24, 2026 by devin-ai-integration[bot] via oauth #797
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants