Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bedrock-inference-profile-arn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ai-sdk/amazon-bedrock": patch
---

fix(amazon-bedrock): support inference profile ARNs as model ids. The provider built request URLs with `encodeURIComponent(modelId)`, which encodes the `/` in an application inference profile ARN (e.g. `arn:aws:bedrock:...:application-inference-profile/abc123`) to `%2F` and made Bedrock reject it with `400 The provided model identifier is invalid`. Model ids are now encoded per path segment so slashes stay literal, matching the path format Bedrock expects.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
type AmazonBedrockLanguageModelChatOptions,
type AmazonBedrockChatModelId,
} from './amazon-bedrock-chat-language-model-options';
import { encodeModelId } from './amazon-bedrock-encode-model-id';
import { AmazonBedrockErrorSchema } from './amazon-bedrock-error';
import { createAmazonBedrockEventStreamResponseHandler } from './amazon-bedrock-event-stream-response-handler';
import { prepareTools } from './amazon-bedrock-prepare-tools';
Expand Down Expand Up @@ -1049,7 +1050,7 @@ export class AmazonBedrockChatLanguageModel implements LanguageModelV4 {
}

private getUrl(modelId: string) {
const encodedModelId = encodeURIComponent(modelId);
const encodedModelId = encodeModelId(modelId);
return `${this.config.baseUrl()}/model/${encodedModelId}`;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
amazonBedrockEmbeddingModelOptionsSchema,
type AmazonBedrockEmbeddingModelId,
} from './amazon-bedrock-embedding-model-options';
import { encodeModelId } from './amazon-bedrock-encode-model-id';
import { AmazonBedrockErrorSchema } from './amazon-bedrock-error';
import { z } from 'zod/v4';

Expand Down Expand Up @@ -59,7 +60,7 @@ export class AmazonBedrockEmbeddingModel implements EmbeddingModelV4 {
) {}

private getUrl(modelId: string): string {
const encodedModelId = encodeURIComponent(modelId);
const encodedModelId = encodeModelId(modelId);
return `${this.config.baseUrl()}/model/${encodedModelId}/invoke`;
}

Expand Down
30 changes: 30 additions & 0 deletions packages/amazon-bedrock/src/amazon-bedrock-encode-model-id.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, expect, it } from 'vitest';
import { encodeModelId } from './amazon-bedrock-encode-model-id';

describe('encodeModelId', () => {
it('encodes a standard model id (colon percent-encoded, no slashes)', () => {
expect(encodeModelId('us.amazon.nova-2-lite-v1:0')).toBe(
'us.amazon.nova-2-lite-v1%3A0',
);
});

it('leaves a plain model id without special characters unchanged', () => {
expect(encodeModelId('anthropic.claude-3-haiku-20240307-v1:0')).toBe(
'anthropic.claude-3-haiku-20240307-v1%3A0',
);
});

it('preserves the slash in an application inference profile ARN', () => {
expect(
encodeModelId(
'arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123xyz',
),
).toBe(
'arn%3Aaws%3Abedrock%3Aus-east-1%3A123456789012%3Aapplication-inference-profile/abc123xyz',
);
});

it('encodes each segment of a multi-slash path individually', () => {
expect(encodeModelId('a b/c:d/e')).toBe('a%20b/c%3Ad/e');
});
});
19 changes: 19 additions & 0 deletions packages/amazon-bedrock/src/amazon-bedrock-encode-model-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Encodes a Bedrock model identifier for use in a REST API URL path.
*
* Standard model ids (e.g. `us.amazon.nova-2-lite-v1:0`) contain no slashes and
* are encoded as a single segment. Inference profile ARNs, however, contain a
* slash (e.g.
* `arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/abc123`).
* Bedrock's REST API treats the model id as a greedy path label and expects the
* slash to stay literal — encoding it to `%2F` (as a plain `encodeURIComponent`
* would) makes Bedrock reject the request with
* `400 The provided model identifier is invalid`.
*
* Encoding each `/`-separated segment individually keeps slashes literal while
* still percent-encoding every other special character (such as the `:` in an
* ARN), matching the path format Bedrock documents for inference profiles.
*/
export function encodeModelId(modelId: string): string {
return modelId.split('/').map(encodeURIComponent).join('/');
}
3 changes: 2 additions & 1 deletion packages/amazon-bedrock/src/amazon-bedrock-image-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
type AmazonBedrockImageModelId,
} from './amazon-bedrock-image-settings';
import { amazonBedrockImageModelOptionsSchema } from './amazon-bedrock-image-model-options';
import { encodeModelId } from './amazon-bedrock-encode-model-id';
import { AmazonBedrockErrorSchema } from './amazon-bedrock-error';
import { z } from 'zod/v4';

Expand Down Expand Up @@ -57,7 +58,7 @@ export class AmazonBedrockImageModel implements ImageModelV4 {
}

private getUrl(modelId: string): string {
const encodedModelId = encodeURIComponent(modelId);
const encodedModelId = encodeModelId(modelId);
return `${this.config.baseUrl()}/model/${encodedModelId}/invoke`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
createSigV4FetchFunction,
type AmazonBedrockCredentials,
} from '../amazon-bedrock-sigv4-fetch';
import { encodeModelId } from '../amazon-bedrock-encode-model-id';
import { createAmazonBedrockAnthropicFetch } from './amazon-bedrock-anthropic-fetch';
import type { AmazonBedrockAnthropicModelId } from './amazon-bedrock-anthropic-options';
import { VERSION } from '../version';
Expand Down Expand Up @@ -258,7 +259,7 @@ export function createAmazonBedrockAnthropic(
fetch: fetchFunction,

buildRequestUrl: (baseURL, isStreaming) =>
`${baseURL}/model/${encodeURIComponent(modelId)}/${
`${baseURL}/model/${encodeModelId(modelId)}/${
isStreaming ? 'invoke-with-response-stream' : 'invoke'
}`,

Expand Down