Skip to content

fix: Anthropic AWS resolves the default fetch per request - #318

Merged
eric8810 merged 14 commits into
masterfrom
fix/anthropic-aws-resolves-the-default-fetch-per-request
Oct 27, 2026
Merged

eric8810 merged 14 commits into
masterfrom
fix/anthropic-aws-resolves-the-default-fetch-per-request

Conversation

@cunninghamcard-bit

@cunninghamcard-bit cunninghamcard-bit commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes the 5 behaviour differences in Anthropic AWS found while porting the upstream tests. Each difference is its own commit (listed below) and adds a test that fails on master and a CHANGELOG Fixed entry. They were separate PRs (#314, #315, #316, #317 and this one). To keep one fix PR per vendor, the others are closed and this PR carries all of them. The Anthropic AWS test PR (#319) stacks on this branch. Part of the #276 list.

# Commit Difference Was
1 77933f11 Anthropic AWS models are named anthropic-aws.messages #314
2 198315f4 Anthropic AWS models accept image and PDF URLs #315
3 d1776d9e Anthropic AWS explains missing SigV4 credentials #316
4 b7a53e9e Anthropic AWS wraps credential provider failures #317
5 966ad7fd Anthropic AWS resolves the default fetch per request #318

1. Anthropic AWS models are named anthropic-aws.messages

Before: an Anthropic AWS model created without a name setting reported provider() == "anthropic-aws".

After: as in @ai-sdk/anthropic-aws 2.0.63, it reports "anthropic-aws.messages". A name setting still replaces it, and the providerOptions key stays anthropic.

Found while porting the upstream Anthropic AWS tests (case anthropicAws provider - model identity › sets the provider name to anthropic-aws.messages on the model).

How it works today

create_anthropic_aws(settings)                         aimux-providers/src/anthropic_aws/mod.rs:139
├─ provider_options_name = CANONICAL ("anthropic") unless named
└─ name = settings.name.unwrap_or(DEFAULT_NAME)       :162   DEFAULT_NAME = "anthropic-aws"   :49
AnthropicAwsProvider::messages → AnthropicMessagesModel { config.provider = name }   :236
  └─ LanguageModel::provider() → "anthropic-aws"       ← upstream: createChatModel → provider: 'anthropic-aws.messages'
                                                         (anthropic-aws-provider.ts:246)

What this PR changes

const DEFAULT_NAME: &str = "anthropic-aws.messages";  mod.rs:50   (docs on the name setting and messages() follow)

Tests (tests/anthropic_aws_settings_test.rs): the_default_provider_name_is_anthropic_aws_messages checks the default and a named provider; on master it fails with left: "anthropic-aws", right: "anthropic-aws.messages". The upstream case is ported in the Anthropic AWS test PR.

Docs: CHANGELOG Fixed.

Note: Nothing else in the repository reads the default name.

2. Anthropic AWS models accept image and PDF URLs

Before: an Anthropic AWS model reported no supported URLs, so an http(s) image or PDF in a prompt was downloaded and sent inline.

After: as in @ai-sdk/anthropic-aws 2.0.63, it supports image/* and application/pdf over http(s), the same patterns as the Anthropic provider, so those URLs go to the Messages API as they are.

Found while porting the upstream Anthropic AWS tests (supportedUrls › should support image URLs and › should support application/pdf URLs).

How it works today

AnthropicAwsProvider::config_for                       aimux-providers/src/anthropic_aws/mod.rs:206
└─ AnthropicModelConfig { supported_urls: SupportedUrls::default(), .. }   :211   ← empty
AnthropicMessagesModel::supported_urls → config.supported_urls             anthropic/model.rs:148
  └─ generate_text / stream_text download every URL the model does not support
                                                         ← upstream: createChatModel supportedUrls
                                                           { 'image/*': [/^https?:\/\/.*$/], 'application/pdf': [...] }
                                                           (anthropic-aws-provider.ts:251-254)
anthropic::supported_urls()                            anthropic/mod.rs:93   the same two patterns, private

What this PR changes

anthropic::supported_urls()                            anthropic/mod.rs:93   pub(crate)
AnthropicAwsProvider::config_for                       anthropic_aws/mod.rs
└─ supported_urls: crate::anthropic::supported_urls()

Tests (tests/anthropic_aws_settings_test.rs): models_support_http_image_and_pdf_urls expects exactly image/* and application/pdf, an https image and an http PDF supported, an ftp URL not; on master it fails with left: []. The upstream cases are ported in the Anthropic AWS test PR.

Docs: CHANGELOG Fixed.

Note: Vertex's Claude models also report no supported URLs (vertex/anthropic_model.rs:75); whether that matches @ai-sdk/google-vertex is checked when that package is ported.

3. Anthropic AWS explains missing SigV4 credentials

Before: an Anthropic AWS request signed with the AWS_* environment credentials failed with the bare LoadSetting error when AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY was missing.

After: as in @ai-sdk/anthropic-aws 2.0.63, a missing access key fails with the guidance AWS SigV4 authentication requires AWS credentials. Please provide either: 1. … 4. … and a missing secret key with AWS SigV4 authentication requires both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. …, each ending with the setting error as Original error:. The error is AiMuxError::Other, as upstream throws a plain Error. A missing region is still LoadSetting, since upstream loads it before that try. Nothing is sent in either case. The four options name aimux's settings (AnthropicAwsAuth::SigV4, AnthropicAwsAuth::ApiKey) where upstream names its JS options.

Found while porting the upstream Anthropic AWS tests (SigV4 path › throws a guided error when SigV4 credentials are missing).

How it works today

AnthropicMessagesModel::do_generate / do_stream
└─ AwsHeaders::resolve                                 aimux-providers/src/anthropic_aws/model.rs:191
   └─ no x-api-key → Auth::preflight                   :202 → :140
      └─ Auth::environment_credentials                 :106
         ├─ region()?                                  LoadSetting { AWS_REGION }
         ├─ load_setting(None, ACCESS_KEY_ID_ENV_VAR)? :109   ← returned as is
         └─ load_setting(None, SECRET_ACCESS_KEY_ENV_VAR)?  :110   ← returned as is
                                                         // upstream: createSigV4FetchFunction(async () => {
                                                         //   region = loadSetting(..)             :137-143, outside the try
                                                         //   try { accessKeyId, secretAccessKey } catch → guided Error  :162-209

What this PR changes

Auth::environment_credentials                          model.rs:106
├─ let region = self.region()?                          unchanged: still LoadSetting
├─ access key:  load_setting(..).map_err(→ AiMuxError::Other("AWS SigV4 authentication requires AWS credentials. …\nOriginal error: {e}"))
└─ secret key:  load_setting(..).map_err(→ AiMuxError::Other("AWS SigV4 authentication requires both … \nOriginal error: {e}"))

Tests (tests/anthropic_aws_settings_test.rs): a_missing_sigv4_key_fails_with_guidance checks both messages and that nothing is sent; on master it fails with a missing access key is the guidance error: LoadSetting { env_var: "AWS_ACCESS_KEY_ID", name: "access_key_id" }. default_auth_is_the_api_key_variable_else_sigv4_from_the_environment asserted the LoadSetting error and now asserts the guidance. The upstream case is ported in the Anthropic AWS test PR.

Docs: CHANGELOG Fixed; the auth setting's doc names the new error.

Note: Bindings no longer get EnvVar for these two cases, since the error is no longer a LoadSetting; this matches upstream, where the setting error only survives inside the message.

4. Anthropic AWS wraps credential provider failures

Before: an Anthropic AWS credential producer (AnthropicAwsAuth::SigV4 with a future or async function) that failed was reported with its own error only: API call error: failed to resolve AWS credentials: STS denied.

After: as in @ai-sdk/anthropic-aws 2.0.63, the error says AWS credential provider failed: STS denied. Please ensure your credential provider returns valid AWS credentials with access_key_id and secret_access_key. (upstream: accessKeyId and secretAccessKey properties). Nothing is sent.

Found while porting the upstream Anthropic AWS tests (SigV4 path › wraps credential provider rejections with a guided message).

How it works today

AnthropicAwsFetch::fetch → SigV4Fetch::fetch           aimux-providers/src/anthropic_aws/model.rs:216 / aimux-provider-utils/src/sigv4_fetch.rs:422
└─ credentials.resolve()                               the producer below, once per request
   └─ Auth::credentials                                model.rs:118
      └─ producer.resolve().await?                     :121   ← the producer's error as is
   └─ map_err → FetchError::Other("failed to resolve AWS credentials: {error}")   sigv4_fetch.rs:430
                                                         // upstream: try { await options.credentialProvider() }
                                                         //   catch → Error('AWS credential provider failed: …')  :145-160

What this PR changes

Auth::credentials                                      model.rs:118
└─ producer.resolve().await.map_err(→ AiMuxError::Other(
       "AWS credential provider failed: {error}. Please ensure your credential provider returns valid AWS credentials …"))

Tests (tests/anthropic_aws_settings_test.rs): a_failing_credential_producer_is_reported_with_guidance; on master it fails with API call error: failed to resolve AWS credentials: STS denied. The upstream case is ported in the Anthropic AWS test PR.

Docs: CHANGELOG Fixed.

Note: The message still arrives inside AiMuxError::ApiCall, with failed to resolve AWS credentials: before it: the shared SigV4Fetch turns any credential failure into a transport error, for Bedrock and Polly too. Upstream throws a plain Error. Changing that is a provider-utils change for all three AWS providers, not this fix; the upstream test checks the message and passes.

5. Anthropic AWS resolves the default fetch per request

Before: an Anthropic AWS provider created with no fetch setting kept the process default transport current at creation, so a later set_default_transport never reached it, although the fetch setting's doc says the default is resolved per request.

After: as in @ai-sdk/anthropic-aws 2.0.63, which reads globalThis.fetch on each call, every request (API key and SigV4 alike) uses the default transport current when it is made. Creating a provider no longer acquires the default transport, so it no longer freezes the startup proxy configuration either; the first request does.

Found while porting the upstream Anthropic AWS tests (createApiKeyFetchFunction › should resolve default fetch lazily when no custom fetch provided).

How it works today

create_anthropic_aws(settings)                         aimux-providers/src/anthropic_aws/mod.rs:139
└─ model::authenticated(auth, headers,
       settings.fetch.unwrap_or_else(default_fetch))   :166   ← the default transport, acquired now
   └─ AnthropicAwsFetch { signing: SigV4Fetch::new(inner, ..), inner }   model.rs:154-175
      └─ fetch: x-api-key → self.inner.fetch / else self.signing.fetch   :216-230   the captured transport
                                                         // upstream: const effectiveFetch = fetch ?? globalThis.fetch
                                                         //   inside the returned function (anthropic-aws-fetch.ts:36, :124)

What this PR changes

create_anthropic_aws → model::authenticated(auth, headers, settings.fetch)        mod.rs
model::authenticated(.., inner: Option<FetchFunction>)                             model.rs
└─ inner.unwrap_or_else(|| Arc::new(ProcessDefaultFetch))
   struct ProcessDefaultFetch; Fetch::fetch → default_fetch().fetch(request)      looked up per request

Tests (tests/anthropic_aws_settings_test.rs): the_default_transport_is_looked_up_per_request creates the provider while the default transport points at a closed port, then points it at a local plain-HTTP proxy that answers; on master the request goes to the closed port and fails with error sending request for url (http://example.com/v1/messages). The upstream case is ported in the Anthropic AWS test PR.

Docs: CHANGELOG Fixed.

Note: Bedrock and Polly capture default_fetch() at creation the same way (bedrock/mod.rs:417, aws_polly.rs:288); they are fixed when their packages are ported, if their upstream tests show the same.

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change
  • New provider
  • New / updated binding
  • Docs / RFC

Checklist

  • cargo +1.99 fmt -p aimux-providers -- --check passes
  • cargo +1.99 clippy -p aimux-providers --lib --tests -- -D warnings passes
  • cargo +1.99 test -p aimux-providers --test anthropic_aws_settings_test passes (9)
  • scripts/check_provider_boundaries.sh passes
  • Updated relevant docs (CHANGELOG)
  • Commit messages follow Conventional Commits

Notes for reviewers

🤖 Generated with Claude Code

https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5

cunninghamcard-bit and others added 5 commits October 20, 2026 08:53
@ai-sdk/anthropic-aws 2.0.63 gives its chat model the provider name
`anthropic-aws.messages` (anthropic-aws-provider.ts:246); aimux used
`anthropic-aws`. A `name` setting still replaces it, and the providerOptions
key stays `anthropic`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/anthropic-aws 2.0.63 configures its chat model with the same
supportedUrls as @ai-sdk/anthropic: image/* and application/pdf over
http(s) (anthropic-aws-provider.ts:251-254). aimux reported none, so such
URLs were downloaded and inlined. Reuse the Anthropic provider's patterns.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/anthropic-aws 2.0.63 catches a missing access key or secret key
and throws its guidance on providing credentials, with the setting error as
the original error (anthropic-aws-provider.ts:183-209). aimux returned the
bare LoadSetting error. The region is loaded before that try, so a missing
region stays the setting error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@ai-sdk/anthropic-aws 2.0.63 rethrows a rejected credentialProvider as
"AWS credential provider failed: <message>. Please ensure your credential
provider returns valid AWS credentials ..." (anthropic-aws-provider.ts:151-160).
aimux passed the producer's error on alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
With no fetch setting, @ai-sdk/anthropic-aws 2.0.63 calls `globalThis.fetch`
as it is when each request is made (anthropic-aws-fetch.ts:36, :124), and
the settings' own doc says the process default is resolved per request.
create_anthropic_aws instead captured default_fetch() once at creation, so
a later set_default_transport never reached the provider.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…quest

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
@cunninghamcard-bit
cunninghamcard-bit marked this pull request as ready for review October 24, 2026 10:31

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

per-request fetch 解析、默认模型、supported URLs 与 pinned anthropic-aws 一致。Approve。

cunninghamcard-bit and others added 7 commits October 25, 2026 08:16
…quest

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoxcKU4zBD64Lri3e4Lrq5
…esolves-the-default-fetch-per-request

# Conflicts:
#	CHANGELOG.md
…esolves-the-default-fetch-per-request

# Conflicts:
#	CHANGELOG.md
…esolves-the-default-fetch-per-request

# Conflicts:
#	CHANGELOG.md
…esolves-the-default-fetch-per-request

# Conflicts:
#	CHANGELOG.md
…esolves-the-default-fetch-per-request

# Conflicts:
#	CHANGELOG.md
…esolves-the-default-fetch-per-request

# Conflicts:
#	CHANGELOG.md

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

Rebase is content-preserving: only master/parent merges and CHANGELOG/layout conflict resolution; tests and CI unchanged. Re-approving current head.

…esolves-the-default-fetch-per-request

# Conflicts:
#	CHANGELOG.md

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

Rebase verified content-preserving: only master/parent merges and CHANGELOG/layout conflict resolution; CI 16/16. Re-approving current head.

@eric8810
eric8810 merged commit f9e24ad into master Oct 27, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants