Skip to content

fix(ai-bedrock): pick the bedrock-mantle URL path per model family#959

Open
feiiiiii5 wants to merge 1 commit into
TanStack:mainfrom
feiiiiii5:fix/ai-bedrock-mantle-per-model-path
Open

fix(ai-bedrock): pick the bedrock-mantle URL path per model family#959
feiiiiii5 wants to merge 1 commit into
TanStack:mainfrom
feiiiiii5:fix/ai-bedrock-mantle-per-model-path

Conversation

@feiiiiii5

@feiiiiii5 feiiiiii5 commented Jul 19, 2026

Copy link
Copy Markdown

🎯 Changes

@tanstack/ai-bedrock hardcoded the bedrock-mantle base URL to /v1, but AWS serves different model families on different mantle paths. From the AWS model cards' "Programmatic Access → In-Region endpoint URL":

Model ID Mantle path Source
google.gemma-* /openai/v1 Gemma card — explicitly: "This model is available on the openai/v1/... path on the bedrock-mantle endpoint. This is different from the v1/... path used by other models."
deepseek.* /v1 DeepSeek card
openai.gpt-oss-* /v1 (default)

The hardcoded /v1 was correct only for the latter two. Sending a Gemma request to /v1/chat/completions returned a misleading 401 ... is not enabled for this account (access_denied) instead of a 404 / "wrong path" error, which made the bug hard to diagnose (#925). The SigV4 signing service (bedrock-mantle) was correct — only the path was wrong.

This PR threads the model id through withBedrockDefaultsbuildBaseURLmantlePathForModel, which routes google.gemma-* to /openai/v1 and falls back to /v1 for every other id. The chat and responses adapter constructors pass the model id through; an explicit baseURL override still wins (preserves the existing escape hatch and the E2E → aimock wiring).

The runtime (bedrock-runtime) endpoint is unchanged — every chat-capable model is served at /openai/v1 there, so the model parameter is unused on that branch (covered by a separate test group).

Out of scope (called out as "Suggested directions" in #925)

  • Catalog-driven path lookup (Direction 1) — carry the path per (endpoint, api) on the generated catalog entry so this becomes data-driven instead of a prefix switch. That's a larger follow-up; the prefix match here is the minimal bug fix.
  • Claude-on-mantle at /anthropic/v1/messages — that path serves the Anthropic Messages API, which has a different wire format from OpenAI Chat Completions, so the chat adapter can't drive it regardless of the path. The catalog already marks Claude models as chat: false, so bedrockText('anthropic.claude-…', { endpoint: 'mantle' }) typechecks as an error today. A native Messages-API adapter would be a separate piece of work.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with `pnpm run test:pr`.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.

Test Plan

Ran locally (skipping test:react-native which needs a RN simulator — RN packages are not touched by this PR):

```
pnpm exec nx affected --targets=test:sherif,test:knip,test:docs,test:kiira,test:eslint,test:lib,test:types,test:build,build --exclude=examples/,testing/
pnpm test:dts
```

All green (9/9 Nx tasks, test:dts clean — 127 .d.ts files). The new tests/mantle-path.test.ts adds 14 cases pinning:

  • Gemma (3 variants incl. versioned and hypothetical future) → /openai/v1 on mantle
  • DeepSeek / gpt-oss / unknown id → /v1 default on mantle
  • Backward compat: model omitted / undefined / empty string → /v1 default
  • acme.google.gemma-fake-v1:0 does not match (uses startsWith, not includes — Bedrock model ids always start with the provider prefix)
  • Explicit baseURL override wins even when model would route elsewhere
  • Runtime endpoint: model has no effect on path (3 cases, pins the runtime-branch invariant)

The signature change to withBedrockDefaults(config, forced?, model?) is backward compatible — model is optional and defaults to undefined → /v1 (the pre-#925 behavior). Existing tests/client.test.ts calls without model still pass unchanged.

Fixes #925.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Amazon Bedrock model routing for Gemma models by using the correct /openai/v1 path.
    • Preserved the default /v1 path for other model families.
    • Continued honoring explicit base URL overrides.
    • Kept runtime endpoint behavior unchanged.

…anStack#925)

`buildBaseURL` returned a single hardcoded path per endpoint — `/v1` on
`bedrock-mantle`. AWS serves different model families on different mantle
paths: Gemma is served on `/openai/v1` (per its model card's "In-Region
endpoint URL"), while gpt-oss and DeepSeek use the `/v1` default. Sending a
request to the wrong path returned a misleading `401 ... is not enabled for
this account (access_denied)` instead of a 404 / "wrong path" error, which
made the bug hard to diagnose (TanStack#925).

Thread the model id through `withBedrockDefaults` → `buildBaseURL` →
`mantlePathForModel`, which routes `google.gemma-*` to `/openai/v1` and
falls back to `/v1` for every other id. The chat and responses adapter
constructors pass the model id through; an explicit `baseURL` override still
wins, preserving the existing escape hatch and the E2E → aimock wiring.

The runtime (`bedrock-runtime`) endpoint is unchanged — every chat-capable
model is served at `/openai/v1` there, so the model parameter is unused on
that branch.

Out of scope (called out as "Suggested directions" in TanStack#925): a catalog-
driven refactor that carries the path per `(endpoint, api)` on the
generated catalog entry. That's a larger follow-up; this is the minimal
bug fix. Claude-on-mantle at `/anthropic/v1/messages` is also out of
scope — that path serves the Anthropic Messages API, which has a different
wire format from OpenAI Chat Completions, so the chat adapter can't drive
it regardless of the path. The catalog already marks Claude models as
`chat: false`, so that combination typechecks as an error today.

Adds `tests/mantle-path.test.ts` (14 cases) pinning the per-family mantle
path, the backward-compat default, the `baseURL` override, and the
runtime-branch invariant that the model parameter has no effect there.

Fixes TanStack#925.
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8e3bfed3-5ece-4601-84ee-fbf8591479cb

📥 Commits

Reviewing files that changed from the base of the PR and between bf870fa and f86d940.

📒 Files selected for processing (5)
  • .changeset/ai-bedrock-mantle-per-model-path.md
  • packages/ai-bedrock/src/adapters/responses-text.ts
  • packages/ai-bedrock/src/adapters/text.ts
  • packages/ai-bedrock/src/utils/client.ts
  • packages/ai-bedrock/tests/mantle-path.test.ts

📝 Walkthrough

Walkthrough

Bedrock client URL construction now selects the mantle path by model family. Adapters pass model identifiers into default configuration, while tests cover Gemma, fallback models, overrides, prefix matching, and unchanged runtime routing.

Changes

Bedrock mantle routing

Layer / File(s) Summary
Model-aware mantle URL selection
packages/ai-bedrock/src/utils/client.ts
withBedrockDefaults and buildBaseURL accept the model identifier and route Gemma mantle models to /openai/v1, with /v1 as the fallback.
Adapter model propagation
packages/ai-bedrock/src/adapters/*.ts
Text and responses adapters pass their model to Bedrock default configuration.
Routing regression coverage and release metadata
packages/ai-bedrock/tests/mantle-path.test.ts, .changeset/ai-bedrock-mantle-per-model-path.md
Tests verify model routing, overrides, defaults, and runtime behavior; a patch changeset documents the fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • TanStack/ai#665: Introduced the related Bedrock/OpenAI-compatible adapter and client option plumbing.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: selecting the Bedrock mantle URL path per model family.
Description check ✅ Passed The description follows the template and includes changes, checklist items, release impact, and test plan details.
Linked Issues check ✅ Passed The PR fixes the core #925 bug by routing Gemma to the correct mantle path while preserving default behavior and overrides.
Out of Scope Changes check ✅ Passed The changes stay focused on the mantle path fix, related adapter plumbing, tests, and the required changeset.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Labels

None yet

Projects

None yet

1 participant