@ai-sdk/amazon-bedrock 4.0.24 → 4.0.26
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 +16 -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/docs/08-amazon-bedrock.mdx +1453 -0
- package/package.json +11 -6
- package/src/__fixtures__/bedrock-json-only-text-first.1.chunks.txt +7 -0
- package/src/__fixtures__/bedrock-json-other-tool.1.chunks.txt +6 -0
- package/src/__fixtures__/bedrock-json-other-tool.1.json +24 -0
- package/src/__fixtures__/bedrock-json-tool-text-then-weather-then-json.1.chunks.txt +12 -0
- package/src/__fixtures__/bedrock-json-tool-with-answer.1.json +29 -0
- package/src/__fixtures__/bedrock-json-tool.1.chunks.txt +4 -0
- package/src/__fixtures__/bedrock-json-tool.1.json +35 -0
- package/src/__fixtures__/bedrock-json-tool.2.chunks.txt +6 -0
- package/src/__fixtures__/bedrock-json-tool.2.json +28 -0
- package/src/__fixtures__/bedrock-json-tool.3.chunks.txt +7 -0
- package/src/__fixtures__/bedrock-json-tool.3.json +36 -0
- package/src/__fixtures__/bedrock-json-with-tool.1.chunks.txt +9 -0
- package/src/__fixtures__/bedrock-json-with-tool.1.json +41 -0
- package/src/__fixtures__/bedrock-json-with-tools.1.chunks.txt +12 -0
- package/src/__fixtures__/bedrock-json-with-tools.1.json +50 -0
- package/src/__fixtures__/bedrock-tool-call.1.chunks.txt +6 -0
- package/src/__fixtures__/bedrock-tool-call.1.json +24 -0
- package/src/__fixtures__/bedrock-tool-no-args.chunks.txt +8 -0
- package/src/__fixtures__/bedrock-tool-no-args.json +25 -0
- package/src/anthropic/bedrock-anthropic-fetch.test.ts +344 -0
- package/src/anthropic/bedrock-anthropic-fetch.ts +62 -0
- package/src/anthropic/bedrock-anthropic-options.ts +28 -0
- package/src/anthropic/bedrock-anthropic-provider.test.ts +456 -0
- package/src/anthropic/bedrock-anthropic-provider.ts +357 -0
- package/src/anthropic/index.ts +9 -0
- package/src/bedrock-api-types.ts +195 -0
- package/src/bedrock-chat-language-model.test.ts +4569 -0
- package/src/bedrock-chat-language-model.ts +1019 -0
- package/src/bedrock-chat-options.ts +114 -0
- package/src/bedrock-embedding-model.test.ts +148 -0
- package/src/bedrock-embedding-model.ts +104 -0
- package/src/bedrock-embedding-options.ts +24 -0
- package/src/bedrock-error.ts +6 -0
- package/src/bedrock-event-stream-decoder.ts +59 -0
- package/src/bedrock-event-stream-response-handler.test.ts +233 -0
- package/src/bedrock-event-stream-response-handler.ts +57 -0
- package/src/bedrock-image-model.test.ts +866 -0
- package/src/bedrock-image-model.ts +297 -0
- package/src/bedrock-image-settings.ts +6 -0
- package/src/bedrock-prepare-tools.ts +190 -0
- package/src/bedrock-provider.test.ts +457 -0
- package/src/bedrock-provider.ts +351 -0
- package/src/bedrock-sigv4-fetch.test.ts +675 -0
- package/src/bedrock-sigv4-fetch.ts +138 -0
- package/src/convert-bedrock-usage.test.ts +207 -0
- package/src/convert-bedrock-usage.ts +50 -0
- package/src/convert-to-bedrock-chat-messages.test.ts +1175 -0
- package/src/convert-to-bedrock-chat-messages.ts +452 -0
- package/src/index.ts +10 -0
- package/src/inject-fetch-headers.test.ts +135 -0
- package/src/inject-fetch-headers.ts +32 -0
- package/src/map-bedrock-finish-reason.ts +22 -0
- package/src/normalize-tool-call-id.test.ts +72 -0
- package/src/normalize-tool-call-id.ts +36 -0
- package/src/reranking/__fixtures__/bedrock-reranking.1.json +12 -0
- package/src/reranking/bedrock-reranking-api.ts +44 -0
- package/src/reranking/bedrock-reranking-model.test.ts +299 -0
- package/src/reranking/bedrock-reranking-model.ts +115 -0
- package/src/reranking/bedrock-reranking-options.ts +36 -0
- package/src/version.ts +6 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ImageModelV3,
|
|
3
|
+
ImageModelV3File,
|
|
4
|
+
SharedV3Warning,
|
|
5
|
+
} from '@ai-sdk/provider';
|
|
6
|
+
import {
|
|
7
|
+
FetchFunction,
|
|
8
|
+
Resolvable,
|
|
9
|
+
combineHeaders,
|
|
10
|
+
convertUint8ArrayToBase64,
|
|
11
|
+
createJsonErrorResponseHandler,
|
|
12
|
+
createJsonResponseHandler,
|
|
13
|
+
postJsonToApi,
|
|
14
|
+
resolve,
|
|
15
|
+
} from '@ai-sdk/provider-utils';
|
|
16
|
+
import {
|
|
17
|
+
BedrockImageModelId,
|
|
18
|
+
modelMaxImagesPerCall,
|
|
19
|
+
} from './bedrock-image-settings';
|
|
20
|
+
import { BedrockErrorSchema } from './bedrock-error';
|
|
21
|
+
import { z } from 'zod/v4';
|
|
22
|
+
|
|
23
|
+
type BedrockImageModelConfig = {
|
|
24
|
+
baseUrl: () => string;
|
|
25
|
+
headers: Resolvable<Record<string, string | undefined>>;
|
|
26
|
+
fetch?: FetchFunction;
|
|
27
|
+
_internal?: {
|
|
28
|
+
currentDate?: () => Date;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export class BedrockImageModel implements ImageModelV3 {
|
|
33
|
+
readonly specificationVersion = 'v3';
|
|
34
|
+
readonly provider = 'amazon-bedrock';
|
|
35
|
+
|
|
36
|
+
get maxImagesPerCall(): number {
|
|
37
|
+
return modelMaxImagesPerCall[this.modelId] ?? 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private getUrl(modelId: string): string {
|
|
41
|
+
const encodedModelId = encodeURIComponent(modelId);
|
|
42
|
+
return `${this.config.baseUrl()}/model/${encodedModelId}/invoke`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
readonly modelId: BedrockImageModelId,
|
|
47
|
+
private readonly config: BedrockImageModelConfig,
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
async doGenerate({
|
|
51
|
+
prompt,
|
|
52
|
+
n,
|
|
53
|
+
size,
|
|
54
|
+
aspectRatio,
|
|
55
|
+
seed,
|
|
56
|
+
providerOptions,
|
|
57
|
+
headers,
|
|
58
|
+
abortSignal,
|
|
59
|
+
files,
|
|
60
|
+
mask,
|
|
61
|
+
}: Parameters<ImageModelV3['doGenerate']>[0]): Promise<
|
|
62
|
+
Awaited<ReturnType<ImageModelV3['doGenerate']>>
|
|
63
|
+
> {
|
|
64
|
+
const warnings: Array<SharedV3Warning> = [];
|
|
65
|
+
const [width, height] = size ? size.split('x').map(Number) : [];
|
|
66
|
+
|
|
67
|
+
const hasFiles = files != null && files.length > 0;
|
|
68
|
+
|
|
69
|
+
// Build image generation config (common to most modes)
|
|
70
|
+
const imageGenerationConfig = {
|
|
71
|
+
...(width ? { width } : {}),
|
|
72
|
+
...(height ? { height } : {}),
|
|
73
|
+
...(seed ? { seed } : {}),
|
|
74
|
+
...(n ? { numberOfImages: n } : {}),
|
|
75
|
+
...(providerOptions?.bedrock?.quality
|
|
76
|
+
? { quality: providerOptions.bedrock.quality }
|
|
77
|
+
: {}),
|
|
78
|
+
...(providerOptions?.bedrock?.cfgScale
|
|
79
|
+
? { cfgScale: providerOptions.bedrock.cfgScale }
|
|
80
|
+
: {}),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
let args: Record<string, unknown>;
|
|
84
|
+
|
|
85
|
+
if (hasFiles) {
|
|
86
|
+
// Check if mask is actually provided (has valid data, not just an empty object)
|
|
87
|
+
const hasMask = mask?.type != null;
|
|
88
|
+
const hasMaskPrompt = providerOptions?.bedrock?.maskPrompt != null;
|
|
89
|
+
|
|
90
|
+
// Determine task type from provider options, or infer from mask presence
|
|
91
|
+
const taskType =
|
|
92
|
+
providerOptions?.bedrock?.taskType ??
|
|
93
|
+
(hasMask || hasMaskPrompt ? 'INPAINTING' : 'IMAGE_VARIATION');
|
|
94
|
+
|
|
95
|
+
const sourceImageBase64 = getBase64Data(files[0]);
|
|
96
|
+
|
|
97
|
+
switch (taskType) {
|
|
98
|
+
case 'INPAINTING': {
|
|
99
|
+
const inPaintingParams: Record<string, unknown> = {
|
|
100
|
+
image: sourceImageBase64,
|
|
101
|
+
...(prompt ? { text: prompt } : {}),
|
|
102
|
+
...(providerOptions?.bedrock?.negativeText
|
|
103
|
+
? { negativeText: providerOptions.bedrock.negativeText }
|
|
104
|
+
: {}),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Handle mask - can be either a maskImage or maskPrompt
|
|
108
|
+
if (hasMask) {
|
|
109
|
+
inPaintingParams.maskImage = getBase64Data(mask);
|
|
110
|
+
} else if (hasMaskPrompt) {
|
|
111
|
+
inPaintingParams.maskPrompt = providerOptions.bedrock.maskPrompt;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
args = {
|
|
115
|
+
taskType: 'INPAINTING',
|
|
116
|
+
inPaintingParams,
|
|
117
|
+
imageGenerationConfig,
|
|
118
|
+
};
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case 'OUTPAINTING': {
|
|
123
|
+
const outPaintingParams: Record<string, unknown> = {
|
|
124
|
+
image: sourceImageBase64,
|
|
125
|
+
...(prompt ? { text: prompt } : {}),
|
|
126
|
+
...(providerOptions?.bedrock?.negativeText
|
|
127
|
+
? { negativeText: providerOptions.bedrock.negativeText }
|
|
128
|
+
: {}),
|
|
129
|
+
...(providerOptions?.bedrock?.outPaintingMode
|
|
130
|
+
? { outPaintingMode: providerOptions.bedrock.outPaintingMode }
|
|
131
|
+
: {}),
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Outpainting requires a maskImage (white pixels = area to change)
|
|
135
|
+
if (hasMask) {
|
|
136
|
+
outPaintingParams.maskImage = getBase64Data(mask);
|
|
137
|
+
} else if (hasMaskPrompt) {
|
|
138
|
+
outPaintingParams.maskPrompt = providerOptions.bedrock.maskPrompt;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
args = {
|
|
142
|
+
taskType: 'OUTPAINTING',
|
|
143
|
+
outPaintingParams,
|
|
144
|
+
imageGenerationConfig,
|
|
145
|
+
};
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case 'BACKGROUND_REMOVAL': {
|
|
150
|
+
// Background removal only needs the image, no other params
|
|
151
|
+
args = {
|
|
152
|
+
taskType: 'BACKGROUND_REMOVAL',
|
|
153
|
+
backgroundRemovalParams: {
|
|
154
|
+
image: sourceImageBase64,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
case 'IMAGE_VARIATION': {
|
|
161
|
+
// Image variation can use multiple images
|
|
162
|
+
const images = files.map(file => getBase64Data(file));
|
|
163
|
+
|
|
164
|
+
const imageVariationParams: Record<string, unknown> = {
|
|
165
|
+
images,
|
|
166
|
+
...(prompt ? { text: prompt } : {}),
|
|
167
|
+
...(providerOptions?.bedrock?.negativeText
|
|
168
|
+
? { negativeText: providerOptions.bedrock.negativeText }
|
|
169
|
+
: {}),
|
|
170
|
+
...(providerOptions?.bedrock?.similarityStrength != null
|
|
171
|
+
? {
|
|
172
|
+
similarityStrength:
|
|
173
|
+
providerOptions.bedrock.similarityStrength,
|
|
174
|
+
}
|
|
175
|
+
: {}),
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
args = {
|
|
179
|
+
taskType: 'IMAGE_VARIATION',
|
|
180
|
+
imageVariationParams,
|
|
181
|
+
imageGenerationConfig,
|
|
182
|
+
};
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
default:
|
|
187
|
+
throw new Error(`Unsupported task type: ${taskType}`);
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
// Standard image generation mode
|
|
191
|
+
args = {
|
|
192
|
+
taskType: 'TEXT_IMAGE',
|
|
193
|
+
textToImageParams: {
|
|
194
|
+
text: prompt,
|
|
195
|
+
...(providerOptions?.bedrock?.negativeText
|
|
196
|
+
? {
|
|
197
|
+
negativeText: providerOptions.bedrock.negativeText,
|
|
198
|
+
}
|
|
199
|
+
: {}),
|
|
200
|
+
...(providerOptions?.bedrock?.style
|
|
201
|
+
? {
|
|
202
|
+
style: providerOptions.bedrock.style,
|
|
203
|
+
}
|
|
204
|
+
: {}),
|
|
205
|
+
},
|
|
206
|
+
imageGenerationConfig,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (aspectRatio != undefined) {
|
|
211
|
+
warnings.push({
|
|
212
|
+
type: 'unsupported',
|
|
213
|
+
feature: 'aspectRatio',
|
|
214
|
+
details:
|
|
215
|
+
'This model does not support aspect ratio. Use `size` instead.',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
220
|
+
const { value: response, responseHeaders } = await postJsonToApi({
|
|
221
|
+
url: this.getUrl(this.modelId),
|
|
222
|
+
headers: await resolve(
|
|
223
|
+
combineHeaders(await resolve(this.config.headers), headers),
|
|
224
|
+
),
|
|
225
|
+
body: args,
|
|
226
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
227
|
+
errorSchema: BedrockErrorSchema,
|
|
228
|
+
errorToMessage: error => `${error.type}: ${error.message}`,
|
|
229
|
+
}),
|
|
230
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
231
|
+
bedrockImageResponseSchema,
|
|
232
|
+
),
|
|
233
|
+
abortSignal,
|
|
234
|
+
fetch: this.config.fetch,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Handle moderated/blocked requests
|
|
238
|
+
if (response.status === 'Request Moderated') {
|
|
239
|
+
const moderationReasons = response.details?.['Moderation Reasons'];
|
|
240
|
+
const reasons = Array.isArray(moderationReasons)
|
|
241
|
+
? moderationReasons
|
|
242
|
+
: ['Unknown'];
|
|
243
|
+
throw new Error(
|
|
244
|
+
`Amazon Bedrock request was moderated: ${reasons.join(', ')}`,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Check if images are present
|
|
249
|
+
if (!response.images || response.images.length === 0) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
'Amazon Bedrock returned no images. ' +
|
|
252
|
+
(response.status ? `Status: ${response.status}` : ''),
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
images: response.images,
|
|
258
|
+
warnings,
|
|
259
|
+
response: {
|
|
260
|
+
timestamp: currentDate,
|
|
261
|
+
modelId: this.modelId,
|
|
262
|
+
headers: responseHeaders,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function getBase64Data(file: ImageModelV3File): string {
|
|
269
|
+
if (file.type === 'url') {
|
|
270
|
+
throw new Error(
|
|
271
|
+
'URL-based images are not supported for Amazon Bedrock image editing. ' +
|
|
272
|
+
'Please provide the image data directly.',
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (file.data instanceof Uint8Array) {
|
|
277
|
+
return convertUint8ArrayToBase64(file.data);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Already base64 string
|
|
281
|
+
return file.data;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// minimal version of the schema, focussed on what is needed for the implementation
|
|
285
|
+
// this approach limits breakages when the API changes and increases efficiency
|
|
286
|
+
const bedrockImageResponseSchema = z.object({
|
|
287
|
+
// Normal successful response
|
|
288
|
+
images: z.array(z.string()).optional(),
|
|
289
|
+
|
|
290
|
+
// Moderation response fields
|
|
291
|
+
id: z.string().optional(),
|
|
292
|
+
status: z.string().optional(),
|
|
293
|
+
result: z.unknown().optional(),
|
|
294
|
+
progress: z.unknown().optional(),
|
|
295
|
+
details: z.record(z.string(), z.unknown()).optional(),
|
|
296
|
+
preview: z.unknown().optional(),
|
|
297
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type BedrockImageModelId = 'amazon.nova-canvas-v1:0' | (string & {});
|
|
2
|
+
|
|
3
|
+
// https://docs.aws.amazon.com/nova/latest/userguide/image-gen-req-resp-structure.html
|
|
4
|
+
export const modelMaxImagesPerCall: Record<BedrockImageModelId, number> = {
|
|
5
|
+
'amazon.nova-canvas-v1:0': 5,
|
|
6
|
+
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JSONObject,
|
|
3
|
+
LanguageModelV3CallOptions,
|
|
4
|
+
SharedV3Warning,
|
|
5
|
+
UnsupportedFunctionalityError,
|
|
6
|
+
} from '@ai-sdk/provider';
|
|
7
|
+
import { asSchema } from '@ai-sdk/provider-utils';
|
|
8
|
+
import {
|
|
9
|
+
anthropicTools,
|
|
10
|
+
prepareTools as prepareAnthropicTools,
|
|
11
|
+
} from '@ai-sdk/anthropic/internal';
|
|
12
|
+
import { BedrockTool, BedrockToolConfiguration } from './bedrock-api-types';
|
|
13
|
+
|
|
14
|
+
export async function prepareTools({
|
|
15
|
+
tools,
|
|
16
|
+
toolChoice,
|
|
17
|
+
modelId,
|
|
18
|
+
}: {
|
|
19
|
+
tools: LanguageModelV3CallOptions['tools'];
|
|
20
|
+
toolChoice?: LanguageModelV3CallOptions['toolChoice'];
|
|
21
|
+
modelId: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
toolConfig: BedrockToolConfiguration;
|
|
24
|
+
additionalTools: Record<string, unknown> | undefined;
|
|
25
|
+
betas: Set<string>;
|
|
26
|
+
toolWarnings: SharedV3Warning[];
|
|
27
|
+
}> {
|
|
28
|
+
const toolWarnings: SharedV3Warning[] = [];
|
|
29
|
+
const betas = new Set<string>();
|
|
30
|
+
|
|
31
|
+
if (tools == null || tools.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
toolConfig: {},
|
|
34
|
+
additionalTools: undefined,
|
|
35
|
+
betas,
|
|
36
|
+
toolWarnings,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Filter out unsupported web_search tool and add a warning
|
|
41
|
+
const supportedTools = tools.filter(tool => {
|
|
42
|
+
if (
|
|
43
|
+
tool.type === 'provider' &&
|
|
44
|
+
tool.id === 'anthropic.web_search_20250305'
|
|
45
|
+
) {
|
|
46
|
+
toolWarnings.push({
|
|
47
|
+
type: 'unsupported',
|
|
48
|
+
feature: 'web_search_20250305 tool',
|
|
49
|
+
details:
|
|
50
|
+
'The web_search_20250305 tool is not supported on Amazon Bedrock.',
|
|
51
|
+
});
|
|
52
|
+
return false; // Exclude this tool
|
|
53
|
+
}
|
|
54
|
+
return true; // Include all other tools
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (supportedTools.length === 0) {
|
|
58
|
+
return {
|
|
59
|
+
toolConfig: {},
|
|
60
|
+
additionalTools: undefined,
|
|
61
|
+
betas,
|
|
62
|
+
toolWarnings,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const isAnthropicModel = modelId.includes('anthropic.');
|
|
67
|
+
const ProviderTools = supportedTools.filter(t => t.type === 'provider');
|
|
68
|
+
const functionTools = supportedTools.filter(t => t.type === 'function');
|
|
69
|
+
|
|
70
|
+
let additionalTools: Record<string, unknown> | undefined = undefined;
|
|
71
|
+
const bedrockTools: BedrockTool[] = [];
|
|
72
|
+
|
|
73
|
+
const usingAnthropicTools = isAnthropicModel && ProviderTools.length > 0;
|
|
74
|
+
|
|
75
|
+
// Handle Anthropic provider-defined tools for Anthropic models on Bedrock
|
|
76
|
+
if (usingAnthropicTools) {
|
|
77
|
+
if (functionTools.length > 0) {
|
|
78
|
+
toolWarnings.push({
|
|
79
|
+
type: 'unsupported',
|
|
80
|
+
feature:
|
|
81
|
+
'mixing Anthropic provider-defined tools and standard function tools',
|
|
82
|
+
details:
|
|
83
|
+
'Mixed Anthropic provider-defined tools and standard function tools are not supported in a single call to Bedrock. Only Anthropic tools will be used.',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const {
|
|
88
|
+
toolChoice: preparedAnthropicToolChoice,
|
|
89
|
+
toolWarnings: anthropicToolWarnings,
|
|
90
|
+
betas: anthropicBetas,
|
|
91
|
+
} = await prepareAnthropicTools({
|
|
92
|
+
tools: ProviderTools,
|
|
93
|
+
toolChoice,
|
|
94
|
+
supportsStructuredOutput: false,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
toolWarnings.push(...anthropicToolWarnings);
|
|
98
|
+
anthropicBetas.forEach(beta => betas.add(beta));
|
|
99
|
+
|
|
100
|
+
// For Anthropic tools on Bedrock, only the 'tool_choice' goes into additional fields.
|
|
101
|
+
// The tool definitions themselves are sent in the standard 'toolConfig'.
|
|
102
|
+
if (preparedAnthropicToolChoice) {
|
|
103
|
+
additionalTools = {
|
|
104
|
+
tool_choice: preparedAnthropicToolChoice,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Create a standard Bedrock tool representation for validation purposes
|
|
109
|
+
for (const tool of ProviderTools) {
|
|
110
|
+
const toolFactory = Object.values(anthropicTools).find(factory => {
|
|
111
|
+
const instance = (factory as (args: any) => any)({});
|
|
112
|
+
return instance.id === tool.id;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (toolFactory != null) {
|
|
116
|
+
const fullToolDefinition = (toolFactory as (args: any) => any)({});
|
|
117
|
+
bedrockTools.push({
|
|
118
|
+
toolSpec: {
|
|
119
|
+
name: tool.name,
|
|
120
|
+
inputSchema: {
|
|
121
|
+
json: (await asSchema(fullToolDefinition.inputSchema)
|
|
122
|
+
.jsonSchema) as JSONObject,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
} else {
|
|
127
|
+
toolWarnings.push({ type: 'unsupported', feature: 'tool ${tool.id}' });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
// Report unsupported provider-defined tools for non-anthropic models
|
|
132
|
+
for (const tool of ProviderTools) {
|
|
133
|
+
toolWarnings.push({ type: 'unsupported', feature: `tool ${tool.id}` });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Handle standard function tools for all models
|
|
138
|
+
for (const tool of functionTools) {
|
|
139
|
+
bedrockTools.push({
|
|
140
|
+
toolSpec: {
|
|
141
|
+
name: tool.name,
|
|
142
|
+
...(tool.description?.trim() !== ''
|
|
143
|
+
? { description: tool.description }
|
|
144
|
+
: {}),
|
|
145
|
+
inputSchema: {
|
|
146
|
+
json: tool.inputSchema as JSONObject,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle toolChoice for standard Bedrock tools, but NOT for Anthropic provider-defined tools
|
|
153
|
+
let bedrockToolChoice: BedrockToolConfiguration['toolChoice'] = undefined;
|
|
154
|
+
if (!usingAnthropicTools && bedrockTools.length > 0 && toolChoice) {
|
|
155
|
+
const type = toolChoice.type;
|
|
156
|
+
switch (type) {
|
|
157
|
+
case 'auto':
|
|
158
|
+
bedrockToolChoice = { auto: {} };
|
|
159
|
+
break;
|
|
160
|
+
case 'required':
|
|
161
|
+
bedrockToolChoice = { any: {} };
|
|
162
|
+
break;
|
|
163
|
+
case 'none':
|
|
164
|
+
bedrockTools.length = 0;
|
|
165
|
+
bedrockToolChoice = undefined;
|
|
166
|
+
break;
|
|
167
|
+
case 'tool':
|
|
168
|
+
bedrockToolChoice = { tool: { name: toolChoice.toolName } };
|
|
169
|
+
break;
|
|
170
|
+
default: {
|
|
171
|
+
const _exhaustiveCheck: never = type;
|
|
172
|
+
throw new UnsupportedFunctionalityError({
|
|
173
|
+
functionality: `tool choice type: ${_exhaustiveCheck}`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const toolConfig: BedrockToolConfiguration =
|
|
180
|
+
bedrockTools.length > 0
|
|
181
|
+
? { tools: bedrockTools, toolChoice: bedrockToolChoice }
|
|
182
|
+
: {};
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
toolConfig,
|
|
186
|
+
additionalTools,
|
|
187
|
+
betas,
|
|
188
|
+
toolWarnings,
|
|
189
|
+
};
|
|
190
|
+
}
|