Skip to content

fix(bedrock): list the account's invocable models behind bedrock/* when check_provider_endpoint is on - #43981

Merged
mateo-berri merged 10 commits into
mainfrom
litellm_bedrock_wildcard_lists_account_models
Oct 9, 2026
Merged

mateo-berri merged 10 commits into
mainfrom
litellm_bedrock_wildcard_lists_account_models

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • bedrock/* lists every Bedrock id LiteLLM knows, not the account's
  • Clients pick listed ids that fail with "The provided model identifier is invalid"
  • check_provider_endpoint: true, the documented remedy, listed zero Bedrock models

How it solves it:

  • With check_provider_endpoint: true, bedrock/* asks Bedrock what the account can invoke
  • Lists the ACTIVE system-defined inference profiles plus on-demand foundation models in the deployment's region
  • Signs the listing with the deployment's own credentials, or its Bedrock API key
  • A partial wildcard such as bedrock/anthropic.* lists only the discovered ids with that prefix
  • A custom prefix such as anthropic/* over bedrock/* lists every discovered id as anthropic/<id>, also when the prefix starts a vendor id
  • With the flag off, the static catalog stays as it was, except that a custom prefix which starts a vendor id no longer lists those ids bare next to the prefixed ones

User Flow

Before: a developer picks a Bedrock model from the gateway's model list and the call fails, because the list is the static catalog rather than what the account can invoke, and the documented discovery flag lists nothing at all

  1. The proxy admin configures model_name: bedrock/* with aws_region_name: us-east-1 and AWS keys, turns on litellm_settings: check_provider_endpoint: true, and restarts
  2. The developer sends GET https://litellm-domain/v1/models and gets {"data": [], "object": "list"}; with the flag off the same call returns the 347 ids of the static catalog, among them bedrock/eu.anthropic.claude-opus-4-5-20251101-v1:0 and bedrock/jp.anthropic.claude-sonnet-4-5-20250929-v1:0
  3. They pick bedrock/eu.anthropic.claude-opus-4-5-20251101-v1:0 from that list (the playground dropdown at https://litellm-domain/ui/playground offers the same ids with the flag off, and "No models available for this key" with it on) and send POST https://litellm-domain/v1/chat/completions with it
  4. HTTP 400 BedrockException - {"message":"The provided model identifier is invalid."}

pr43981-8fa44a26ab-pr43981-8fa44a26ab-before-playground-opus45-boxed.png

After: the same developer sees only the models the account can invoke in that region, and every listed id answers

  1. The proxy admin configures model_name: bedrock/* with aws_region_name: us-east-1 and AWS keys, turns on litellm_settings: check_provider_endpoint: true, and restarts
  2. The developer sends GET https://litellm-domain/v1/models and gets the 158 ids the account can call in us-east-1, such as bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0 and bedrock/amazon.nova-micro-v1:0; the eu., jp., and us-gov. ids are gone, and GET https://litellm-domain/model/info shows the same set
  3. They pick bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0 (the playground dropdown at https://litellm-domain/ui/playground now offers it) and send POST https://litellm-domain/v1/chat/completions with it
  4. HTTP 200 with the model's reply

pr43981-8fa44a26ab-pr43981-8fa44a26ab-after-playground-opus45-boxed.png

Linear ticket

Resolves LIT-1694

Design decisions

  1. Discovery goes through BaseLLMModelInfo.discover_models(litellm_params), the hook main added in feat(anthropic): workload identity federation and pluggable identity sources #44448, which defaults to the old get_models(api_key, api_base) for every other provider. Bedrock overrides it to read the deployment's region and AWS credentials, so no other provider changes. The PR first carried its own get_models_for_deployment hook for this and dropped it for main's once main shipped the same thing
  2. The listed set is the ACTIVE system-defined inference profiles plus the foundation models that support on-demand throughput, both filtered on the Bedrock side (typeEquals=SYSTEM_DEFINED, byInferenceType=ON_DEMAND). Application inference profiles and provisioned throughput are not listed. Listing every foundation model was rejected because it brings back the ids that only work through a profile
  3. The listing signs with the same credential resolver the invoke path uses (keys, role, profile, web identity, default chain), and a Bedrock API key (api_key or AWS_BEARER_TOKEN_BEDROCK) wins over SigV4, the same precedence as the invoke path. Query strings are percent-encoded RFC 3986 style (a space is %20, never +), since that is the form SigV4 canonicalizes to
  4. It stays behind the existing check_provider_endpoint flag, so the default list is still the static catalog. Listing live by default was rejected because it adds two AWS calls per deployment at boot and needs two more IAM permissions
  5. A failed listing raises; discovery logs a warning and bedrock/* lists no model for that call, and the next listing call tries again, since the shared discovery cache stores successes only (a transient failure never hides the models for five minutes, a persistent one costs one round trip per call, measured in the blast radius section), like every other discovered provider. Falling back to the static catalog on failure was rejected because it silently brings the bug back
  6. The inference profile listing follows nextToken for at most 20 pages of 1000 and raises past that, the same bounded loop the Anthropic lister uses on main, so a control plane that keeps returning a token can never loop forever
  7. The lister returns bare ids (amazon.nova-micro-v1:0), deduped and sorted, the shape get_valid_models returns for every other provider and the shape the static Bedrock catalog has. The proxy's wildcard expansion adds the deployment's own prefix (bedrock/, or a custom model_name prefix), so a custom-prefix deployment lists under its own name. Returning bedrock/-prefixed ids was tried first and dropped because the proxy's partial-wildcard filter (anthropic.* over the discovered ids) never matched a prefixed id
  8. The lister subclasses BaseAWSLLM to reuse its region and credential resolution instead of calling those helpers from outside
  9. A partial wildcard whose deployment repeats the suffix (model_name: bedrock/anthropic.* over model: bedrock/anthropic.*) is a filter over the discovered ids, never an alias: it lists the ids that start with anthropic., and nothing when the account has none (an account whose Anthropic models are reachable only through us. or global. profiles, which is every account in us-east-1 today). Before 5ce9254 the proxy took the alias branch of get_known_models_from_wildcard for that deployment, meant for genai/test/* over openai/* (fix(model_checks.py): handle custom values in wildcard model name (e.g. genai/test/*) #13116), and listed every discovered id as bedrock/anthropic.<id>, 156 names no deployment can route. The alias branch still applies to a deployment whose model does not repeat the suffix
  10. The custom-prefix branch of get_known_models_from_wildcard decided whether an id already carries the deployment's prefix by startswith(prefix) without the /, so anthropic/* over bedrock/* listed anthropic.claude-haiku-4-5-20251001-v1:0 bare (the id starts with the string anthropic) and every other id as anthropic/<id>. It now matches <prefix>/. The bare names were never routable on that key (a chat on one answers 403 on both legs). Pre-existing on main with the static catalog (33 of its 347 ids start with anthropic), reached by every discovered id with the flag on. Fixed by 8fa44a2

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/unit/<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

Last updated: 3dbe39e. QA and /live-pr-risk ran on 8fa44a2, the last commit with a product diff

Both legs ran on the same box against the real Bedrock control plane and real model invocations in us-east-1 with the CI AWS account's keys on the deployment, plus a real OpenAI deployment as the control. Each leg is one proxy per boot (--num_workers 2, so two uvicorn workers) with its own worktree, venv and Postgres (the Admin UI login needs one), no Redis: the discovery cache is a per-process 5 minute TTL with no shared state, so one two-worker proxy covers the whole runtime path. Every amazonaws.com request from either proxy went through a forwarding recorder in front of the real control plane and runtime (it delays bedrock.us-east-2.amazonaws.com by 12 s for the bedrock-slow/* deployment and forwards everything else untouched). Each proxy booted with check_provider_endpoint on, then off for the static-catalog case, then on again. The same 44 requests ran in the same order on both legs; the base answered on port 56739, the head on 57097

model_list:
  - model_name: bedrock/*
    litellm_params:
      model: bedrock/*
      aws_region_name: us-east-1
      aws_access_key_id: os.environ/QA_BEDROCK_ACCESS_KEY_ID
      aws_secret_access_key: os.environ/QA_BEDROCK_SECRET_ACCESS_KEY
    model_info:
      access_groups: ["bedrock-group"]
  - model_name: bedrock/anthropic.*
    litellm_params:
      model: bedrock/anthropic.*
      aws_region_name: us-east-1
      aws_access_key_id: os.environ/QA_BEDROCK_ACCESS_KEY_ID
      aws_secret_access_key: os.environ/QA_BEDROCK_SECRET_ACCESS_KEY
  - model_name: anthropic/*
    litellm_params:
      model: bedrock/*
      aws_region_name: us-east-1
      aws_access_key_id: os.environ/QA_BEDROCK_ACCESS_KEY_ID
      aws_secret_access_key: os.environ/QA_BEDROCK_SECRET_ACCESS_KEY
  - model_name: bedrock-badkey/*
    litellm_params:
      model: bedrock/*
      aws_region_name: us-east-1
      aws_access_key_id: AKIAQAQAQAQAQAQAQAQA
      aws_secret_access_key: qaqaqaqaqaqaqaqaqaqaqaqaqaqaqaqaqaqaqaqa
  - model_name: bedrock-nocreds/*
    litellm_params:
      model: bedrock/*
      aws_region_name: us-east-1
  - model_name: bedrock-slow/*
    litellm_params:
      model: bedrock/*
      aws_region_name: us-east-2
      aws_access_key_id: os.environ/QA_BEDROCK_ACCESS_KEY_ID
      aws_secret_access_key: os.environ/QA_BEDROCK_SECRET_ACCESS_KEY
  - model_name: gpt-control
    litellm_params:
      model: openai/gpt-5.6
      api_key: os.environ/OPENAI_API_KEY
general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY
litellm_settings:
  check_provider_endpoint: true   # false for the static-catalog case

Reproduce in the Admin UI: log in at http://localhost:4000/ui, open http://localhost:4000/ui/playground, click the "Select Model" box and type claude-opus-4-5-20251101. Before (flag on) the box says "No models available for this key"; after it offers bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0 and bedrock/us.anthropic.claude-opus-4-5-20251101-v1:0, the same two under anthropic/ from the custom-prefix deployment, and no eu. id. The screenshots are under User Flow above

Before (8fc31b1)

Flag on: listings

  1. curl -s http://127.0.0.1:56739/v1/models -H "Authorization: Bearer $LITELLM_MASTER_KEY" | jq '.data | length' -> 1 (gpt-control; eu.=0 jp.=0 us-gov.=0 global.=0 us.=0, no Bedrock id at all), 0.11 s cold, 6 ms warm; GET /models the same
  2. curl -s 'http://127.0.0.1:56739/v1/models?return_wildcard_routes=true' ... | jq '.data | length' -> 7 (gpt-control plus the six wildcard routes)
  3. curl -s http://127.0.0.1:56739/model/info ... | jq '.data | length' -> 7 (the six wildcard rows and gpt-control); GET /v1/model/info -> 7; GET /model_group/info -> 1 group (gpt-control)
  4. POST /key/generate with {"models": ["bedrock/*"]}, then GET /v1/models with that key -> 0 ids; a key on {"models": ["bedrock-group"]} -> 0; a key on a team with {"models": ["bedrock/*"]} -> 0; a key on {"models": ["bedrock/anthropic.*"]} -> 0
  5. The playground's "Select Model" box, searched for claude-opus-4-5-20251101: "No models available for this key" (first screenshot under User Flow)

Flag on: custom prefix over the wildcard

  1. POST /key/generate with {"models": ["anthropic/*"]}, then curl -s http://127.0.0.1:56739/v1/models -H "Authorization: Bearer $KEY_CUSTOM" | jq '.data | length' -> 0
  2. curl -s -w '%{http_code}' http://127.0.0.1:56739/v1/chat/completions -H "Authorization: Bearer $KEY_CUSTOM" -H 'Content-Type: application/json' -d '{"model": "anthropic/us.anthropic.claude-haiku-4-5-20251001-v1:0", "messages": [{"role": "user", "content": "Say hi in three words"}], "max_tokens": 20}' -> HTTP 200 "Hey there, friend!" (not listed, routes anyway); the same with the bare anthropic.claude-haiku-4-5-20251001-v1:0 -> 403 not available for this API key

Flag on: /v1/chat/completions

  1. curl -s -w '%{http_code}' http://127.0.0.1:56739/v1/chat/completions -H "Authorization: Bearer $KEY_BEDROCK_WILDCARD" -H 'Content-Type: application/json' -d '{"model": "bedrock/amazon.nova-micro-v1:0", "messages": [{"role": "user", "content": "Say hi in three words"}], "max_tokens": 20}' -> HTTP 200 "Hello there!", model=bedrock/amazon.nova-micro-v1:0 (not listed, routes anyway); the team key -> 200 "Hello there!", the access-group key -> 200 "Hello, world!"
  2. The same with bedrock/eu.anthropic.claude-opus-4-5-20251101-v1:0 -> HTTP 400 BedrockException - {"message":"The provided model identifier is invalid."}
  3. The same with bedrock/anthropic.claude-opus-4-5-20251101-v1:0 -> HTTP 400 Invocation of model ID anthropic.claude-opus-4-5-20251101-v1:0 with on-demand throughput isn't supported
  4. The same with bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0 -> HTTP 200 "Hi there, friend!"
  5. "stream": true on bedrock/amazon.nova-micro-v1:0 -> 200, 6 chunks, [DONE], "Hello, friend!"; "model": "gpt-control" on the bedrock/* key -> 403 not available for this API key; "fallbacks": ["bedrock/amazon.nova-micro-v1:0"] -> 200 "Hello, world!"; after POST /key/delete the same key -> 200 "Hello, friend!" this pass (401 in earlier passes; the two workers keep separate key caches and the delete had not reached the one that served it, see Blast radius)

Flag on: /v1/messages

  1. curl -s -w '%{http_code}' http://127.0.0.1:56739/v1/messages -H "Authorization: Bearer $KEY_BEDROCK_WILDCARD" -H 'Content-Type: application/json' -d '{"model": "bedrock/amazon.nova-micro-v1:0", "max_tokens": 20, "messages": [{"role": "user", "content": "Say hi in three words"}]}' -> HTTP 200 type=message, "Hello, friend!", input_tokens=5 output_tokens=5
  2. The same with "stream": true -> 200, 8 events ending in message_stop

Flag on: /v1/responses

  1. curl -s -w '%{http_code}' http://127.0.0.1:56739/v1/responses -H "Authorization: Bearer $KEY_BEDROCK_WILDCARD" -H 'Content-Type: application/json' -d '{"model": "bedrock/amazon.nova-micro-v1:0", "input": "Say hi in three words", "max_output_tokens": 20}' -> HTTP 200 status=completed, "Hello, friend!", id=resp_JtPRyQt
  2. The same with "stream": true -> 200, 13 events, response.completed, "Hello, friend!"

Flag off: the static catalog

  1. curl -s http://127.0.0.1:56739/v1/models ... | jq '.data | length' -> 1769 (the four bedrock wildcard deployments each expand to the 347 ids of the static catalog, eu.=34 jp.=10 us-gov.=15 global.=32 us.=64 anthropic.=66 under bedrock/; the partial one to 33; anthropic/* to 347), GET /model/info -> 1769 rows
  2. A key on {"models": ["bedrock/anthropic.*"]}, GET /v1/models -> 33 static bedrock/anthropic. ids
  3. A key on {"models": ["anthropic/*"]}, GET /v1/models -> 347: 314 under anthropic/ plus 33 bare ids such as anthropic.claude-3-5-haiku-20241022-v1:0 and anthropic.claude-3-5-sonnet-20240620-v1:0
  4. POST /v1/chat/completions with bedrock/eu.anthropic.claude-opus-4-5-20251101-v1:0 (listed) -> HTTP 400 The provided model identifier is invalid.; bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0 -> 200 "Hi there, friend!"; bedrock/amazon.nova-micro-v1:0 -> 200 "Hello, friend!"; anthropic/us.anthropic.claude-haiku-4-5-20251001-v1:0 on the anthropic/* key -> 200 "Hey there, friend!"

After (8fa44a2)

Flag on: listings

  1. curl -s http://127.0.0.1:57097/v1/models -H "Authorization: Bearer $LITELLM_MASTER_KEY" | jq '.data | length' -> 317: the 158 ids the account can invoke in us-east-1 under bedrock/ (global.=32 us.=67, 59 bare ids, eu.=0 jp.=0 us-gov.=0), the same 158 under anthropic/ from the custom-prefix deployment, plus gpt-control; first ids anthropic/ai21.jamba-1-5-large-v1:0, anthropic/ai21.jamba-1-5-mini-v1:0, anthropic/amazon.nova-2-5-sonic, anthropic/amazon.nova-2-multimodal-embeddings-v1:0, anthropic/amazon.nova-2-sonic-v1:0; 11.6 s cold and 10.3 s warm, both set by the bedrock-slow/* deployment's 10 s listing timeout (see Caveats); GET /models the same
  2. curl -s 'http://127.0.0.1:57097/v1/models?return_wildcard_routes=true' ... | jq '.data | length' -> 323 (the 317 plus the six wildcard routes)
  3. curl -s http://127.0.0.1:57097/model/info ... | jq '.data | length' -> 321 (316 expanded rows, gpt-control, and the four wildcard rows that expanded to nothing: bedrock/anthropic.*, bedrock-badkey/*, bedrock-nocreds/*, bedrock-slow/*), 10.5 s; GET /v1/model/info -> 321 (10.4 s); GET /model_group/info -> 317 groups
  4. POST /key/generate with {"models": ["bedrock/*"]}, then GET /v1/models with that key -> 158 ids; a key on {"models": ["bedrock-group"]} -> 158; a key on a team with {"models": ["bedrock/*"]} -> 158; a key on {"models": ["bedrock/anthropic.*"]} -> 0 (no invocable id starts with anthropic.; the account's Anthropic models are the us. and global. profiles), and POST /v1/chat/completions on that key with bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0 or bedrock/amazon.nova-micro-v1:0 -> 403 on both legs
  5. The playground's "Select Model" box, searched for claude-opus-4-5-20251101: bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0, bedrock/us.anthropic.claude-opus-4-5-20251101-v1:0 and the same two under anthropic/, no eu. id (second screenshot under User Flow)

Flag on: custom prefix over the wildcard

  1. POST /key/generate with {"models": ["anthropic/*"]}, then curl -s http://127.0.0.1:57097/v1/models -H "Authorization: Bearer $KEY_CUSTOM" | jq '.data | length' -> 158, every id under anthropic/ and none bare; first anthropic/ai21.jamba-1-5-large-v1:0, anthropic/ai21.jamba-1-5-mini-v1:0, anthropic/amazon.nova-2-5-sonic
  2. curl -s -w '%{http_code}' http://127.0.0.1:57097/v1/chat/completions -H "Authorization: Bearer $KEY_CUSTOM" -H 'Content-Type: application/json' -d '{"model": "anthropic/us.anthropic.claude-haiku-4-5-20251001-v1:0", "messages": [{"role": "user", "content": "Say hi in three words"}], "max_tokens": 20}' -> HTTP 200 "Hi, how are you?", prompt_tokens=12 completion_tokens=9 (listed); the same with the bare anthropic.claude-haiku-4-5-20251001-v1:0 -> 403 not available for this API key

Flag on: /v1/chat/completions

  1. curl -s -w '%{http_code}' http://127.0.0.1:57097/v1/chat/completions -H "Authorization: Bearer $KEY_BEDROCK_WILDCARD" -H 'Content-Type: application/json' -d '{"model": "bedrock/amazon.nova-micro-v1:0", "messages": [{"role": "user", "content": "Say hi in three words"}], "max_tokens": 20}' -> HTTP 200 "Hello, world!", model=bedrock/amazon.nova-micro-v1:0 (now listed); the team key -> 200 "Hello there!", the access-group key -> 200 "Hello, friend!"
  2. The same with bedrock/eu.anthropic.claude-opus-4-5-20251101-v1:0 (no longer listed) -> HTTP 400 BedrockException - {"message":"The provided model identifier is invalid."}
  3. The same with bedrock/anthropic.claude-opus-4-5-20251101-v1:0 (not listed; the static catalog listed it) -> HTTP 400 Invocation of model ID anthropic.claude-opus-4-5-20251101-v1:0 with on-demand throughput isn't supported
  4. The same with bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0 (listed) -> HTTP 200 "Hi there, friend!"
  5. "stream": true on bedrock/amazon.nova-micro-v1:0 -> 200, 5 chunks, [DONE], "Hello there!"; "model": "gpt-control" on the bedrock/* key -> 403 not available for this API key; "fallbacks": ["bedrock/amazon.nova-micro-v1:0"] -> 200 "Hello there!"; after POST /key/delete the same key -> 401

Flag on: /v1/messages

  1. curl -s -w '%{http_code}' http://127.0.0.1:57097/v1/messages -H "Authorization: Bearer $KEY_BEDROCK_WILDCARD" -H 'Content-Type: application/json' -d '{"model": "bedrock/amazon.nova-micro-v1:0", "max_tokens": 20, "messages": [{"role": "user", "content": "Say hi in three words"}]}' -> HTTP 200 type=message, "Hello there!", input_tokens=5 output_tokens=4
  2. The same with "stream": true -> 200, 8 events ending in message_stop

Flag on: /v1/responses

  1. curl -s -w '%{http_code}' http://127.0.0.1:57097/v1/responses -H "Authorization: Bearer $KEY_BEDROCK_WILDCARD" -H 'Content-Type: application/json' -d '{"model": "bedrock/amazon.nova-micro-v1:0", "input": "Say hi in three words", "max_output_tokens": 20}' -> HTTP 200 status=completed, "Hello there!", id=resp_YKrfN4j
  2. The same with "stream": true -> 200, 12 events, response.completed, "Hello, world!"

Flag off: the static catalog

  1. curl -s http://127.0.0.1:57097/v1/models ... | jq '.data | length' -> 1769 (eu.=34 jp.=10 us-gov.=15 global.=32 us.=64 anthropic.=66 under bedrock/, the same set as the base), GET /model/info -> 1769 rows
  2. A key on {"models": ["bedrock/anthropic.*"]}, GET /v1/models -> 33 static bedrock/anthropic. ids
  3. A key on {"models": ["anthropic/*"]}, GET /v1/models -> 347, all under anthropic/ and none bare; first anthropic/1024-x-1024/50-steps/bedrock/amazon.nova-canvas-v1:0 (the static catalog's image ids included, as before under bedrock/)
  4. POST /v1/chat/completions with bedrock/eu.anthropic.claude-opus-4-5-20251101-v1:0 -> HTTP 400 The provided model identifier is invalid.; bedrock/global.anthropic.claude-opus-4-5-20251101-v1:0 -> 200 "Hi there, friend!"; bedrock/amazon.nova-micro-v1:0 -> 200 "Hello, friend!"; anthropic/us.anthropic.claude-haiku-4-5-20251001-v1:0 on the anthropic/* key -> 200 "Hey there, friend!"

Closing observations

  • Flag off: 1769 ids on both legs; the PR leaves it alone
  • Flag off, custom prefix: 33 bare ids now prefixed; the PR causes this
  • Discovery on: 0 Bedrock ids before, 158 per wildcard deployment after; the PR causes this
  • /model/info, /model_group/info, key and team listings agree with /v1/models; the PR causes this
  • Custom prefix with the flag on lists 158 anthropic/<id>; the PR causes this
  • A stalled control plane adds 10 s to every listing; the PR causes this (Caveats)
  • Two concurrent cold listings block both workers 16 s; the PR causes this (Caveats)
  • Non-chat ids (embeddings, sonic) listed like the static catalog; the PR leaves it alone
  • A chat right after POST /key/delete answers 200 or 401 by worker; pre-existing, the PR leaves it alone
  • /spend/logs answers the same two rows on both legs; the PR leaves spend alone
  • SDK cache ignores a region change inside the TTL; pre-existing, the PR leaves it alone

PASS: with check_provider_endpoint: true, bedrock/* lists the account's invocable ids in the region instead of nothing, a partial wildcard lists only matching ids, a custom prefix lists every discovered id under that prefix, each listed id answers on all three endpoints, and the flag-off catalog is unchanged apart from the bare ids a colliding custom prefix used to list

Blast radius, base 8fc31b1 vs head 8fa44a2 (/live-pr-risk)

Verdict: no dependent path breaks. The contract changes are the fix itself and, behind the opt-in flag, the cost of a listing: every Bedrock wildcard deployment now makes one or two control-plane round trips per cold listing (one per worker per 5 minutes when they succeed, every call when they fail), and a deployment whose control plane does not answer stalls each listing call for the lister's 10 s read timeout on the worker's event loop. That is the shape the OpenAI and Anthropic listers already have on main behind the same flag. Both are listed under backward incompatible with their measurements and dispositions, and the class fix (cache failures, run discovery off the event loop) is tracked on a private ticket. The custom-prefix match is the one flag-off change, listed there too

Rig: the same box, worktree, venv and Postgres per leg as the proof above, --num_workers 2 on both, no Redis, the same config and the same 44 requests in the same order, each leg's litellm.__file__ asserted inside its own worktree, plus a concurrency cell (two cold listings at once with 70 /health/liveliness pings at 0.5 s and a gpt-control chat 2 s in) and an SDK cell (litellm.get_valid_models in a subprocess of each worktree). Every amazonaws.com request went through a forwarding recorder per leg (mitmdump, SigV4 intact) in front of the real control plane and runtime, so the outbound wire is diffed base vs head below

Breaking: none. Status, body and token usage matched on every chat and auth request but one: bedrock/amazon.nova-micro-v1:0 200 on the wildcard key, the access-group key and the team key; the unlisted eu. id 400 from Bedrock (invalid identifier) and the bare anthropic.claude-opus-4-5-20251101-v1:0 400 from Bedrock (on-demand unsupported) on both legs; gpt-control on a bedrock/* key 403 on both; anthropic/us.anthropic.claude-haiku-4-5-20251001-v1:0 200 and its bare form 403 on the custom-prefix key on both; fallback, streaming, /v1/messages, /v1/responses, key info, /health/readiness, /ui and the playground all identical; /spend/logs answers the same two rows on both legs. The one difference is the chat sent right after POST /key/delete: 200 on the base and 401 on the head in this pass, 401 on the base and 200 on the head in earlier passes, 200 on the merged head. The proxy runs two uvicorn workers with no Redis, so each worker keeps its own key cache and the chat passes when it lands on the worker that did not serve the delete; the same on both legs, outside this PR. The 14 runtime POSTs per leg are identical on the wire (the base has a 15th, the chat that race let through): the same eight bedrock-runtime.us-east-1.amazonaws.com paths (converse and converse-stream on nova micro; converse on the eu., bare, global. and us. haiku ids), the same 118-byte bodies, the same nine header keys, 11 x 200 and 3 x 400 on the head, one attempt each. The head adds 18 control-plane GETs to bedrock.us-east-1.amazonaws.com and nothing else: 4 GET /foundation-models?byInferenceType=ON_DEMAND (median 152 ms, max 167) and 4 GET /inference-profiles?maxResults=1000&typeEquals=SYSTEM_DEFINED (median 370 ms, max 412) from the good deployments (two workers, two cold periods in the run; anthropic/* shares the discovery cache entry with bedrock/* because their litellm_params are identical, so it added no round trip) and 10 GET /inference-profiles answered 403 from the bedrock-badkey/* deployment (one per listing call, median 38 ms, max 43); header keys accept, accept-encoding, authorization, connection, host, user-agent, x-amz-date, no body. bedrock-nocreds/* never reaches the wire. The bedrock-slow/* requests time out on the proxy side before the recorder logs them, so their count comes from the proxy's Error getting valid models: The read operation timed out warning, one per listing call

Backward incompatible:

  1. Listing surfaces with the flag on grow from nothing to the account's invocable set (the fix): /v1/models and /models 1 -> 317 ids (158 under each of the two discovered wildcard deployments), ?return_wildcard_routes=true 7 -> 323, /model/info and /v1/model/info 7 -> 321 rows, /model_group/info 1 -> 317 groups, wildcard-key, access-group-key, team-key and custom-prefix-key listings 0 -> 158; the playground offers the chat-mode entries. Intended, this PR
  2. A partial wildcard whose deployment repeats the suffix (bedrock/anthropic.* over bedrock/anthropic.*) filters the discovered ids instead of aliasing them: with the flag on it lists 0 ids on both legs here (no invocable id starts with anthropic.), with the flag off it lists the same 33 static ids on both legs. The intermediate commit 25d5a61 of this PR (never on main) listed 156 bedrock/anthropic.<id> alias names that no deployment can route. Fixed by 5ce9254
  3. A misconfigured Bedrock wildcard (bad key, missing bedrock:ListInferenceProfiles or bedrock:ListFoundationModels, no credentials) now costs one failed control-plane round trip and one Error getting valid models WARNING per listing call, retried on every call because the discovery cache stores successes only: the badkey deployment's 403 was 38 ms median upstream, one per listing call; the nocreds deployment fails its credential chain locally once per call. Base: those deployments list nothing and log nothing. Low caveat, shipped as-is, approved by mateo-berri in the review on this PR
  4. A Bedrock wildcard whose control plane does not answer (a firewall, a VPC with the bedrock-runtime endpoint but no bedrock endpoint) adds the lister's 10 s read timeout to every listing call, and the listing runs synchronously inside the async route handler, so that worker's event loop is blocked meanwhile: with bedrock-slow/* delayed 12 s, warm /v1/models took 10.45 s and /model/info 10.96 s on the head against 0.11 s and 0.35 s on the base; two cold listings sent at once took 16.0 s and 16.9 s and left both workers blocked, so a /health/liveliness ping in that window waited 12.15 s (3 of 70 pings over 1 s against a base max of 1.48 s, 1 of 70) and a gpt-control chat sent 2 s in took 18.7 s against 8.27 s on the base. The same shape as the OpenAI and Anthropic listers on main behind the same flag, and the reason the flag stays off by default (design decision 4). Medium caveat, shipped as-is behind the opt-in flag, approved by mateo-berri in the review on this PR; the class fix is on a private ticket
  5. SDK: litellm.get_valid_models(check_provider_endpoint=True, custom_llm_provider="bedrock") returns the account's 158 ids in AWS_REGION_NAME instead of 0 (0.78 s cold), 347 with the flag off on both legs, and aws_region_name on litellm_params picks the region. The 5 minute cache keys on the provider and litellm_params only, so a second call inside the TTL after changing AWS_REGION_NAME to eu-west-1 returned the cached us-east-1 list (158 ids, eu.=0): the env-hash check in get_cached_model_info only runs when litellm_params is None, which get_valid_models never passes. Pre-existing cache shape, now reachable from Bedrock; Low caveat, same private ticket
  6. AWS environment variables never infer the provider: with only AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY set and no custom_llm_provider, get_valid_models(check_provider_endpoint=True) lists nothing for Bedrock on both legs (_infer_valid_provider_from_env_vars keys on <PROVIDER>_API_KEY names, so only BEDROCK_API_KEY infers it). Unchanged by the PR, named so nobody reads it as a regression
  7. A custom model_name prefix that starts a vendor id (anthropic/* over bedrock/*) lists every id under the prefix: with the flag on, a key on anthropic/* lists 158 anthropic/<id> on the head against 0 on the base; with the flag off it lists 347 on both legs, but the base lists 33 of them bare (anthropic.claude-3-5-haiku-20241022-v1:0 and the other ids that start with the string anthropic) where the head lists all 347 as anthropic/<id>. The bare names never routed on that key (a chat on one answers 403 on both legs), so a client that picked one from the list failed before and no longer sees it. Pre-existing on main with the static catalog, reached by every discovered id with the flag on. Fixed by 8fa44a2

Regression risk (reachable dependents this rig did not exercise):

  • The Bearer token listing path (api_key or AWS_BEARER_TOKEN_BEDROCK), proven against a scripted control plane in the audit cells below and unit tested in tests/unit/llms/bedrock/test_bedrock_model_listing.py; the live rig unsets the token to keep one SigV4 identity
  • Role assumption, profile and web-identity credentials on the listing: the lister reuses BaseAWSLLM's resolver, which the invoke path exercises live, but the listing ran with static keys only
  • Pagination past one page of 1000 inference profiles, scripted and unit tested only (the QA account has under 1000)
  • GovCloud and the China partition, scripted only (the listing host per partition is an audit cell), never run against a real partition

Dependency graph (dependents of BedrockModelInfo.__init__, get_models, discover_models, BedrockModelLister, and the partial-wildcard and custom-prefix branches of get_known_models_from_wildcard): get_provider_model_info (utils.py) verified live through every listing route; _get_valid_models_from_provider_api and get_valid_models verified live (SDK cell, both legs); get_known_models_from_wildcard, _get_wildcard_models and get_complete_model_list verified live (/v1/models, /models, /model/info, /v1/model/info, /model_group/info, the partial and custom-prefix wildcards, flag on and off); get_available_models_for_user (key, team and access-group scoping) verified live; expand_wildcard_deployments_for_model_info verified live; the dashboard playground verified live (screenshots); BedrockPassthroughConfig and BedrockMantlePassthroughConfig (the only subclasses; the new __init__ adds _client=None and keeps parser) verified live through the proxy boot and the passthrough routes in the earlier run at b44ccc0; the alias branch of get_known_models_from_wildcard (genai/test/* over openai/*) tested in tests/unit/proxy/auth/test_model_checks.py and unchanged for a deployment whose model does not repeat the suffix; the custom-prefix branch's <prefix>/ match verified live with anthropic/* over bedrock/* (key listing and a chat on a listed id, flag on and off) and unit tested in the same file

Merge-ref check: main moved to 4df5006 past the branch point. Of its commits only #44447 touches the Bedrock auth helpers (base_aws_llm.py, common_utils.py); the lister signs its own GETs with SigV4Auth in _auth_headers, which that change does not touch. origin/main merged cleanly into the head worktree on the box (merge commit c02803425d), the head leg rebooted on the merged tree, and the 44 requests reran identical to the head run: 317 / 323 / 321 / 317 on the master-key routes, 158 on the wildcard, access-group, team and custom-prefix keys, 0 on the partial key, the same chat statuses and bodies apart from the deleted-key race (200 there) and the model's own stream event counts (9 and 13 against 8 and 12), and the same 18 control-plane GETs with 15 runtime POSTs (the race's chat included). A local merge of the later main 04f0ea8 into the tip is clean and passes the 98 unit tests of the lister and the wildcard expander

Not verified: the four regression-risk items above; the bedrock-slow/* control-plane requests themselves (the proxy times out before the recorder logs them, so their count is read from the proxy warning rather than the wire); the runtime boundary is recorded for the us-east-1 converse paths only; which worker served the chat after POST /key/delete (the per-worker key cache is read from the proxy's design, not instrumented in the run); a Redis-backed multi-pod cache (not applicable, the discovery cache is per process and the PR does not change that)

Audit round 5 of 3 (/audit, base 8fc31b1 vs head 8fa44a2, changed cells again at 3dbe39e)

Every surface in the inventory (34 rows: the listing routes, key, team and access-group scoping, bearer and SigV4 listing auth, the listing host per region and partition, the three unified endpoints on a discovered id, flag off, control-plane 403/404/html/shape/empty answers, no credentials, refused egress, a hung control plane, endless pagination, unusable region values, non-string credential fields, the per-worker cache, two regions, partial wildcards, custom-prefix wildcards both distinct from and starting a discovered id, inactive profiles, concurrent cold listings, a region update mid-flow, an outage and a slow control plane under a burst, a worker killed mid-listing, the SDK entrypoints, and the live row above) is a cell in tests/integration/providers/test_bedrock_model_discovery_wire.py, tests/integration/chaos/test_bedrock_model_discovery_chaos.py or tests/integration/sdk/test_bedrock_model_discovery_sdk.py, run against a scripted AWS control plane that terminates TLS for bedrock.<region>.<suffix> and verifies the SigV4 signature of every listing. Each cell ran on both legs at the tip (two runs on the head, one on the base, same seeds), plus the control cells that share the SigV4 verifier. Head 39/39 twice on the provider and chaos cells, 3/3 twice on the SDK cells, controls 5/5 and 2/2; the base is red on every cell the PR changes and green on the ones it leaves alone (flag off, no credentials, unusable region, non-string fields, environment-only SDK). Rig lit1694a3 on the same box, base :34181, head :45787; run ids round-lanes-round5.json (head-sdk-run2, ctl-providers, ctl-security), round-lanes-round5b.json (base-sdk, head-providers-run1, head-providers-run2) and round-lanes-round5c.json (base-providers, head-sdk-run1; the first two tries of those lanes were refused by the box's memory admission and reran once it had room)

Rounds 4 and 5 ran past the cap of three, each after a product fix the live QA found: the partial-wildcard filter in 5ce9254 and the custom prefix in 8fa44a2

The tests-only commit 3dbe39e (type annotations in the discovery cells and in aws_control_plane.py and bedrock_discovery.py, the two helpers every one of them uses) reran those 42 cells on both legs at 3dbe39e with the same seeds, base once and head twice, on rig lit1694d2 (base :57753, head :37595, run id round-lanes-rerun.json). Head 39/39 and 3/3 on both runs with identical collected and passed selections, base green on the same 7 cells as round 5 and red on the rest, so every row matches round 5. The 7 controls and the live row do not use those helpers and keep their 8fa44a2 results; the Last ran at column names each row's commit

Cell matrix (49 rows)
# Cell Base 8fc31b1 Head 3dbe39e run 1 Head run 2 Last ran at
1 chaos::test_worker_killed_mid_listing_leaves_the_sibling_serving_and_a_replacement_listing_again failure passed passed 3dbe39e
2 wire::test_access_group_holding_the_wildcard_lists_the_discovered_models failure passed passed 3dbe39e
3 wire::test_admin_models_list_the_accounts_invocable_models_signed_for_the_deployment_region failure passed passed 3dbe39e
4 wire::test_bearer_token_deployment_lists_with_the_token_and_no_signature failure passed passed 3dbe39e
5 wire::test_concurrent_cold_listings_all_answer_with_the_discovered_models failure passed passed 3dbe39e
6 wire::test_control_plane_errors_leave_the_listing_without_bedrock_ids_and_the_proxy_serving[403] failure passed passed 3dbe39e
7 wire::test_control_plane_errors_leave_the_listing_without_bedrock_ids_and_the_proxy_serving[404] failure passed passed 3dbe39e
8 wire::test_control_plane_errors_leave_the_listing_without_bedrock_ids_and_the_proxy_serving[empty] failure passed passed 3dbe39e
9 wire::test_control_plane_errors_leave_the_listing_without_bedrock_ids_and_the_proxy_serving[html] failure passed passed 3dbe39e
10 wire::test_control_plane_errors_leave_the_listing_without_bedrock_ids_and_the_proxy_serving[shape] failure passed passed 3dbe39e
11 wire::test_control_plane_outage_mid_burst_recovers_without_a_proxy_restart failure passed passed 3dbe39e
12 wire::test_custom_prefix_wildcard_lists_the_discovered_models_under_that_prefix[distinct] failure passed passed 3dbe39e
13 wire::test_custom_prefix_wildcard_lists_the_discovered_models_under_that_prefix[starts-a-discovered-id] failure passed passed 3dbe39e
14 wire::test_deployment_without_credentials_never_reaches_the_control_plane passed passed passed 3dbe39e
15 wire::test_discovered_model_is_callable_through_the_wildcard_on_every_endpoint failure passed passed 3dbe39e
16 wire::test_empty_api_key_falls_back_to_sigv4 failure passed passed 3dbe39e
17 wire::test_endless_pagination_stops_at_the_page_cap failure passed passed 3dbe39e
18 wire::test_flag_off_proxy_lists_the_static_catalog_and_never_calls_the_control_plane passed passed passed 3dbe39e
19 wire::test_hung_control_plane_is_bounded_by_the_listing_timeout_while_the_proxy_keeps_serving failure passed passed 3dbe39e
20 wire::test_inactive_profiles_are_excluded_and_a_model_in_both_listings_appears_once failure passed passed 3dbe39e
21 wire::test_key_scoped_to_the_wildcard_lists_the_discovered_models_and_nothing_else failure passed passed 3dbe39e
22 wire::test_listing_reaches_the_control_plane_of_the_deployment_region_and_partition[cn-north-1] failure passed passed 3dbe39e
23 wire::test_listing_reaches_the_control_plane_of_the_deployment_region_and_partition[eu-west-1] failure passed passed 3dbe39e
24 wire::test_listing_reaches_the_control_plane_of_the_deployment_region_and_partition[us-gov-west-1] failure passed passed 3dbe39e
25 wire::test_model_group_info_carries_every_invocable_model failure passed passed 3dbe39e
26 wire::test_model_info_expands_the_wildcard_into_one_row_per_invocable_model failure passed passed 3dbe39e
27 wire::test_non_string_credential_fields_are_refused_at_model_creation[api_key] passed passed passed 3dbe39e
28 wire::test_non_string_credential_fields_are_refused_at_model_creation[aws_region_name] passed passed passed 3dbe39e
29 wire::test_partial_wildcard_lists_only_the_matching_models_under_their_real_ids failure passed passed 3dbe39e
30 wire::test_partial_wildcard_matching_no_invocable_id_lists_nothing_instead_of_alias_names failure passed passed 3dbe39e
31 wire::test_refused_egress_leaves_the_listing_without_bedrock_ids failure passed passed 3dbe39e
32 wire::test_region_update_while_listings_flow_moves_the_listing_to_the_new_regions_control_plane failure passed passed 3dbe39e
33 wire::test_repeated_listings_are_served_from_the_cache_once_per_worker failure passed passed 3dbe39e
34 wire::test_slow_control_plane_under_a_burst_answers_everything_without_a_deadlock failure passed passed 3dbe39e
35 wire::test_team_scoped_to_the_wildcard_lists_the_discovered_models failure passed passed 3dbe39e
36 wire::test_two_regions_list_the_union_from_each_regions_control_plane failure passed passed 3dbe39e
37 wire::test_unusable_region_never_reaches_a_control_plane[5kb] passed passed passed 3dbe39e
38 wire::test_unusable_region_never_reaches_a_control_plane[empty] passed passed passed 3dbe39e
39 wire::test_wildcard_route_is_listed_alongside_the_discovered_models_when_asked failure passed passed 3dbe39e
40 sdk::test_sdk_get_valid_models_lists_the_account_through_the_deployment_credentials failure passed passed 3dbe39e
41 sdk::test_sdk_get_valid_models_with_only_aws_environment_never_infers_bedrock passed passed passed 3dbe39e
42 sdk::test_sdk_get_valid_models_infers_bedrock_from_its_api_key_and_lists_with_the_bearer_token failure passed passed 3dbe39e
43 ctl providers::test_s3_wire.py::test_sigv4_verifier_matches_published_put_and_rejects_corruption n/a passed n/a 8fa44a2
44 ctl providers::test_s3_wire.py::test_s3_sync_and_async_uploads_pass_independent_wire_verification n/a passed n/a 8fa44a2
45 ctl providers::test_bedrock_mantle_claude_chat_wire.py::test_chat_completion_signs_the_native_messages_request_with_the_deployment_aws_keys n/a passed n/a 8fa44a2
46 ctl providers::test_bedrock_runtime_chat_completions_wire.py::test_sigv4_deployment_signs_the_native_request n/a passed n/a 8fa44a2
47 ctl providers::test_bedrock_runtime_chat_completions_wire.py::test_blank_api_key_on_a_sigv4_deployment_is_signed_not_sent_as_an_empty_bearer n/a passed n/a 8fa44a2
48 ctl security::test_stored_config_slots.py::test_aws_secret_key_signs_the_provider_request_and_stays_encrypted[success] n/a passed n/a 8fa44a2
49 ctl security::test_stored_config_slots.py::test_aws_secret_key_signs_the_provider_request_and_stays_encrypted[provider_4xx] n/a passed n/a 8fa44a2

Type

🐛 Bug Fix

Caveats (if any)

The before/after proof holds: before at the merge base 8fc31b1 (flag on lists nothing), after at 8fa44a2, the last commit with a product diff (flag on lists the account's 158 ids under each wildcard deployment, a custom prefix included), both on live proxies against the real Bedrock control plane and runtime, through the Admin UI playground the reporter used plus curl, with the commands and raw outputs above

Medium

  • With check_provider_endpoint: true, a bedrock/* deployment whose control plane does not answer stalls every listing call for the lister's 10 s timeout
    • Affects /v1/models, /models, /model/info, /model_group/info, key and team scoped listings, on every call (failures are not cached)
    • The listing runs on the worker's event loop: warm /v1/models 10.45 s against 0.11 s, two concurrent cold listings 16.0 s and 16.9 s with both workers blocked, a liveliness ping in that window 12.15 s, an unrelated chat 18.7 s against 8.27 s
    • Blast radius: a deployment that turns the flag on, runs a Bedrock wildcard, and cannot reach bedrock.<region>.amazonaws.com (a firewall, or a VPC with only the bedrock-runtime endpoint)
    • Workaround: the bedrock VPC endpoint plus the two IAM permissions (documented in the docs PR), or the flag off
    • The OpenAI and Anthropic listers on main have the same shape behind the same flag; the class fix (cache failures, discovery off the event loop) is on a private ticket

Low

  • A misconfigured Bedrock wildcard (bad key, missing bedrock:ListInferenceProfiles or bedrock:ListFoundationModels, no credentials) costs one failed round trip (38 ms median) and one WARNING per listing call. Same private ticket
  • A runtime endpoint override (api_base, aws_bedrock_runtime_endpoint) does not apply to the listing, which calls bedrock.<region>.<dns suffix>; a VPC needs a bedrock interface endpoint with private DNS
  • A deployment with no aws_region_name and no AWS_REGION_NAME lists us-west-2's set, the invoke path's default region
  • SDK get_valid_models(check_provider_endpoint=True, custom_llm_provider="bedrock") keys its 5 minute cache on the provider and litellm_params only, so a region change through AWS_REGION_NAME inside the TTL returns the first region's list (pre-existing cache shape). Same private ticket
  • The list carries every invocable id, non-chat ones included (embeddings, sonic), as the static catalog does; the playground's chat-mode filter hides them, /v1/models consumers that do not filter by capability see them
  • A partial wildcard with the flag on lists nothing in a region where every id of that family is a profile (us., global.); with the flag off it still lists the static ids with that prefix
  • With the flag off, a custom prefix that starts a vendor id (anthropic/*) no longer lists those ids bare; a client that read a bare name from that list and sent it got 403 before and now does not see the name

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

  • 8fa44a2 passes /live-pr-risk

  • 3dbe39e passes /audit


Note

Medium Risk
Changes proxy model listing and AWS-signed control-plane calls behind an opt-in flag; unreachable or slow Bedrock endpoints can block listing for up to 10s per worker and affect wildcard expansion.

Overview
With check_provider_endpoint: true, Bedrock wildcard deployments no longer return an empty list or only the static catalog. BedrockModelInfo now implements discover_models via a new BedrockModelLister that calls the regional Bedrock control plane (/foundation-models for on-demand models and paginated /inference-profiles for ACTIVE system-defined profiles), signed with the deployment’s SigV4 credentials or Bedrock bearer token. Proxy/SDK listings and wildcard expansion use that live set; with the flag off, behavior is unchanged.

Proxy wildcard handling is tightened: partial wildcards whose model repeats the suffix (e.g. bedrock/anthropic.*) filter discovered ids instead of aliasing them, and custom-prefix wildcards only strip a leading segment when it is a known provider (requires prefix/ match).

Broad unit, integration, and SDK tests add a scripted TLS control-plane proxy and cover auth, regions/partitions, failures, caching, concurrency, and chaos (worker killed mid-listing).

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

@devin-ai-integration

devin-ai-integration Bot commented Oct 1, 2026 •

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

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with the previous annotation finding fixed and no new actionable issues

Summary

With check_provider_endpoint enabled, Bedrock discovery lists active system-defined profiles and on-demand foundation models from the deployment’s region. Wildcard expansion filters partial matches and keeps custom prefixes

  • Bedrock wildcards list models the deployment can invoke.
  • Partial Bedrock wildcards filter real IDs under the configured prefix.

Previous dismissals and accepted limits:

  • mateo-berri accepted excluding application profiles, provisioned models, and imported models because admins configure those ARN-addressed resources explicitly
  • mateo-berri accepted leaving per-model IAM checks to invocation because checking each model would require paid calls
  • mateo-berri dismissed using api_base for discovery because it names the runtime endpoint, not the listing endpoint
  • mateo-berri dismissed ambient credential and region cache staleness because the shared cache is outside this change and expires after five minutes
  • mateo-berri dismissed moving UNKNOWN_CREDENTIAL to product constants because it is only test metadata
  • mateo-berri dismissed moving module-local test rigs because only shared fixtures belong in conftest.py
  • mateo-berri disputed adding Final to page because Pyright rejects it inside a loop. Greptile withdrew that suggestion

Reviews (7) · Last reviewed commit: "test(bedrock): Final and read-only annot..." · Reviewed by Greptile

Comment thread litellm/llms/bedrock/model_listing.py Outdated
Comment thread litellm/llms/bedrock/model_listing.py Outdated
Comment thread litellm/llms/bedrock/model_listing.py
Comment thread litellm/llms/bedrock/common_utils.py Outdated
Comment thread litellm/llms/bedrock/model_listing.py Outdated
@codspeed

codspeed Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

⚠️ 1 benchmark spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 31 untouched benchmarks


Comparing litellm_bedrock_wildcard_lists_account_models (3dbe39e) with main (bbc8d66)

Open in CodSpeed

@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.18072% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/llms/bedrock/common_utils.py 57.14% 3 Missing ⚠️
litellm/llms/bedrock/model_listing.py 98.63% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

…models

Main added the same discovery hook as BaseLLMModelInfo.discover_models
(#44448), so the PR's get_models_for_deployment hook and the HTTPHandler
header widening are dropped for main's versions and the Bedrock override
now reads the deployment's region and credentials from the mapping the
proxy passes. The inference profile pager is a bounded for loop (20 pages
of 1000) instead of recursion, which the recursion detector flagged.
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@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.

Stale Bugbot comment from a previous run.

…ial wildcards filter

get_valid_models returns bare vendor ids for every other provider and for the static bedrock catalog, and the proxy's wildcard expansion adds the provider prefix itself. The lister prefixed its ids, so a partial wildcard like bedrock/anthropic.* never matched the proxy's filter and listed uncallable bedrock/anthropic.bedrock/<id> entries
…ats the prefix

The wildcard expansion guessed filter-or-alias by whether any provider id carried the prefix. A deployment such as model_name bedrock/anthropic.* over model bedrock/anthropic.* can only ever route names that share the prefix, so when the account has no on-demand anthropic.* id (every Anthropic model behind an inference profile) the guess fell into the alias branch and listed bedrock/anthropic.<every id>, none of them callable. A deployment whose model repeats the suffix now always filters, and lists nothing when nothing matches
@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.

Stale Bugbot comment from a previous run.

Comment thread litellm/llms/bedrock/model_listing.py
Comment thread tests/integration/_support/aws_control_plane.py
Comment thread tests/integration/providers/test_bedrock_model_discovery_wire.py
Comment thread tests/integration/providers/test_bedrock_model_discovery_wire.py Outdated
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

Comment thread litellm/llms/bedrock/model_listing.py

@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

@greptileai

Comment thread tests/integration/_support/aws_control_plane.py Outdated
@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 3dbe39e. Configure here.

@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

This branch is waiting to be deployed

1 waiting deployment
e2e-changed — 3dbe39e4 Waiting Oct 9, 2026 by mateo-berri via oauth #4194
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.

1 participant