@ai-sdk/amazon-bedrock 4.0.27 → 4.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/anthropic/index.js +1 -1
- package/dist/anthropic/index.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +9 -5
- package/src/__fixtures__/bedrock-json-only-text-first.1.chunks.txt +0 -7
- package/src/__fixtures__/bedrock-json-other-tool.1.chunks.txt +0 -6
- package/src/__fixtures__/bedrock-json-other-tool.1.json +0 -24
- package/src/__fixtures__/bedrock-json-tool-text-then-weather-then-json.1.chunks.txt +0 -12
- package/src/__fixtures__/bedrock-json-tool-with-answer.1.json +0 -29
- package/src/__fixtures__/bedrock-json-tool.1.chunks.txt +0 -4
- package/src/__fixtures__/bedrock-json-tool.1.json +0 -35
- package/src/__fixtures__/bedrock-json-tool.2.chunks.txt +0 -6
- package/src/__fixtures__/bedrock-json-tool.2.json +0 -28
- package/src/__fixtures__/bedrock-json-tool.3.chunks.txt +0 -7
- package/src/__fixtures__/bedrock-json-tool.3.json +0 -36
- package/src/__fixtures__/bedrock-json-with-tool.1.chunks.txt +0 -9
- package/src/__fixtures__/bedrock-json-with-tool.1.json +0 -41
- package/src/__fixtures__/bedrock-json-with-tools.1.chunks.txt +0 -12
- package/src/__fixtures__/bedrock-json-with-tools.1.json +0 -50
- package/src/__fixtures__/bedrock-tool-call.1.chunks.txt +0 -6
- package/src/__fixtures__/bedrock-tool-call.1.json +0 -24
- package/src/__fixtures__/bedrock-tool-no-args.chunks.txt +0 -8
- package/src/__fixtures__/bedrock-tool-no-args.json +0 -25
- package/src/anthropic/bedrock-anthropic-fetch.test.ts +0 -344
- package/src/anthropic/bedrock-anthropic-provider.test.ts +0 -456
- package/src/bedrock-chat-language-model.test.ts +0 -4569
- package/src/bedrock-embedding-model.test.ts +0 -148
- package/src/bedrock-event-stream-response-handler.test.ts +0 -233
- package/src/bedrock-image-model.test.ts +0 -866
- package/src/bedrock-provider.test.ts +0 -457
- package/src/bedrock-sigv4-fetch.test.ts +0 -675
- package/src/convert-bedrock-usage.test.ts +0 -207
- package/src/convert-to-bedrock-chat-messages.test.ts +0 -1175
- package/src/inject-fetch-headers.test.ts +0 -135
- package/src/normalize-tool-call-id.test.ts +0 -72
- package/src/reranking/__fixtures__/bedrock-reranking.1.json +0 -12
- package/src/reranking/bedrock-reranking-model.test.ts +0 -299
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { createTestServer } from '@ai-sdk/test-server/with-vitest';
|
|
2
|
-
import { BedrockEmbeddingModel } from './bedrock-embedding-model';
|
|
3
|
-
import { injectFetchHeaders } from './inject-fetch-headers';
|
|
4
|
-
import { beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
|
|
6
|
-
const mockEmbeddings = [
|
|
7
|
-
[-0.09, 0.05, -0.02, 0.01, 0.04],
|
|
8
|
-
[-0.08, 0.06, -0.03, 0.02, 0.03],
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
const fakeFetchWithAuth = injectFetchHeaders({ 'x-amz-auth': 'test-auth' });
|
|
12
|
-
|
|
13
|
-
const testValues = ['sunny day at the beach', 'rainy day in the city'];
|
|
14
|
-
|
|
15
|
-
const embedUrl = `https://bedrock-runtime.us-east-1.amazonaws.com/model/${encodeURIComponent(
|
|
16
|
-
'amazon.titan-embed-text-v2:0',
|
|
17
|
-
)}/invoke`;
|
|
18
|
-
|
|
19
|
-
describe('doEmbed', () => {
|
|
20
|
-
const mockConfigHeaders = {
|
|
21
|
-
'config-header': 'config-value',
|
|
22
|
-
'shared-header': 'config-shared',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const server = createTestServer({
|
|
26
|
-
[embedUrl]: {
|
|
27
|
-
response: {
|
|
28
|
-
type: 'binary',
|
|
29
|
-
headers: {
|
|
30
|
-
'content-type': 'application/json',
|
|
31
|
-
},
|
|
32
|
-
body: Buffer.from(
|
|
33
|
-
JSON.stringify({
|
|
34
|
-
embedding: mockEmbeddings[0],
|
|
35
|
-
inputTextTokenCount: 8,
|
|
36
|
-
}),
|
|
37
|
-
),
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const model = new BedrockEmbeddingModel('amazon.titan-embed-text-v2:0', {
|
|
43
|
-
baseUrl: () => 'https://bedrock-runtime.us-east-1.amazonaws.com',
|
|
44
|
-
headers: mockConfigHeaders,
|
|
45
|
-
fetch: fakeFetchWithAuth,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
let callCount = 0;
|
|
49
|
-
|
|
50
|
-
beforeEach(() => {
|
|
51
|
-
callCount = 0;
|
|
52
|
-
server.urls[embedUrl].response = {
|
|
53
|
-
type: 'binary',
|
|
54
|
-
headers: {
|
|
55
|
-
'content-type': 'application/json',
|
|
56
|
-
},
|
|
57
|
-
body: Buffer.from(
|
|
58
|
-
JSON.stringify({
|
|
59
|
-
embedding: mockEmbeddings[0],
|
|
60
|
-
inputTextTokenCount: 8,
|
|
61
|
-
}),
|
|
62
|
-
),
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should handle single input value and return embeddings', async () => {
|
|
67
|
-
const { embeddings } = await model.doEmbed({
|
|
68
|
-
values: [testValues[0]],
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
expect(embeddings.length).toBe(1);
|
|
72
|
-
expect(embeddings[0]).toStrictEqual(mockEmbeddings[0]);
|
|
73
|
-
|
|
74
|
-
const body = await server.calls[0].requestBodyJson;
|
|
75
|
-
expect(body).toEqual({
|
|
76
|
-
inputText: testValues[0],
|
|
77
|
-
dimensions: undefined,
|
|
78
|
-
normalize: undefined,
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should handle single input value and extract usage', async () => {
|
|
83
|
-
const { usage } = await model.doEmbed({
|
|
84
|
-
values: [testValues[0]],
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
expect(usage?.tokens).toStrictEqual(8);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should properly combine headers from all sources', async () => {
|
|
91
|
-
const optionsHeaders = {
|
|
92
|
-
'options-header': 'options-value',
|
|
93
|
-
'shared-header': 'options-shared',
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const modelWithHeaders = new BedrockEmbeddingModel(
|
|
97
|
-
'amazon.titan-embed-text-v2:0',
|
|
98
|
-
{
|
|
99
|
-
baseUrl: () => 'https://bedrock-runtime.us-east-1.amazonaws.com',
|
|
100
|
-
headers: {
|
|
101
|
-
'model-header': 'model-value',
|
|
102
|
-
'shared-header': 'model-shared',
|
|
103
|
-
},
|
|
104
|
-
fetch: injectFetchHeaders({
|
|
105
|
-
'signed-header': 'signed-value',
|
|
106
|
-
authorization: 'AWS4-HMAC-SHA256...',
|
|
107
|
-
}),
|
|
108
|
-
},
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
await modelWithHeaders.doEmbed({
|
|
112
|
-
values: [testValues[0]],
|
|
113
|
-
headers: optionsHeaders,
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const requestHeaders = server.calls[0].requestHeaders;
|
|
117
|
-
expect(requestHeaders['options-header']).toBe('options-value');
|
|
118
|
-
expect(requestHeaders['model-header']).toBe('model-value');
|
|
119
|
-
expect(requestHeaders['signed-header']).toBe('signed-value');
|
|
120
|
-
expect(requestHeaders['authorization']).toBe('AWS4-HMAC-SHA256...');
|
|
121
|
-
expect(requestHeaders['shared-header']).toBe('options-shared');
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('should work with partial headers', async () => {
|
|
125
|
-
const modelWithPartialHeaders = new BedrockEmbeddingModel(
|
|
126
|
-
'amazon.titan-embed-text-v2:0',
|
|
127
|
-
{
|
|
128
|
-
baseUrl: () => 'https://bedrock-runtime.us-east-1.amazonaws.com',
|
|
129
|
-
headers: {
|
|
130
|
-
'model-header': 'model-value',
|
|
131
|
-
},
|
|
132
|
-
fetch: injectFetchHeaders({
|
|
133
|
-
'signed-header': 'signed-value',
|
|
134
|
-
authorization: 'AWS4-HMAC-SHA256...',
|
|
135
|
-
}),
|
|
136
|
-
},
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
await modelWithPartialHeaders.doEmbed({
|
|
140
|
-
values: [testValues[0]],
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const requestHeaders = server.calls[0].requestHeaders;
|
|
144
|
-
expect(requestHeaders['model-header']).toBe('model-value');
|
|
145
|
-
expect(requestHeaders['signed-header']).toBe('signed-value');
|
|
146
|
-
expect(requestHeaders['authorization']).toBe('AWS4-HMAC-SHA256...');
|
|
147
|
-
});
|
|
148
|
-
});
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { EmptyResponseBodyError } from '@ai-sdk/provider';
|
|
2
|
-
import { createBedrockEventStreamResponseHandler } from './bedrock-event-stream-response-handler';
|
|
3
|
-
import { EventStreamCodec } from '@smithy/eventstream-codec';
|
|
4
|
-
import { z } from 'zod/v4';
|
|
5
|
-
import { describe, it, expect, vi, MockInstance } from 'vitest';
|
|
6
|
-
|
|
7
|
-
// Helper that constructs a properly framed message.
|
|
8
|
-
// The first 4 bytes will contain the frame total length (big-endian).
|
|
9
|
-
const createFrame = (payload: Uint8Array): Uint8Array => {
|
|
10
|
-
const totalLength = 4 + payload.length;
|
|
11
|
-
const frame = new Uint8Array(totalLength);
|
|
12
|
-
new DataView(frame.buffer).setUint32(0, totalLength, false);
|
|
13
|
-
frame.set(payload, 4);
|
|
14
|
-
return frame;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// Mock EventStreamCodec
|
|
18
|
-
vi.mock('@smithy/eventstream-codec', () => ({
|
|
19
|
-
EventStreamCodec: vi.fn(),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
describe('createEventSourceResponseHandler', () => {
|
|
23
|
-
// Define a sample schema for testing
|
|
24
|
-
const testSchema = z.object({
|
|
25
|
-
chunk: z.object({
|
|
26
|
-
content: z.string(),
|
|
27
|
-
}),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('throws EmptyResponseBodyError when response body is null', async () => {
|
|
31
|
-
const response = new Response(null);
|
|
32
|
-
const handler = createBedrockEventStreamResponseHandler(testSchema);
|
|
33
|
-
|
|
34
|
-
await expect(
|
|
35
|
-
handler({
|
|
36
|
-
response,
|
|
37
|
-
url: 'test-url',
|
|
38
|
-
requestBodyValues: {},
|
|
39
|
-
}),
|
|
40
|
-
).rejects.toThrow(EmptyResponseBodyError);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('successfully processes valid event stream data', async () => {
|
|
44
|
-
// Prepare the message we wish to simulate.
|
|
45
|
-
// Our decoded message will contain headers and a body that is valid JSON.
|
|
46
|
-
const message = {
|
|
47
|
-
headers: {
|
|
48
|
-
':message-type': { value: 'event' },
|
|
49
|
-
':event-type': { value: 'chunk' },
|
|
50
|
-
},
|
|
51
|
-
body: new TextEncoder().encode(
|
|
52
|
-
JSON.stringify({ content: 'test message' }),
|
|
53
|
-
),
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// Create a frame that properly encapsulates the message.
|
|
57
|
-
const dummyPayload = new Uint8Array([1, 2, 3, 4]); // arbitrary payload that makes the length check pass
|
|
58
|
-
const frame = createFrame(dummyPayload);
|
|
59
|
-
|
|
60
|
-
const mockDecode = vi.fn().mockReturnValue(message);
|
|
61
|
-
(EventStreamCodec as unknown as MockInstance).mockImplementation(() => ({
|
|
62
|
-
decode: mockDecode,
|
|
63
|
-
}));
|
|
64
|
-
|
|
65
|
-
// Create a stream that enqueues the complete frame.
|
|
66
|
-
const stream = new ReadableStream({
|
|
67
|
-
start(controller) {
|
|
68
|
-
controller.enqueue(frame);
|
|
69
|
-
controller.close();
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const response = new Response(stream);
|
|
74
|
-
const handler = createBedrockEventStreamResponseHandler(testSchema);
|
|
75
|
-
const result = await handler({
|
|
76
|
-
response,
|
|
77
|
-
url: 'test-url',
|
|
78
|
-
requestBodyValues: {},
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const reader = result.value.getReader();
|
|
82
|
-
const { done, value } = await reader.read();
|
|
83
|
-
|
|
84
|
-
expect(done).toBe(false);
|
|
85
|
-
expect(value).toEqual({
|
|
86
|
-
success: true,
|
|
87
|
-
value: { chunk: { content: 'test message' } },
|
|
88
|
-
rawValue: { chunk: { content: 'test message' } },
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('handles invalid JSON data', async () => {
|
|
93
|
-
// Our mock decode returns a body that is not valid JSON.
|
|
94
|
-
const message = {
|
|
95
|
-
headers: {
|
|
96
|
-
':message-type': { value: 'event' },
|
|
97
|
-
':event-type': { value: 'chunk' },
|
|
98
|
-
},
|
|
99
|
-
body: new TextEncoder().encode('invalid json'),
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const dummyPayload = new Uint8Array([5, 6, 7, 8]);
|
|
103
|
-
const frame = createFrame(dummyPayload);
|
|
104
|
-
|
|
105
|
-
const mockDecode = vi.fn().mockReturnValue(message);
|
|
106
|
-
(EventStreamCodec as unknown as MockInstance).mockImplementation(() => ({
|
|
107
|
-
decode: mockDecode,
|
|
108
|
-
}));
|
|
109
|
-
|
|
110
|
-
const stream = new ReadableStream({
|
|
111
|
-
start(controller) {
|
|
112
|
-
controller.enqueue(frame);
|
|
113
|
-
controller.close();
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const response = new Response(stream);
|
|
118
|
-
const handler = createBedrockEventStreamResponseHandler(testSchema);
|
|
119
|
-
const result = await handler({
|
|
120
|
-
response,
|
|
121
|
-
url: 'test-url',
|
|
122
|
-
requestBodyValues: {},
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const reader = result.value.getReader();
|
|
126
|
-
const { done, value } = await reader.read();
|
|
127
|
-
|
|
128
|
-
expect(done).toBe(false);
|
|
129
|
-
// When JSON is invalid, safeParseJSON returns a result with success: false.
|
|
130
|
-
expect(value?.success).toBe(false);
|
|
131
|
-
expect((value as { success: false; error: Error }).error).toBeDefined();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('handles schema validation failures', async () => {
|
|
135
|
-
// The decoded message returns valid JSON but that does not meet our schema.
|
|
136
|
-
const message = {
|
|
137
|
-
headers: {
|
|
138
|
-
':message-type': { value: 'event' },
|
|
139
|
-
':event-type': { value: 'chunk' },
|
|
140
|
-
},
|
|
141
|
-
body: new TextEncoder().encode(JSON.stringify({ invalid: 'data' })),
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const dummyPayload = new Uint8Array([9, 10, 11, 12]);
|
|
145
|
-
const frame = createFrame(dummyPayload);
|
|
146
|
-
|
|
147
|
-
const mockDecode = vi.fn().mockReturnValue(message);
|
|
148
|
-
(EventStreamCodec as unknown as MockInstance).mockImplementation(() => ({
|
|
149
|
-
decode: mockDecode,
|
|
150
|
-
}));
|
|
151
|
-
|
|
152
|
-
const stream = new ReadableStream({
|
|
153
|
-
start(controller) {
|
|
154
|
-
controller.enqueue(frame);
|
|
155
|
-
controller.close();
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
const response = new Response(stream);
|
|
160
|
-
const handler = createBedrockEventStreamResponseHandler(testSchema);
|
|
161
|
-
const result = await handler({
|
|
162
|
-
response,
|
|
163
|
-
url: 'test-url',
|
|
164
|
-
requestBodyValues: {},
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
const reader = result.value.getReader();
|
|
168
|
-
const { done, value } = await reader.read();
|
|
169
|
-
|
|
170
|
-
expect(done).toBe(false);
|
|
171
|
-
// The schema does not match so safeParseJSON with the schema should yield success: false.
|
|
172
|
-
expect(value?.success).toBe(false);
|
|
173
|
-
expect((value as { success: false; error: Error }).error).toBeDefined();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('handles partial messages correctly', async () => {
|
|
177
|
-
// In this test, we simulate a partial message followed by a complete one.
|
|
178
|
-
// The first invocation of decode will throw an error (simulated incomplete message),
|
|
179
|
-
// and the subsequent invocation returns a valid event.
|
|
180
|
-
const message = {
|
|
181
|
-
headers: {
|
|
182
|
-
':message-type': { value: 'event' },
|
|
183
|
-
':event-type': { value: 'chunk' },
|
|
184
|
-
},
|
|
185
|
-
body: new TextEncoder().encode(
|
|
186
|
-
JSON.stringify({ content: 'complete message' }),
|
|
187
|
-
),
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
const dummyPayload1 = new Uint8Array([13, 14]); // too short, part of a frame
|
|
191
|
-
const frame1 = createFrame(dummyPayload1);
|
|
192
|
-
const dummyPayload2 = new Uint8Array([15, 16, 17, 18]);
|
|
193
|
-
const frame2 = createFrame(dummyPayload2);
|
|
194
|
-
|
|
195
|
-
const mockDecode = vi
|
|
196
|
-
.fn()
|
|
197
|
-
.mockImplementationOnce(() => {
|
|
198
|
-
throw new Error('Incomplete data');
|
|
199
|
-
})
|
|
200
|
-
.mockReturnValue(message);
|
|
201
|
-
(EventStreamCodec as unknown as MockInstance).mockImplementation(() => ({
|
|
202
|
-
decode: mockDecode,
|
|
203
|
-
}));
|
|
204
|
-
|
|
205
|
-
const stream = new ReadableStream({
|
|
206
|
-
start(controller) {
|
|
207
|
-
// Send first, incomplete frame (decode will throw error).
|
|
208
|
-
controller.enqueue(frame1);
|
|
209
|
-
// Then send a proper frame.
|
|
210
|
-
controller.enqueue(frame2);
|
|
211
|
-
controller.close();
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
const response = new Response(stream);
|
|
216
|
-
const handler = createBedrockEventStreamResponseHandler(testSchema);
|
|
217
|
-
const result = await handler({
|
|
218
|
-
response,
|
|
219
|
-
url: 'test-url',
|
|
220
|
-
requestBodyValues: {},
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
const reader = result.value.getReader();
|
|
224
|
-
const { done, value } = await reader.read();
|
|
225
|
-
|
|
226
|
-
expect(done).toBe(false);
|
|
227
|
-
expect(value).toEqual({
|
|
228
|
-
success: true,
|
|
229
|
-
value: { chunk: { content: 'complete message' } },
|
|
230
|
-
rawValue: { chunk: { content: 'complete message' } },
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
});
|