feat(bedrock): forward bedrock_tags to CreateModelInvocationJob for batch jobs - #33733
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Greptile SummaryThis PR adds
Confidence Score: 5/5Safe to merge — the change is purely additive, the security gate mirrors established patterns, and the precedence logic is backed by targeted tests. The transformation logic is straightforward, the No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/llms/bedrock/batches/transformation.py | Adds bedrock_tags forwarding to CreateModelInvocationJob: reads from litellm_params first (config-level) with is not None guard to prevent empty-list fallthrough, then falls back to optional_params; validates both sources with a strict Pydantic TypeAdapter before signing. |
| litellm/proxy/auth/auth_utils.py | Adds bedrock_tags to _BANNED_REQUEST_BODY_PARAMS, requiring admin opt-in (allow_client_side_credentials or configurable_clientside_auth_params) before a caller-supplied value can reach the AWS signing path; mirrors the existing pattern for aws_bedrock_project_id. |
| litellm/types/llms/bedrock.py | Introduces BedrockTag TypedDict with key/value string fields and tightens BedrockCreateBatchRequest.tags from Optional[List[dict]] to Optional[List[BedrockTag]]. |
| tests/test_litellm/llms/bedrock/batches/test_transformation.py | Adds nine new tests: tags forwarded from litellm_params and optional_params, empty-list-prevents-fallthrough, tags-absent-omits-key, and six malformed-shape rejections with assert_not_called() on sign_aws_request. |
| tests/test_litellm/proxy/auth/test_auth_utils.py | Adds four auth-gate tests: rejection without opt-in, proxy-wide opt-in allows tags, per-deployment opt-in allows tags, and per-deployment opt-in for a different param does not open bedrock_tags. |
| tests/test_litellm/test_router.py | Adds an async router-level test verifying deployment-level tags are applied by default and request-level tags (passed as bedrock_tags kwarg to acreate_batch) override them, locking the end-to-end precedence claim. |
Reviews (2): Last reviewed commit: "fix(proxy): require admin opt-in for req..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 1 · PR risk: 0/10 |
…vel tags for acreate_batch
…itellm_lit_4162_bedrock_batch_tags # Conflicts: # tests/test_litellm/test_router.py
Caller-supplied bedrock_tags land as AWS resource tags under the proxy's AWS identity, letting an authenticated caller forge ownership or cost-allocation labels. Add bedrock_tags to _BANNED_REQUEST_BODY_PARAMS so per-request tags need general_settings.allow_client_side_credentials or configurable_clientside_auth_params on the deployment, matching the aws_bedrock_project_id precedent. Deployment-level bedrock_tags in litellm_params are unaffected. Also stop an explicit empty bedrock_tags list in litellm_params from falling through to optional_params
…-proxy-extras to 0.4.79.post1 (#34215) * feat(spend): track prompt compression saved tokens in daily spend aggregates (#33810) * feat(spend): track prompt compression saved tokens in daily spend aggregates Native compression interception now records tokens_before/after/saved into the request litellm_metadata so savings land in the SpendLog metadata JSON under a typed compression_savings key. A single normalizer (extract_compression_saved_tokens) sums that key with Headroom guardrail tokens_saved; the two writers are disjoint and run at different stages, so summing never double-counts. The spend-log redactor now preserves purely numeric compression stats inside guardrail_response so Headroom savings survive the store_prompts_in_spend_logs=false default. compression_saved_tokens is threaded through BaseDailySpendTransaction, queue aggregation, the daily upsert blocks, a new BigInt column on all six daily spend tables, and the daily activity read path (SpendMetrics, DailySpendMetadata, raw-SQL rollups) * fix(spend): normalize legacy guardrail shapes and float token stats in compression savings reader * feat(spend): aggregate compression and prompt caching dollar savings in daily rollups Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(spend): update daily spend aggregation fixtures for savings columns Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(ui): add Cost Optimization dashboard page New left-nav Cost Optimization page under Observability that surfaces money saved by prompt compression and prompt caching. It reads the daily activity rollup (userDailyActivityCall / get_daily_activity) and never scans SpendLogs, so it stays fast at 1M+ rows. Renders a Total saved card, per-driver Compression and Prompt caching cards, a savings-over-time area chart, and a savings-by-driver donut, all aggregated in memory from the per-day metrics.compression_savings_spend and metrics.prompt_caching_savings_spend fields. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> (cherry picked from commit 3f3295b) * Merge pull request #33733 from BerriAI/litellm_lit_4162_bedrock_batch_tags feat(bedrock): forward bedrock_tags to CreateModelInvocationJob for batch jobs (cherry picked from commit 3819ee5) * bump: version 0.4.79 → 0.4.79.post1 --------- Co-authored-by: tin-berri <tin@berri.ai> Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Mateo Wang <277851410+mateo-berri@users.noreply.github.com>
Relevant issues
Linear ticket
Resolves LIT-4162
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito 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
All runs captured at c2bd869 (the PR head) against a live local proxy signing with real AWS credentials (dev account 439158074652, us-west-2). Every job below is a real
CreateModelInvocationJobaccepted by AWS, fed by a 100-record input JSONL uploaded to S3, and each tag claim is verified withaws bedrock list-tags-for-resourceagainst the returned job ARN. The deployments pinus.anthropic.claude-sonnet-4-6because Bedrock rejects batch forclaude-sonnet-5outright ("Batch inference is not supported for the requested model")Before, captured at staging commit 214945a (predates this PR's merge) with the same config, live proxy, and real credentials. The customer's original attempt, top-level
tagsin the request body, dies on the tag-routing field before any Bedrock call:{ "error": { "message": "2 validation errors for GenericLiteLLMParams\ntags.0\n Input should be a valid string [type=string_type, input_value={'key': 'application', 'value': 'genai-proxy'}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.13/v/string_type\ntags.1\n Input should be a valid string [type=string_type, input_value={'key': 'team', 'value': 'ml-platform'}, input_type=dict]\n For further information visit https://errors.pydantic.dev/2.13/v/string_type", "type": "internal_server_error", "param": "None", "code": "500" } }And
bedrock_tagsis silently dropped there: the exact request from item 1 below creates real jobxe29mg490si5, butlist-tags-for-resourceon it returns{"tags": []}Proxy config (port 58731):
bedrock_tags, the customer flow with the field renamed fromtags, against a deployment that opts in viaconfigurable_clientside_auth_params:{ "id": "arn:aws:bedrock:us-west-2:439158074652:model-invocation-job/79rvz20ap4uq", "completion_window": "24h", "endpoint": "/v1/chat/completions", "input_file_id": "s3://litellm-lit4162-batch-439158074652/batch-input.jsonl", "object": "batch", "status": "validating" }The tags are on the real AWS resource:
{ "tags": [ {"key": "application", "value": "genai-proxy"}, {"key": "team", "value": "ml-platform"} ] }And the job is retrievable through the proxy like any OpenAI batch:
{"id": "arn:aws:bedrock:us-west-2:439158074652:model-invocation-job/79rvz20ap4uq", "status": "validating", "object": "batch"}"bedrock_tags": ["application=genai-proxy"]:{"error":{"message":"Invalid 'bedrock_tags' value. Expected a list of {'key': <str>, 'value': <str>} dicts, e.g. [{'key': 'team', 'value': 'genai'}]. Got: ['application=genai-proxy']","type":"internal_server_error","param":"None","code":"500"}}bedrock-batch-sonnet-taggedwithoutbedrock_tagscreates job817c23imuul0, andlist-tags-for-resourceon it returns:{"tags": [{"key": "cost-center", "value": "ml-platform"}]}bedrock-batch-sonnet-tagged-clientsidewithbedrock_tags: [{"key": "cost-center", "value": "override-team"}]creates jobdlm10mow7vf7, andlist-tags-for-resourceon it returns:{"tags": [{"key": "cost-center", "value": "override-team"}]}bedrock_tagsagainst any deployment without the opt-in is rejected before any AWS call. Both the plain request againstbedrock-batch-sonnetand the override attempt againstbedrock-batch-sonnet-taggedreturn:{"error":{"message":"Authentication Error, Rejected Request: bedrock_tags is not allowed in request body. Clientside passthrough requires explicit admin opt-in via either `general_settings.allow_client_side_credentials = true` (proxy-wide) or `configurable_clientside_auth_params` on the deployment in your proxy config.yaml. Relevant Issue: https://huntr.com/bounties/4001e1a2-7b7a-4776-a3ae-e6692ec3d997","type":"auth_error","param":"None","code":"401"}}Type
🆕 New Feature
Changes
A customer (Pylon #5645) runs under an SCP that denies Bedrock batch jobs without resource tags, and LiteLLM had no way to pass any:
BedrockBatchesConfig.transform_create_batch_requestnever populated thetagsfield ofCreateModelInvocationJob. Sending top-leveltagsin the request body can never work for this becauseGenericLiteLLMParams.tagsis the LiteLLM tag-routing field typedOptional[List[str]], so Bedrock's dict-shaped tags fail validation with a 500. The new parameter is therefore namedbedrock_tagstransform_create_batch_requestnow readsbedrock_tagsfromlitellm_params(with anoptional_paramsfallback, same precedence asaws_batch_role_arn), validates it strictly as a list of{"key": str, "value": str}via a PydanticTypeAdapterraising a clearValueErroron bad shapes, and sets it on the signed request.BedrockCreateBatchRequest.tagsis tightened fromOptional[List[dict]]toOptional[List[BedrockTag]]. SinceGenericLiteLLMParamsallows extras and the router merges request kwargs over deploymentlitellm_params, the one key works both per request and per deployment in proxy config, with the request value winningNine regression tests added to
tests/test_litellm/llms/bedrock/batches/test_transformation.py: tags forwarded fromlitellm_paramsand fromoptional_params, thetagskey omitted entirely whenbedrock_tagsis absent, and six malformed shapes rejected before signing. Eight of the nine fail on the parent commitA router-level test in
tests/test_litellm/test_router.pylocks the precedence claim end-to-end:Router.acreate_batchwithbedrock_tagson the deployment applies the config tags to the signedCreateModelInvocationJobbody, and the same call with request-levelbedrock_tagsoverrides them. Mutating the router merge to let deploymentlitellm_paramswin over request kwargs fails the testA Veria review finding pointed out that request-supplied
bedrock_tagslet any authenticated caller stamp arbitrary ownership or cost-allocation labels on jobs created under the proxy's AWS identity.bedrock_tagsis now in_BANNED_REQUEST_BODY_PARAMS, the same gate that coversaws_bedrock_project_id, so per-request tags require eithergeneral_settings.allow_client_side_credentials: trueproxy-wide orconfigurable_clientside_auth_params: ["bedrock_tags"]on the deployment; deployment-levelbedrock_tagskeep working with no opt-in. The same commit stops an explicit emptybedrock_tagslist inlitellm_paramsfrom falling through to the request value (a Greptile note). Four tests intests/test_litellm/proxy/auth/test_auth_utils.pycover the rejection, both opt-ins, and that an opt-in for a different param does not openbedrock_tags; one more transformation test locks the empty-list behaviorFinal Attestation