Skip to content

feat(bedrock): support file delete and list for S3-backed managed files - #39836

Open
mateo-berri wants to merge 6 commits into
litellm_internal_stagingfrom
litellm_lit_6975_bedrock_files_delete_list
Open

feat(bedrock): support file delete and list for S3-backed managed files#39836
mateo-berri wants to merge 6 commits into
litellm_internal_stagingfrom
litellm_lit_6975_bedrock_files_delete_list

Conversation

@mateo-berri

@mateo-berri mateo-berri commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • DELETE /v1/files/{id} on a Bedrock managed file returned HTTP 500
  • GET /v1/files?target_model_names=<bedrock model> returned HTTP 500 too
  • The Bedrock files provider had no delete or list implementation
  • Deleted batch inputs stayed in the customer's S3 bucket forever

How it solves it:

  • Delete sends a SigV4-signed S3 DeleteObject for the managed key
  • List sends a SigV4-signed S3 ListObjectsV2 over the managed prefixes
  • purpose picks the bucket and prefix: batch_output lists the configured output bucket under litellm-batch-outputs/, batch the input bucket under litellm-bedrock-files
  • List follows S3 continuation tokens, so a prefix with more than 1000 objects comes back whole, stopping at 10,000 files (the OpenAI limit ceiling) with the request timeout on every page
  • Both check the id against the deployment's configured bucket first
  • The delete path and the list route now pass the deployment's bucket through
  • Builds on fix(bedrock): support SigV4 file deletion for S3 batch files (#39715) #39731 (delete only, CLA unsigned) and adds list plus tests

User Flow

Before: a developer cleaning up Bedrock batch inputs through the gateway gets HTTP 500 on delete and on listing, and the objects stay in their S3 bucket

  1. They send POST https://litellm-domain/v1/files with purpose=batch, target_model_names=bedrock-claude-haiku-batch, and a JSONL file; they get 200 with a long scrambled id and "status": "uploaded"
  2. They send GET https://litellm-domain/v1/files/{file_id} and get 200 with the same metadata
  3. They send GET https://litellm-domain/v1/files?purpose=batch&target_model_names=bedrock-claude-haiku-batch and get HTTP 500 BedrockFilesConfig does not support file listing
  4. They send GET https://litellm-domain/v1/files?purpose=batch_output&target_model_names=bedrock-claude-haiku-batch and get the same HTTP 500
  5. They send DELETE https://litellm-domain/v1/files/{file_id} and get HTTP 500 BedrockFilesConfig does not support file deletion
  6. GET https://litellm-domain/v1/files/{file_id} still returns 200, and the JSONL object is still sitting in their S3 bucket

After: the same developer lists the batch files in their bucket and deletes one, and the object is gone from S3

  1. They send POST https://litellm-domain/v1/files with purpose=batch, target_model_names=bedrock-claude-haiku-batch, and a JSONL file; they get 200 with a long scrambled id and "status": "uploaded"
  2. They send GET https://litellm-domain/v1/files/{file_id} and get 200 with the same metadata
  3. They send GET https://litellm-domain/v1/files?purpose=batch&target_model_names=bedrock-claude-haiku-batch and get 200 with the batch inputs stored in that deployment's bucket, each carrying its s3://bucket/key id, size, and upload time
  4. They send GET https://litellm-domain/v1/files?purpose=batch_output&target_model_names=bedrock-claude-haiku-batch and get 200 with the result objects a finished batch job wrote to the deployment's output bucket, with "purpose": "batch_output"
  5. They send DELETE https://litellm-domain/v1/files/{file_id} and get 200 with the file's metadata (id, bytes, filename, purpose, status)
  6. GET https://litellm-domain/v1/files/{file_id} now returns 404, the file is gone from the list, and the object is gone from their S3 bucket

Relevant issues

Supersedes #39731, which added the delete transform only and could not be merged without a signed CLA. Credit to its author for the SigV4 DeleteObject approach

Linear ticket

Resolves LIT-6975

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • 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)

Screenshots / Proof of Fix

Each leg ran two proxy processes booted from that commit, each with --num_workers 2, sharing one Postgres: instance A takes the uploads and the post-delete checks, instance B the retrieve, list, and delete calls, so every read after a write crosses instances. Real AWS: a Bedrock deployment (bedrock-claude-haiku-batch) with s3_bucket_name: litellm-lit6975-qa and s3_output_bucket_name: litellm-lit6975-qa/batch-out in us-west-2, plus an OpenAI deployment on the same routes. aws s3api list-objects-v2 at the end of each leg shows what is left in the bucket, and one aws s3 cp stands in for the result object a finished Bedrock batch job writes under the output bucket

Before (b3c867c, merge base)

Every Bedrock list and delete call returns HTTP 500 and all three objects stay in S3; the OpenAI deployment works either way

Before log (both instances)

Bedrock deployment: two proxy processes sharing one Postgres, each --num_workers 2: A=http://127.0.0.1:40433 (uploads), B=http://127.0.0.1:57590 (retrieve, list, delete)

  1. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:40433/v1/files -H 'Authorization: Bearer $MK' -F purpose=batch -F file=@lit6975_batch_input.jsonl -F target_model_names=bedrock-claude-haiku-batch
    {"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCwzZDc1YjQwNS0yMDgxLTRiMDItYjY1ZS05YmMxNTkyOGNhNGI7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC05MTNkNDE4Ny1hNWJkLTRhODYtYjIwZi1mNzI3ZTlkY2Y3NDAuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"created_at":1788572514,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-913d4187-a5bd-4a86-b20f-f727e9dcf740.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}
    HTTP 200
    FILE_ID=bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCwzZDc1YjQwNS0yMDgxLTRiMDItYjY1ZS05YmMxNTkyOGNhNGI7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC05MTNkNDE4Ny1hNWJkLTRhODYtYjIwZi1mNzI3ZTlkY2Y3NDAuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I
    
  2. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:40433/v1/files -H 'Authorization: Bearer $MK' -F purpose=user_data -F file=@lit6975_note.txt -F target_model_names=bedrock-claude-haiku-batch
    {"id":"bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsNjU3MjFlYWMtY2JiYi00MDZlLWJlZTAtMThkNWQxNjg1NTk0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzL2Q1Zjc3ZWY0MTllYjQwODNhMzI1ZTRkYzE0OTE5OGY2LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":25,"created_at":1788572514,"filename":"d5f77ef419eb4083a325e4dc149198f6-lit6975_note.txt","object":"file","purpose":"user_data","status":"uploaded","expires_at":null,"status_details":null}
    HTTP 200
    FILE_ID2=bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsNjU3MjFlYWMtY2JiYi00MDZlLWJlZTAtMThkNWQxNjg1NTk0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzL2Q1Zjc3ZWY0MTllYjQwODNhMzI1ZTRkYzE0OTE5OGY2LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I
    
  3. aws s3 cp lit6975_batch_output.jsonl.out s3://litellm-lit6975-qa/batch-out/litellm-batch-outputs/lit6975-job/input.jsonl.out (stands in for the result a finished Bedrock batch job writes under s3_output_bucket_name)
    Completed 271 Bytes/271 Bytes (1.3 KiB/s) with 1 file(s) remaining
    upload: ./lit6975_batch_output.jsonl.out to s3://litellm-lit6975-qa/batch-out/litellm-batch-outputs/lit6975-job/input.jsonl.out
    
  4. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:57590/v1/files/$FILE_ID -H 'Authorization: Bearer $MK'
    {"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCwzZDc1YjQwNS0yMDgxLTRiMDItYjY1ZS05YmMxNTkyOGNhNGI7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC05MTNkNDE4Ny1hNWJkLTRhODYtYjIwZi1mNzI3ZTlkY2Y3NDAuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"created_at":1788572514,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-913d4187-a5bd-4a86-b20f-f727e9dcf740.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}
    HTTP 200
    
  5. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:57590/v1/files?purpose=batch&target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK'
    {"error":{"message":"BedrockFilesConfig does not support file listing","type":"None","param":"None","code":"500"}}
    HTTP 500
    
  6. aws s3 sync lit6975_page_objs/ s3://litellm-lit6975-qa/litellm-bedrock-files/lit6975-page/ --only-show-errors (stages 1001 managed inputs so the S3 listing spans two 1000-key pages)
    synced
    
  7. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:57590/v1/files?purpose=batch&target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK' | python3 lit6975_page_summary.py (counts the listing instead of dumping 1003 entries)
    HTTP 500
    0 files listed (0 staged under litellm-bedrock-files/lit6975-page/); first: None; last: None
    
  8. aws s3 rm --recursive s3://litellm-lit6975-qa/litellm-bedrock-files/lit6975-page/ --only-show-errors (removes the 1001 staged inputs again)
    removed
    
  9. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:57590/v1/files?target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK'
    {"error":{"message":"BedrockFilesConfig does not support file listing","type":"None","param":"None","code":"500"}}
    HTTP 500
    
  10. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:57590/v1/files?purpose=batch_output&target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK'
{"error":{"message":"BedrockFilesConfig does not support file listing","type":"None","param":"None","code":"500"}}
HTTP 500
  1. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:57590/v1/files' -H 'Authorization: Bearer $MK'
{"object":"list","data":[{"id":"bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsNjU3MjFlYWMtY2JiYi00MDZlLWJlZTAtMThkNWQxNjg1NTk0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzL2Q1Zjc3ZWY0MTllYjQwODNhMzI1ZTRkYzE0OTE5OGY2LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":25,"created_at":1788572514,"filename":"d5f77ef419eb4083a325e4dc149198f6-lit6975_note.txt","object":"file","purpose":"user_data","status":"uploaded","expires_at":null,"status_details":null},{"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCwzZDc1YjQwNS0yMDgxLTRiMDItYjY1ZS05YmMxNTkyOGNhNGI7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC05MTNkNDE4Ny1hNWJkLTRhODYtYjIwZi1mNzI3ZTlkY2Y3NDAuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"created_at":1788572514,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-913d4187-a5bd-4a86-b20f-f727e9dcf740.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}],"first_id":"bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsNjU3MjFlYWMtY2JiYi00MDZlLWJlZTAtMThkNWQxNjg1NTk0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzL2Q1Zjc3ZWY0MTllYjQwODNhMzI1ZTRkYzE0OTE5OGY2LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","last_id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCwzZDc1YjQwNS0yMDgxLTRiMDItYjY1ZS05YmMxNTkyOGNhNGI7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC05MTNkNDE4Ny1hNWJkLTRhODYtYjIwZi1mNzI3ZTlkY2Y3NDAuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","has_more":false}
HTTP 200
  1. curl -s -w '\nHTTP %{http_code}' -X DELETE http://127.0.0.1:57590/v1/files/$FILE_ID -H 'Authorization: Bearer $MK'
{"error":{"message":"BedrockFilesConfig does not support file deletion","type":"None","param":"None","code":"500"}}
HTTP 500
  1. curl -s -w '\nHTTP %{http_code}' -X DELETE http://127.0.0.1:57590/v1/files/$FILE_ID2 -H 'Authorization: Bearer $MK'
{"error":{"message":"BedrockFilesConfig does not support file deletion","type":"None","param":"None","code":"500"}}
HTTP 500
  1. curl -s -w '\nHTTP %{http_code}' -X DELETE 'http://127.0.0.1:57590/v1/files/$OUT_ID?model=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK'
{"error":{"message":"BedrockFilesConfig does not support file deletion","type":"None","param":"None","code":"500"}}
HTTP 500
  1. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:40433/v1/files/$FILE_ID -H 'Authorization: Bearer $MK' (after delete, via the other instance)
{"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCwzZDc1YjQwNS0yMDgxLTRiMDItYjY1ZS05YmMxNTkyOGNhNGI7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC05MTNkNDE4Ny1hNWJkLTRhODYtYjIwZi1mNzI3ZTlkY2Y3NDAuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"created_at":1788572514,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-913d4187-a5bd-4a86-b20f-f727e9dcf740.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}
HTTP 200
  1. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:40433/v1/files?target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK' (after delete, via the other instance)
{"error":{"message":"BedrockFilesConfig does not support file listing","type":"None","param":"None","code":"500"}}
HTTP 500
  1. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:40433/v1/files?purpose=batch_output&target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK' (after delete, via the other instance)
{"error":{"message":"BedrockFilesConfig does not support file listing","type":"None","param":"None","code":"500"}}
HTTP 500
  1. aws s3api list-objects-v2 --bucket litellm-lit6975-qa --query 'Contents[].Key' --output text (what is left in S3)
batch-out/litellm-batch-outputs/lit6975-job/input.jsonl.out	litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-913d4187-a5bd-4a86-b20f-f727e9dcf740.jsonl	litellm-bedrock-files/d5f77ef419eb4083a325e4dc149198f6-lit6975_note.txt

OpenAI-backed deployment on the same routes (unchanged provider path, same two instances)

  1. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:40433/v1/files -H 'Authorization: Bearer $MK' -F purpose=batch -F file=@lit6975_openai_batch_input.jsonl -F target_model_names=openai-gpt-mini
    {"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw4ZjFlZGM2NC02MWIyLTQxNjEtYWU2Mi03NjMwMTdmMmJiM2Y7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1HdFZHZVg4alRIVDk5ZnpQOFFMUVBwO2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2","bytes":214,"created_at":1788572536,"filename":"modified_file.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":1791164536,"status_details":null}
    HTTP 200
    FILE_ID3=bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw4ZjFlZGM2NC02MWIyLTQxNjEtYWU2Mi03NjMwMTdmMmJiM2Y7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1HdFZHZVg4alRIVDk5ZnpQOFFMUVBwO2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2
    
  2. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:57590/v1/files?purpose=batch&target_model_names=openai-gpt-mini' -H 'Authorization: Bearer $MK' | python3 lit6975_list_summary.py (summarises the OpenAI listing instead of dumping it)
    HTTP 200
    1 files listed from OpenAI; newest: bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw4ZjFlZGM2NC02MWIyLTQxNjEtYWU2Mi03NjMwMTdmMmJiM2Y7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1HdFZHZVg4alRIVDk5ZnpQOFFMUVBwO2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2 (modified_file.jsonl)
    
  3. curl -s -w '\nHTTP %{http_code}' -X DELETE http://127.0.0.1:57590/v1/files/$FILE_ID3 -H 'Authorization: Bearer $MK'
    {"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw4ZjFlZGM2NC02MWIyLTQxNjEtYWU2Mi03NjMwMTdmMmJiM2Y7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1HdFZHZVg4alRIVDk5ZnpQOFFMUVBwO2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2","bytes":214,"object":"file","status":"uploaded","purpose":"batch","filename":"modified_file.jsonl","created_at":1788572536,"expires_at":1791164536,"status_details":null}
    HTTP 200
    
  4. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:40433/v1/files/$FILE_ID3 -H 'Authorization: Bearer $MK' (after delete, via the other instance)
    {"error":{"message":"File not found: bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw4ZjFlZGM2NC02MWIyLTQxNjEtYWU2Mi03NjMwMTdmMmJiM2Y7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1HdFZHZVg4alRIVDk5ZnpQOFFMUVBwO2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2","type":"None","param":"None","code":"404"}}
    HTTP 404
    

After (7bddb65, PR tip)

Listing returns the batch inputs with their s3:// ids and follows S3 continuation tokens past 1000 keys, purpose=batch_output returns the result object from the output bucket, every delete returns 200, the retrieve afterwards is 404 through the other instance, and the bucket is empty

After log (both instances)

Bedrock deployment: two proxy processes sharing one Postgres, each --num_workers 2: A=http://127.0.0.1:32309 (uploads), B=http://127.0.0.1:48732 (retrieve, list, delete)

  1. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:32309/v1/files -H 'Authorization: Bearer $MK' -F purpose=batch -F file=@lit6975_batch_input.jsonl -F target_model_names=bedrock-claude-haiku-batch
    {"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw3MDFkMWQ1ZS0zZjBlLTQ0N2MtYjZjNC02Yzk0ZmExYWE1Nzg7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC02ZmNiYzAwMS1lN2RiLTQxNDktOTlkMi00NDFkNjIzZjZkNGIuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"created_at":1788584766,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}
    HTTP 200
    FILE_ID=bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw3MDFkMWQ1ZS0zZjBlLTQ0N2MtYjZjNC02Yzk0ZmExYWE1Nzg7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC02ZmNiYzAwMS1lN2RiLTQxNDktOTlkMi00NDFkNjIzZjZkNGIuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I
    
  2. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:32309/v1/files -H 'Authorization: Bearer $MK' -F purpose=user_data -F file=@lit6975_note.txt -F target_model_names=bedrock-claude-haiku-batch
    {"id":"bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsZDEzMTg1OTctOTMwOS00MDM1LThiZDMtZjI5OTliMGVmNjA0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzLzUyNjg1Njg4ZmUxMDRjM2ZhMTRlMmVkNThkNWM5NmU1LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":25,"created_at":1788584767,"filename":"52685688fe104c3fa14e2ed58d5c96e5-lit6975_note.txt","object":"file","purpose":"user_data","status":"uploaded","expires_at":null,"status_details":null}
    HTTP 200
    FILE_ID2=bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsZDEzMTg1OTctOTMwOS00MDM1LThiZDMtZjI5OTliMGVmNjA0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzLzUyNjg1Njg4ZmUxMDRjM2ZhMTRlMmVkNThkNWM5NmU1LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I
    
  3. aws s3 cp lit6975_batch_output.jsonl.out s3://litellm-lit6975-qa/batch-out/litellm-batch-outputs/lit6975-job/input.jsonl.out (stands in for the result a finished Bedrock batch job writes under s3_output_bucket_name)
    Completed 271 Bytes/271 Bytes (1.5 KiB/s) with 1 file(s) remaining
    upload: ./lit6975_batch_output.jsonl.out to s3://litellm-lit6975-qa/batch-out/litellm-batch-outputs/lit6975-job/input.jsonl.out
    
  4. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:48732/v1/files/$FILE_ID -H 'Authorization: Bearer $MK'
    {"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw3MDFkMWQ1ZS0zZjBlLTQ0N2MtYjZjNC02Yzk0ZmExYWE1Nzg7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC02ZmNiYzAwMS1lN2RiLTQxNDktOTlkMi00NDFkNjIzZjZkNGIuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"created_at":1788584766,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}
    HTTP 200
    
  5. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:48732/v1/files?purpose=batch&target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK'
    [{"id":"s3://litellm-lit6975-qa/litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl","bytes":360,"created_at":1788584767,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null},{"id":"s3://litellm-lit6975-qa/litellm-bedrock-files/52685688fe104c3fa14e2ed58d5c96e5-lit6975_note.txt","bytes":25,"created_at":1788584768,"filename":"52685688fe104c3fa14e2ed58d5c96e5-lit6975_note.txt","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}]
    HTTP 200
    
  6. aws s3 sync lit6975_page_objs/ s3://litellm-lit6975-qa/litellm-bedrock-files/lit6975-page/ --only-show-errors (stages 1001 managed inputs so the S3 listing spans two 1000-key pages)
    synced
    
  7. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:48732/v1/files?purpose=batch&target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK' | python3 lit6975_page_summary.py (counts the listing instead of dumping 1003 entries)
    HTTP 200
    1003 files listed (1001 staged under litellm-bedrock-files/lit6975-page/); first: s3://litellm-lit6975-qa/litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl; last: s3://litellm-lit6975-qa/litellm-bedrock-files/lit6975-page/obj-1000.jsonl
    
  8. aws s3 rm --recursive s3://litellm-lit6975-qa/litellm-bedrock-files/lit6975-page/ --only-show-errors (removes the 1001 staged inputs again)
    removed
    
  9. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:48732/v1/files?target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK'
    [{"id":"s3://litellm-lit6975-qa/litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl","bytes":360,"created_at":1788584767,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null},{"id":"s3://litellm-lit6975-qa/litellm-bedrock-files/52685688fe104c3fa14e2ed58d5c96e5-lit6975_note.txt","bytes":25,"created_at":1788584768,"filename":"52685688fe104c3fa14e2ed58d5c96e5-lit6975_note.txt","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}]
    HTTP 200
    
  10. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:48732/v1/files?purpose=batch_output&target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK'
[{"id":"s3://litellm-lit6975-qa/batch-out/litellm-batch-outputs/lit6975-job/input.jsonl.out","bytes":271,"created_at":1788584768,"filename":"input.jsonl.out","object":"file","purpose":"batch_output","status":"uploaded","expires_at":null,"status_details":null}]
HTTP 200
  1. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:48732/v1/files' -H 'Authorization: Bearer $MK'
{"object":"list","data":[{"id":"bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsZDEzMTg1OTctOTMwOS00MDM1LThiZDMtZjI5OTliMGVmNjA0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzLzUyNjg1Njg4ZmUxMDRjM2ZhMTRlMmVkNThkNWM5NmU1LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":25,"created_at":1788584767,"filename":"52685688fe104c3fa14e2ed58d5c96e5-lit6975_note.txt","object":"file","purpose":"user_data","status":"uploaded","expires_at":null,"status_details":null},{"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw3MDFkMWQ1ZS0zZjBlLTQ0N2MtYjZjNC02Yzk0ZmExYWE1Nzg7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC02ZmNiYzAwMS1lN2RiLTQxNDktOTlkMi00NDFkNjIzZjZkNGIuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"created_at":1788584766,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null},{"id":"bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsNjU3MjFlYWMtY2JiYi00MDZlLWJlZTAtMThkNWQxNjg1NTk0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzL2Q1Zjc3ZWY0MTllYjQwODNhMzI1ZTRkYzE0OTE5OGY2LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":25,"created_at":1788572514,"filename":"d5f77ef419eb4083a325e4dc149198f6-lit6975_note.txt","object":"file","purpose":"user_data","status":"uploaded","expires_at":null,"status_details":null},{"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCwzZDc1YjQwNS0yMDgxLTRiMDItYjY1ZS05YmMxNTkyOGNhNGI7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC05MTNkNDE4Ny1hNWJkLTRhODYtYjIwZi1mNzI3ZTlkY2Y3NDAuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"created_at":1788572514,"filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-913d4187-a5bd-4a86-b20f-f727e9dcf740.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":null,"status_details":null}],"first_id":"bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsZDEzMTg1OTctOTMwOS00MDM1LThiZDMtZjI5OTliMGVmNjA0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzLzUyNjg1Njg4ZmUxMDRjM2ZhMTRlMmVkNThkNWM5NmU1LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","last_id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCwzZDc1YjQwNS0yMDgxLTRiMDItYjY1ZS05YmMxNTkyOGNhNGI7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC05MTNkNDE4Ny1hNWJkLTRhODYtYjIwZi1mNzI3ZTlkY2Y3NDAuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","has_more":false}
HTTP 200
  1. curl -s -w '\nHTTP %{http_code}' -X DELETE http://127.0.0.1:48732/v1/files/$FILE_ID -H 'Authorization: Bearer $MK'
{"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw3MDFkMWQ1ZS0zZjBlLTQ0N2MtYjZjNC02Yzk0ZmExYWE1Nzg7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC02ZmNiYzAwMS1lN2RiLTQxNDktOTlkMi00NDFkNjIzZjZkNGIuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":360,"object":"file","status":"uploaded","purpose":"batch","filename":"litellm-bedrock-files-us.anthropic.claude-haiku-4-5-20251001-v1-0-6fcbc001-e7db-4149-99d2-441d623f6d4b.jsonl","created_at":1788584766,"expires_at":null,"status_details":null}
HTTP 200
  1. curl -s -w '\nHTTP %{http_code}' -X DELETE http://127.0.0.1:48732/v1/files/$FILE_ID2 -H 'Authorization: Bearer $MK'
{"id":"bGl0ZWxsbV9wcm94eTp0ZXh0L3BsYWluO3VuaWZpZWRfaWQsZDEzMTg1OTctOTMwOS00MDM1LThiZDMtZjI5OTliMGVmNjA0O3RhcmdldF9tb2RlbF9uYW1lcyxiZWRyb2NrLWNsYXVkZS1oYWlrdS1iYXRjaDtsbG1fb3V0cHV0X2ZpbGVfaWQsczM6Ly9saXRlbGxtLWxpdDY5NzUtcWEvbGl0ZWxsbS1iZWRyb2NrLWZpbGVzLzUyNjg1Njg4ZmUxMDRjM2ZhMTRlMmVkNThkNWM5NmU1LWxpdDY5NzVfbm90ZS50eHQ7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","bytes":25,"object":"file","status":"uploaded","purpose":"user_data","filename":"52685688fe104c3fa14e2ed58d5c96e5-lit6975_note.txt","created_at":1788584767,"expires_at":null,"status_details":null}
HTTP 200
  1. curl -s -w '\nHTTP %{http_code}' -X DELETE 'http://127.0.0.1:48732/v1/files/$OUT_ID?model=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK'
{"id":"s3://litellm-lit6975-qa/batch-out/litellm-batch-outputs/lit6975-job/input.jsonl.out","deleted":true,"object":"file"}
HTTP 200
  1. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:32309/v1/files/$FILE_ID -H 'Authorization: Bearer $MK' (after delete, via the other instance)
{"error":{"message":"File not found: bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCw3MDFkMWQ1ZS0zZjBlLTQ0N2MtYjZjNC02Yzk0ZmExYWE1Nzg7dGFyZ2V0X21vZGVsX25hbWVzLGJlZHJvY2stY2xhdWRlLWhhaWt1LWJhdGNoO2xsbV9vdXRwdXRfZmlsZV9pZCxzMzovL2xpdGVsbG0tbGl0Njk3NS1xYS9saXRlbGxtLWJlZHJvY2stZmlsZXMtdXMuYW50aHJvcGljLmNsYXVkZS1oYWlrdS00LTUtMjAyNTEwMDEtdjEtMC02ZmNiYzAwMS1lN2RiLTQxNDktOTlkMi00NDFkNjIzZjZkNGIuanNvbmw7bGxtX291dHB1dF9maWxlX21vZGVsX2lkLDlhNzQyYjNlYjA0Nzc4MDdlZWY5NjNiOWUzYzI1NTlhMTZkZDJjZjczMTRiZDU3N2QwOWQ3ZWZiNWRlOTY2M2I","type":"None","param":"None","code":"404"}}
HTTP 404
  1. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:32309/v1/files?target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK' (after delete, via the other instance)
[]
HTTP 200
  1. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:32309/v1/files?purpose=batch_output&target_model_names=bedrock-claude-haiku-batch' -H 'Authorization: Bearer $MK' (after delete, via the other instance)
[]
HTTP 200
  1. aws s3api list-objects-v2 --bucket litellm-lit6975-qa --query 'Contents[].Key' --output text (what is left in S3)
None

OpenAI-backed deployment on the same routes (unchanged provider path, same two instances)

  1. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:32309/v1/files -H 'Authorization: Bearer $MK' -F purpose=batch -F file=@lit6975_openai_batch_input.jsonl -F target_model_names=openai-gpt-mini
    {"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCxjNWU5ZGRmYS05MmRlLTQ4MTMtOWIxYy00OWRlNGNjNWZlMTQ7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1VTXFzazVMc3hIMW9GazcxQjdzQkI1O2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2","bytes":214,"created_at":1788584789,"filename":"modified_file.jsonl","object":"file","purpose":"batch","status":"uploaded","expires_at":1791176789,"status_details":null}
    HTTP 200
    FILE_ID3=bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCxjNWU5ZGRmYS05MmRlLTQ4MTMtOWIxYy00OWRlNGNjNWZlMTQ7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1VTXFzazVMc3hIMW9GazcxQjdzQkI1O2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2
    
  2. curl -s -w '\nHTTP %{http_code}' 'http://127.0.0.1:48732/v1/files?purpose=batch&target_model_names=openai-gpt-mini' -H 'Authorization: Bearer $MK' | python3 lit6975_list_summary.py (summarises the OpenAI listing instead of dumping it)
    HTTP 200
    1 files listed from OpenAI; newest: bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCxjNWU5ZGRmYS05MmRlLTQ4MTMtOWIxYy00OWRlNGNjNWZlMTQ7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1VTXFzazVMc3hIMW9GazcxQjdzQkI1O2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2 (modified_file.jsonl)
    
  3. curl -s -w '\nHTTP %{http_code}' -X DELETE http://127.0.0.1:48732/v1/files/$FILE_ID3 -H 'Authorization: Bearer $MK'
    {"id":"bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCxjNWU5ZGRmYS05MmRlLTQ4MTMtOWIxYy00OWRlNGNjNWZlMTQ7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1VTXFzazVMc3hIMW9GazcxQjdzQkI1O2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2","bytes":214,"object":"file","status":"uploaded","purpose":"batch","filename":"modified_file.jsonl","created_at":1788584789,"expires_at":1791176789,"status_details":null}
    HTTP 200
    
  4. curl -s -w '\nHTTP %{http_code}' http://127.0.0.1:32309/v1/files/$FILE_ID3 -H 'Authorization: Bearer $MK' (after delete, via the other instance)
    {"error":{"message":"File not found: bGl0ZWxsbV9wcm94eTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07dW5pZmllZF9pZCxjNWU5ZGRmYS05MmRlLTQ4MTMtOWIxYy00OWRlNGNjNWZlMTQ7dGFyZ2V0X21vZGVsX25hbWVzLG9wZW5haS1ncHQtbWluaTtsbG1fb3V0cHV0X2ZpbGVfaWQsZmlsZS1VTXFzazVMc3hIMW9GazcxQjdzQkI1O2xsbV9vdXRwdXRfZmlsZV9tb2RlbF9pZCw5NTRiODdmZjNhYzRhYWQwZDIyN2RkODIzNjZkMGM2MjYyMzU3NjA0M2E3Y2U5MDU0MWU4MzgxMWUyN2E2MTU2","type":"None","param":"None","code":"404"}}
    HTTP 404
    

Observations from the run:

  • Managed-id delete answers with the stored file object, not deleted: true
  • Same shape at the merge base for OpenAI: pre-existing, not this PR
  • Raw s3:// id delete answers deleted: true
  • Unfiltered list covers the input bucket only
  • Output bucket QA'd as a prefixed name of the same bucket
  • 1001 staged inputs list whole across two S3 pages
  • Same run on the PR merged into current staging (c81a901849) answers identically

Type

🆕 New Feature
🐛 Bug Fix

Caveats (if any)

Low

  • A prefix past 10,000 managed objects lists its first 10,000, the most OpenAI returns per call; limit and after reach no provider listing path yet (OpenAI and Azure included), so threading them through is separate work
  • The listed purpose comes from the key prefix, so a user_data upload lists as batch; reading it per object would cost one HeadObject per key
  • A list without purpose covers the input bucket only; pass purpose=batch_output to list a separate s3_output_bucket_name
  • Deleting a managed id answers with the stored file object rather than deleted: true, the pre-existing managed-files behavior for every provider (the OpenAI leg at the merge base shows the same shape)
  • Raw s3:// ids still work on delete when require_managed_files is off, scoped to the deployment's buckets and managed prefixes, the same trust boundary the content route already has
  • proxy_store_model_in_db_tests red staging-wide; test(store_model_in_db): assert the 400 contract in the unknown-model spend log test #39842 fixes it

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

Note

Medium Risk
Changes touch S3 delete/list against customer buckets with SigV4 and bucket validation; misconfiguration or credential handoff bugs could delete or expose wrong objects, though scope is limited to managed prefixes and deployment snapshots.

Overview
Adds SigV4-signed S3 delete and list for Bedrock managed batch files, replacing the previous NotImplementedError paths on DELETE and GET /v1/files when routed to Bedrock.

Bedrock BedrockFilesConfig now builds signed DeleteObject and ListObjectsV2 requests over LiteLLM-managed prefixes (litellm-bedrock-files, litellm-batch-outputs/), validates s3:// ids against the deployment bucket, and maps listing XML to OpenAI file objects with purpose filtering (batch vs batch_output, including optional output bucket). Shared signing is generalized via _sign_s3_empty_body_request and S3_SIGNED_REQUEST_HEADERS_PARAM.

Listing pagination is extended in the files HTTP handler: providers can implement transform_list_files_next_request; sync/async list loops follow S3 continuation tokens, apply timeouts on each page, and cap results at MAX_FILE_LIST_LIMIT (centralized in litellm.constants).

Proxy and managed-files wiring passes the deployment credential snapshot (_litellm_internal_model_credentials) into router delete/list calls so bucket resolution does not depend on env vars alone; litellm.file_delete also merges trusted credentials for custom provider configs.

Reviewed by Cursor Bugbot for commit 7bddb65. Bugbot is set up for automated code reviews on this repo. Configure here.

@mateo-berri
mateo-berri requested a review from a team September 5, 2026 00:34
@codspeed-hq

codspeed-hq Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_lit_6975_bedrock_files_delete_list (7bddb65) with litellm_internal_staging (9c05c15)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds S3-backed delete and paginated listing support for Bedrock managed files, including purpose-specific input and output bucket selection, trusted deployment credential forwarding, SigV4 request signing, and regression coverage. The latest commit only deduplicates next-page header preparation without changing behavior

Confidence Score: 5/5

The PR appears safe to merge, with no outstanding correctness, security, or repository-rule issues

The previously reported output-bucket and pagination concerns are fixed, all earlier threads are resolved, and the latest helper extraction preserves the existing next-page header behavior

Important Files Changed

Filename Overview
litellm/llms/bedrock/files/transformation.py Implements bucket-scoped, SigV4-signed S3 deletion and purpose-aware paginated listing
litellm/llms/custom_httpx/llm_http_handler.py Adds bounded sync and async provider-list pagination while preserving timeout and refreshed-header handling
enterprise/litellm_enterprise/proxy/hooks/managed_files.py Passes an immutable deployment credential snapshot through managed unified-ID deletion
litellm/proxy/openai_files_endpoints/files_endpoints.py Supplies trusted internal deployment credentials to model-routed file listing
tests/test_litellm/llms/bedrock/files/test_bedrock_files_transformation.py Covers request signing, bucket and prefix validation, output listings, pagination, limits, and sync and async behavior

Reviews (6): Last reviewed commit: "refactor(files): build the next listing ..." | Re-trigger Greptile

Comment thread litellm/llms/bedrock/files/transformation.py
Comment thread litellm/llms/bedrock/files/transformation.py Outdated
@codecov

codecov Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Patch % Lines
litellm/llms/custom_httpx/llm_http_handler.py 91.11% 4 Missing ⚠️
litellm/llms/bedrock/files/transformation.py 97.93% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor Author

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.

@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/llms/custom_httpx/llm_http_handler.py Outdated
@veria-ai

veria-ai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

…every page

Following S3 continuation tokens let GET /v1/files walk an entire managed prefix however large it grew, and the follow-up page fetches dropped the caller's timeout. The handler now stops once MAX_FILE_LIST_LIMIT files are collected (10,000, the most OpenAI returns per list call), slicing the last page to fit, and hands the request timeout to the first and every later page fetch. MAX_FILE_LIST_LIMIT moves to litellm.constants so the proxy's limit validation and the handler share one number
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/llms/custom_httpx/llm_http_handler.py
… the managed files hook

The enterprise hook reached the constant through the common_utils re-export, which no longer defines it, so point it at the constant's new home
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri mateo-berri added run-ci and removed run-ci labels Sep 5, 2026
@mateo-berri

Copy link
Copy Markdown
Contributor Author

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.

… helper

Staging sits exactly at the LIT002 ceiling, so the duplicated validate_environment call for the next page is shared to keep the merged tree under it
@mateo-berri mateo-berri added run-ci and removed run-ci labels Sep 5, 2026
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor Author

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 7bddb65. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant