chore(release): patch v1.89.0-rc.2 with batch-file auth, CrowdStrike AIDR, Mantle SigV4, NetApp streaming-cost fix, and team-scoped Datadog toward v1.89.0-rc.3 - #30179
Conversation
…T-3593) (#30009) * fix(proxy): authorize batch files using upload target_model_names (LIT-3593) After replace_model_in_jsonl, body.model is a stripped provider id. Reverse-mapping it via resolve_model_name_from_model_id is first-match on model_list and caused false 403s when multiple deployments share the same stripped name. Use target_model_names from the unified file id instead. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(proxy): restore resolve_model_name_from_model_id for JSONL fallback path (LIT-3593) Restores the reverse-lookup for the JSONL body.model fallback path so that legacy/pre-target_model_names managed files still map stripped provider IDs back to proxy aliases before auth. Also cleans up redundant `or None`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Revert "fix(proxy): restore resolve_model_name_from_model_id for JSONL fallback path (LIT-3593)" This reverts commit 30d2e96. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> (cherry picked from commit 2cd7e87)
(cherry picked from commit 6fc715c)
…gs (#29991) Capture user_id and extra_info from metadata or litellm_metadata. The single-bag read dropped identity whenever a request carried a present litellm_metadata field (null or a user-supplied dict), since /chat/completions routes the authenticated identity into metadata while the guardrail read litellm_metadata first (cherry picked from commit 1bbaf1c)
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
… responses (#26153) (#27346) Cherry-picked from staging squash 4a3860d. The rc line predates the Usage.__init__ server_tool_use dict->ServerToolUse coercion that staging carries (it landed via the squashed OSS sync #29932 / 32c88ca, not as a standalone commit). The calculate_usage Usage(**returned_usage.model_dump()) round-trip re-serializes server_tool_use to a plain dict, so without that coercion the rebuilt usage holds a dict and the regression test asserting a ServerToolUse type fails. Restored the coercion in litellm/types/utils.py to satisfy the prerequisite -- it matches #27346's own first commit (coerce server_tool_use dict to ServerToolUse in Usage.__init__), which was dropped from the squash only because staging already carried it.
Cherry-picked from the PR head 9c049da (single-commit PR, merged to litellm_oss_branch). Applied cleanly; no conflicts. Note: black --check in this worktree flags pre-existing multi-line string formatting in litellm_core_utils/litellm_logging.py (lines ~1006-1050) that is already present on the patch/v1.89.0-rc.1 base and is untouched by this pick -- left as-is to avoid reformatting unrelated lines.
|
|
Greptile SummaryThis PR backports four independent fixes onto the
Confidence Score: 4/5Safe to merge; all three feature backports include mock-based tests, the signing hook is a no-op for every existing provider, and the DD credential block list prevents user injection. The changes are cherry-picks of already-merged staging work with their own tests. The Bedrock Mantle SigV4 path signs correctly after body finalisation and strips stale Authorization headers before handing to The
|
| Filename | Overview |
|---|---|
| litellm/llms/bedrock_mantle/responses/transformation.py | Adds SigV4/IAM auth alongside existing Bearer-token path; correctly strips stale Authorization before signing and pins the credential-scope region to the URL host via _resolve_region. |
| litellm/llms/base_llm/responses/transformation.py | Adds a no-op default sign_request hook so existing providers are unaffected; straightforward extension point. |
| litellm/llms/custom_httpx/llm_http_handler.py | Wires sign_request into all four Responses API handler variants (sync/async x create/compact) after body finalisation and fake-stream prep; replaces json=data with data=signed_body or json=data via body_kwargs. |
| litellm/proxy/hooks/batch_rate_limiter.py | Passes target_model_names from the unified file ID into _enforce_batch_file_model_access, removing the ambiguous resolve_model_name_from_model_id reverse-lookup that could match the wrong deployment when multiple share a provider model ID. |
| litellm/proxy/guardrails/guardrail_hooks/crowdstrike_aidr/crowdstrike_aidr.py | Adds _merge_metadata_bags to read identity from both metadata and litellm_metadata; injects user_id, model, and extra_info into the AIDR payload; extra_info is always set (even {}) when a metadata bag is present. |
| litellm/integrations/datadog/datadog_team_handler.py | New file implementing per-team DataDogLogger caching via DynamicLoggingCache, following the same pattern as LangFuseHandler; credentials keyed correctly for isolation. |
| litellm/integrations/datadog/datadog.py | Adds explicit dd_api_key, dd_site, dd_agent_host, dd_agent_port kwargs that take priority over env vars, enabling team-scoped credential injection. |
| litellm/litellm_core_utils/litellm_logging.py | Moves process_dynamic_callbacks after standard_callback_dynamic_params initialisation; adds a datadog-specific _custom_logger_init_args filter so team-scoped DD credentials are passed into _init_custom_logger_compatible_class. |
| litellm/litellm_core_utils/streaming_chunk_builder_utils.py | Coerces server_tool_use dict to ServerToolUse pydantic instance during chunk aggregation, fixing the AttributeError on downstream attribute access (issue #26153). |
| litellm/litellm_core_utils/llm_cost_calc/utils.py | Adds _get_web_search_requests helper that tolerates server_tool_use being None, dict, or pydantic instance; used across cost-calculation call sites. |
| litellm/litellm_core_utils/initialize_dynamic_callback_params.py | Adds dd_* params to both the supported list and the request-blocked set, preventing end-user injection of Datadog credentials via request body. |
| litellm/types/utils.py | Extends Usage.init to accept server_tool_use as Union[ServerToolUse, dict] and coerce dicts; adds dd_* fields to StandardCallbackDynamicParams. |
| tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py | Comprehensive mock-based SigV4 tests covering bearer precedence, region consistency, Authorization-header stripping, and credential-error mapping; all mock network calls. |
| tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py | Adds tests for the new sign_request wiring: no-op provider sends json=, signing provider sends data= bytes; fake-stream ordering test confirms stream key is absent from signed body. |
| tests/test_litellm/proxy/hooks/test_batch_file_validation.py | Updates existing proxy-alias test to pass target_model_names directly and asserts reverse-lookup is never called; adds parametrised multi-deployment ordering test (LIT-3593). |
| tests/test_litellm/proxy/guardrails/guardrail_hooks/test_crowdstrike_aidr.py | Adds four new tests covering identity capture, empty extra_info, missing metadata, and parametrised dual-bag reading; all mock-based. |
| tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py | Updates existing assertion from dict access to isinstance+attribute access, correctly reflecting the new ServerToolUse coercion behavior. |
Comments Outside Diff (1)
-
litellm/integrations/datadog/datadog_team_handler.py, line 228-241 (link)_dynamic_datadog_credentials_are_passedis unused in production pathsThe method is defined and tested but is never called by the production code — the credential check happens inline in
_init_custom_logger_compatible_classviaif _dd_api_key or _dd_site or _dd_agent_host. If the intent is to keep it for future callers or as a utility, a short docstring clarifying that would help; otherwise it can be removed to avoid dead-code drift.
Reviews (1): Last reviewed commit: "feat(datadog): add team-scoped Datadog c..." | Re-trigger Greptile
| extra_info: dict[str, str] = {} | ||
| user_email = metadata.get("user_api_key_user_email") | ||
| if user_email: | ||
| extra_info["user_name"] = user_email | ||
| ai_guard_payload["extra_info"] = extra_info |
There was a problem hiding this comment.
extra_info unconditionally written when metadata bag is present
extra_info is always added to the payload (even as {}) when any metadata bag resolves, while user_id is conditionally added only when truthy. If the CrowdStrike AIDR API treats an explicit "extra_info": {} differently from an absent key (e.g., strict schema validation), this asymmetry could cause unexpected failures on requests where identity is unavailable despite metadata existing. The tests already codify this behavior, so this is a question about API contract rather than a defect — worth confirming CrowdStrike accepts an empty extra_info object.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Does the CrowdStrike AIDR API accept "extra_info": {} (empty object) without error, or should extra_info be omitted from the payload when there is no user email?
| if isinstance(usage_chunk.server_tool_use, dict): | ||
| server_tool_use = ServerToolUse(**usage_chunk.server_tool_use) | ||
| elif isinstance(usage_chunk.server_tool_use, ServerToolUse): | ||
| server_tool_use = usage_chunk.server_tool_use | ||
| else: | ||
| server_tool_use = ServerToolUse.model_validate( | ||
| usage_chunk.server_tool_use | ||
| ) |
There was a problem hiding this comment.
The three-branch isinstance check can be replaced with a single
model_validate call, which handles dict, ServerToolUse, and any other compatible object uniformly and is more future-proof if ServerToolUse gains additional fields.
| if isinstance(usage_chunk.server_tool_use, dict): | |
| server_tool_use = ServerToolUse(**usage_chunk.server_tool_use) | |
| elif isinstance(usage_chunk.server_tool_use, ServerToolUse): | |
| server_tool_use = usage_chunk.server_tool_use | |
| else: | |
| server_tool_use = ServerToolUse.model_validate( | |
| usage_chunk.server_tool_use | |
| ) | |
| server_tool_use = ServerToolUse.model_validate( | |
| usage_chunk.server_tool_use | |
| ) |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| for bag in (request_data.get("metadata"), request_data.get("litellm_metadata")): | ||
| if isinstance(bag, Mapping): | ||
| present = True | ||
| merged.update(bag) |
There was a problem hiding this comment.
Medium: Spoofable guardrail identity
litellm_metadata is part of the request body on normal proxy calls, and _ensure_litellm_metadata() only populates it when the key is absent. Because this merge lets litellm_metadata overwrite the server-populated metadata, an authenticated caller can send litellm_metadata: {"user_api_key_user_id": "victim"} and have the AIDR payload report the request as that user; take user_id/email from the authenticated UserAPIKeyAuth data or another server-only field instead of a merged request metadata bag.
PR overviewThis PR prepares the v1.89.0-rc.3 patch release with updates spanning batch-file authentication, CrowdStrike AIDR guardrail integration, Mantle SigV4 support, NetApp streaming-cost handling, and team-scoped Datadog behavior. The open code area is in the CrowdStrike AIDR guardrail hook, where request metadata is prepared for AIDR reporting. One security issue remains open around how the CrowdStrike AIDR guardrail derives the user identity included in its outbound payload. An authenticated caller can influence request metadata so that AIDR reporting attributes their request to another user, creating a spoofing/audit-integrity risk rather than direct data access or code execution. No issues have been addressed yet, so the PR still needs a server-trusted source for user identity before this area is secure. Open issues (1)
Fixed/addressed: 0 · PR risk: 5/10 |
Relevant issues
Adds the next batch of fixes to the
patch/v1.89.0-rc.1line (on top of the v1.89.0-rc.2 content) so it can be taggedv1.89.0-rc.3:target_model_names(LIT-3593), cherry-picked from staging2cd7e87485stable/1.84.x— the rc line predates it) + fix(guardrails): read CrowdStrike AIDR identity from both metadata bags #29991 follow-up fix reading identity from both metadata bags (staging squash1bbaf1c39d)litellm_oss_staging_080626sync (32c88ca74f). The rc line already carries the prerequisite feat(bedrock_mantle): add Responses API support (/openai/v1/responses) #29490 Responses route via the 040626 synccompletion_costAttributeError on streaming Anthropic web_search responses ([Bug]:completion_cost()raisesAttributeErroron streaming Anthropic responses with web_search in v1.83.10 #26153), cherry-picked from staging squash4a3860df1f9c049daa1bNo version bump: the 1.89 line keeps
pyproject.tomlat 1.89.0 and the rc number is a git tag, matching rc.2 (#30143).Conflict resolutions / branch adaptations
guard_input.model_dump(mode="json"), adopted thedict[str, Any]annotation; appended the three feature tests and fix(guardrails): read CrowdStrike AIDR identity from both metadata bags #29991's parametrized test to this line's test-file tail (parametrized block black-formatted; upstream carries >88-col lines there)Usage.__init__server_tool_usedict→ServerToolUsecoercion that staging carries (it landed via the squashed OSS sync Litellm oss staging 080626 #29932 /32c88ca74f).calculate_usageround-trips usage throughUsage(**returned_usage.model_dump()), which re-serializesserver_tool_useto a plain dict, so without that coercion the regression test asserting aServerToolUsetype fails. Restored the coercion inlitellm/types/utils.py— it matches fix: completion_cost AttributeError on streaming Anthropic web_search responses (#26153) #27346's own first commit, dropped from the squash only because staging already had it.--checkin the worktree flags pre-existing multi-line-string formatting inlitellm_core_utils/litellm_logging.py(lines ~1006-1050) that is already present on thepatch/v1.89.0-rc.1base and untouched by this pick — left as-is to avoid reformatting unrelated lines.Verified on this branch
test_batch_file_validation.py,test_crowdstrike_aidr.py,test_bedrock_mantle_responses_transformation.py,test_llm_http_handler.py— 149 passedtest_streaming_chunk_builder_server_tool_use.py,test_tool_call_cost_tracking_dict_safety.py,test_cost_calculation_dict_safety.py,test_streaming_chunk_builder_utils.py,test_datadog_team_handler.py) — 41 passedPre-Submission checklist
Type
🐛 Bug Fix
🆕 New Feature (backport to rc line)
Generated by Claude Code