@fairyhunter13/ai-anthropic 3.0.58-fork.1
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 +2521 -0
- package/LICENSE +13 -0
- package/README.md +43 -0
- package/dist/index.d.mts +1099 -0
- package/dist/index.d.ts +1099 -0
- package/dist/index.js +5222 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +5298 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internal/index.d.mts +960 -0
- package/dist/internal/index.d.ts +960 -0
- package/dist/internal/index.js +5122 -0
- package/dist/internal/index.js.map +1 -0
- package/dist/internal/index.mjs +5190 -0
- package/dist/internal/index.mjs.map +1 -0
- package/docs/05-anthropic.mdx +1321 -0
- package/internal.d.ts +1 -0
- package/package.json +83 -0
- package/src/anthropic-error.ts +26 -0
- package/src/anthropic-message-metadata.ts +143 -0
- package/src/anthropic-messages-api.ts +1348 -0
- package/src/anthropic-messages-language-model.ts +2407 -0
- package/src/anthropic-messages-options.ts +267 -0
- package/src/anthropic-prepare-tools.ts +404 -0
- package/src/anthropic-provider.ts +177 -0
- package/src/anthropic-tools.ts +238 -0
- package/src/convert-anthropic-messages-usage.ts +73 -0
- package/src/convert-to-anthropic-messages-prompt.ts +1144 -0
- package/src/forward-anthropic-container-id-from-last-step.ts +38 -0
- package/src/get-cache-control.ts +63 -0
- package/src/index.ts +17 -0
- package/src/internal/index.ts +4 -0
- package/src/map-anthropic-stop-reason.ts +30 -0
- package/src/tool/bash_20241022.ts +33 -0
- package/src/tool/bash_20250124.ts +33 -0
- package/src/tool/code-execution_20250522.ts +61 -0
- package/src/tool/code-execution_20250825.ts +281 -0
- package/src/tool/code-execution_20260120.ts +315 -0
- package/src/tool/computer_20241022.ts +87 -0
- package/src/tool/computer_20250124.ts +130 -0
- package/src/tool/computer_20251124.ts +151 -0
- package/src/tool/memory_20250818.ts +62 -0
- package/src/tool/text-editor_20241022.ts +69 -0
- package/src/tool/text-editor_20250124.ts +69 -0
- package/src/tool/text-editor_20250429.ts +70 -0
- package/src/tool/text-editor_20250728.ts +86 -0
- package/src/tool/tool-search-bm25_20251119.ts +99 -0
- package/src/tool/tool-search-regex_20251119.ts +111 -0
- package/src/tool/web-fetch-20250910.ts +145 -0
- package/src/tool/web-fetch-20260209.ts +145 -0
- package/src/tool/web-search_20250305.ts +136 -0
- package/src/tool/web-search_20260209.ts +136 -0
- package/src/version.ts +6 -0
|
@@ -0,0 +1,2407 @@
|
|
|
1
|
+
import {
|
|
2
|
+
APICallError,
|
|
3
|
+
JSONObject,
|
|
4
|
+
LanguageModelV3,
|
|
5
|
+
LanguageModelV3CallOptions,
|
|
6
|
+
LanguageModelV3Content,
|
|
7
|
+
LanguageModelV3FinishReason,
|
|
8
|
+
LanguageModelV3FunctionTool,
|
|
9
|
+
LanguageModelV3GenerateResult,
|
|
10
|
+
LanguageModelV3Prompt,
|
|
11
|
+
LanguageModelV3Source,
|
|
12
|
+
LanguageModelV3StreamPart,
|
|
13
|
+
LanguageModelV3StreamResult,
|
|
14
|
+
LanguageModelV3ToolCall,
|
|
15
|
+
SharedV3ProviderMetadata,
|
|
16
|
+
SharedV3Warning,
|
|
17
|
+
} from '@ai-sdk/provider';
|
|
18
|
+
import {
|
|
19
|
+
combineHeaders,
|
|
20
|
+
createEventSourceResponseHandler,
|
|
21
|
+
createJsonResponseHandler,
|
|
22
|
+
createToolNameMapping,
|
|
23
|
+
FetchFunction,
|
|
24
|
+
generateId,
|
|
25
|
+
InferSchema,
|
|
26
|
+
parseProviderOptions,
|
|
27
|
+
ParseResult,
|
|
28
|
+
postJsonToApi,
|
|
29
|
+
Resolvable,
|
|
30
|
+
resolve,
|
|
31
|
+
} from '@ai-sdk/provider-utils';
|
|
32
|
+
import { anthropicFailedResponseHandler } from './anthropic-error';
|
|
33
|
+
import { AnthropicMessageMetadata } from './anthropic-message-metadata';
|
|
34
|
+
import {
|
|
35
|
+
AnthropicContainer,
|
|
36
|
+
anthropicMessagesChunkSchema,
|
|
37
|
+
anthropicMessagesResponseSchema,
|
|
38
|
+
AnthropicReasoningMetadata,
|
|
39
|
+
AnthropicResponseContextManagement,
|
|
40
|
+
AnthropicTool,
|
|
41
|
+
Citation,
|
|
42
|
+
} from './anthropic-messages-api';
|
|
43
|
+
import {
|
|
44
|
+
AnthropicMessagesModelId,
|
|
45
|
+
anthropicLanguageModelOptions,
|
|
46
|
+
} from './anthropic-messages-options';
|
|
47
|
+
import { prepareTools } from './anthropic-prepare-tools';
|
|
48
|
+
import {
|
|
49
|
+
AnthropicMessagesUsage,
|
|
50
|
+
convertAnthropicMessagesUsage,
|
|
51
|
+
} from './convert-anthropic-messages-usage';
|
|
52
|
+
import { convertToAnthropicMessagesPrompt } from './convert-to-anthropic-messages-prompt';
|
|
53
|
+
import { CacheControlValidator } from './get-cache-control';
|
|
54
|
+
import { mapAnthropicStopReason } from './map-anthropic-stop-reason';
|
|
55
|
+
|
|
56
|
+
function createCitationSource(
|
|
57
|
+
citation: Citation,
|
|
58
|
+
citationDocuments: Array<{
|
|
59
|
+
title: string;
|
|
60
|
+
filename?: string;
|
|
61
|
+
mediaType: string;
|
|
62
|
+
}>,
|
|
63
|
+
generateId: () => string,
|
|
64
|
+
): LanguageModelV3Source | undefined {
|
|
65
|
+
if (citation.type === 'web_search_result_location') {
|
|
66
|
+
return {
|
|
67
|
+
type: 'source' as const,
|
|
68
|
+
sourceType: 'url' as const,
|
|
69
|
+
id: generateId(),
|
|
70
|
+
url: citation.url,
|
|
71
|
+
title: citation.title,
|
|
72
|
+
providerMetadata: {
|
|
73
|
+
anthropic: {
|
|
74
|
+
citedText: citation.cited_text,
|
|
75
|
+
encryptedIndex: citation.encrypted_index,
|
|
76
|
+
},
|
|
77
|
+
} satisfies SharedV3ProviderMetadata,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (citation.type !== 'page_location' && citation.type !== 'char_location') {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const documentInfo = citationDocuments[citation.document_index];
|
|
86
|
+
|
|
87
|
+
if (!documentInfo) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
type: 'source' as const,
|
|
93
|
+
sourceType: 'document' as const,
|
|
94
|
+
id: generateId(),
|
|
95
|
+
mediaType: documentInfo.mediaType,
|
|
96
|
+
title: citation.document_title ?? documentInfo.title,
|
|
97
|
+
filename: documentInfo.filename,
|
|
98
|
+
providerMetadata: {
|
|
99
|
+
anthropic:
|
|
100
|
+
citation.type === 'page_location'
|
|
101
|
+
? {
|
|
102
|
+
citedText: citation.cited_text,
|
|
103
|
+
startPageNumber: citation.start_page_number,
|
|
104
|
+
endPageNumber: citation.end_page_number,
|
|
105
|
+
}
|
|
106
|
+
: {
|
|
107
|
+
citedText: citation.cited_text,
|
|
108
|
+
startCharIndex: citation.start_char_index,
|
|
109
|
+
endCharIndex: citation.end_char_index,
|
|
110
|
+
},
|
|
111
|
+
} satisfies SharedV3ProviderMetadata,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
type AnthropicMessagesConfig = {
|
|
116
|
+
provider: string;
|
|
117
|
+
baseURL: string;
|
|
118
|
+
headers: Resolvable<Record<string, string | undefined>>;
|
|
119
|
+
fetch?: FetchFunction;
|
|
120
|
+
buildRequestUrl?: (baseURL: string, isStreaming: boolean) => string;
|
|
121
|
+
transformRequestBody?: (
|
|
122
|
+
args: Record<string, any>,
|
|
123
|
+
betas: Set<string>,
|
|
124
|
+
) => Record<string, any>;
|
|
125
|
+
supportedUrls?: () => LanguageModelV3['supportedUrls'];
|
|
126
|
+
generateId?: () => string;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* When false, the model will use JSON tool fallback for structured outputs.
|
|
130
|
+
*/
|
|
131
|
+
supportsNativeStructuredOutput?: boolean;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
|
|
135
|
+
readonly specificationVersion = 'v3';
|
|
136
|
+
|
|
137
|
+
readonly modelId: AnthropicMessagesModelId;
|
|
138
|
+
|
|
139
|
+
private readonly config: AnthropicMessagesConfig;
|
|
140
|
+
private readonly generateId: () => string;
|
|
141
|
+
|
|
142
|
+
constructor(
|
|
143
|
+
modelId: AnthropicMessagesModelId,
|
|
144
|
+
config: AnthropicMessagesConfig,
|
|
145
|
+
) {
|
|
146
|
+
this.modelId = modelId;
|
|
147
|
+
this.config = config;
|
|
148
|
+
this.generateId = config.generateId ?? generateId;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
supportsUrl(url: URL): boolean {
|
|
152
|
+
return url.protocol === 'https:';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get provider(): string {
|
|
156
|
+
return this.config.provider;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Extracts the dynamic provider name from the config.provider string.
|
|
161
|
+
* e.g., 'my-custom-anthropic.messages' -> 'my-custom-anthropic'
|
|
162
|
+
*/
|
|
163
|
+
private get providerOptionsName(): string {
|
|
164
|
+
const provider = this.config.provider;
|
|
165
|
+
const dotIndex = provider.indexOf('.');
|
|
166
|
+
return dotIndex === -1 ? provider : provider.substring(0, dotIndex);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
get supportedUrls() {
|
|
170
|
+
return this.config.supportedUrls?.() ?? {};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private async getArgs({
|
|
174
|
+
userSuppliedBetas,
|
|
175
|
+
prompt,
|
|
176
|
+
maxOutputTokens,
|
|
177
|
+
temperature,
|
|
178
|
+
topP,
|
|
179
|
+
topK,
|
|
180
|
+
frequencyPenalty,
|
|
181
|
+
presencePenalty,
|
|
182
|
+
stopSequences,
|
|
183
|
+
responseFormat,
|
|
184
|
+
seed,
|
|
185
|
+
tools,
|
|
186
|
+
toolChoice,
|
|
187
|
+
providerOptions,
|
|
188
|
+
stream,
|
|
189
|
+
}: LanguageModelV3CallOptions & {
|
|
190
|
+
stream: boolean;
|
|
191
|
+
userSuppliedBetas: Set<string>;
|
|
192
|
+
}) {
|
|
193
|
+
const warnings: SharedV3Warning[] = [];
|
|
194
|
+
|
|
195
|
+
if (frequencyPenalty != null) {
|
|
196
|
+
warnings.push({ type: 'unsupported', feature: 'frequencyPenalty' });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (presencePenalty != null) {
|
|
200
|
+
warnings.push({ type: 'unsupported', feature: 'presencePenalty' });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (seed != null) {
|
|
204
|
+
warnings.push({ type: 'unsupported', feature: 'seed' });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (temperature != null && temperature > 1) {
|
|
208
|
+
warnings.push({
|
|
209
|
+
type: 'unsupported',
|
|
210
|
+
feature: 'temperature',
|
|
211
|
+
details: `${temperature} exceeds anthropic maximum of 1.0. clamped to 1.0`,
|
|
212
|
+
});
|
|
213
|
+
temperature = 1;
|
|
214
|
+
} else if (temperature != null && temperature < 0) {
|
|
215
|
+
warnings.push({
|
|
216
|
+
type: 'unsupported',
|
|
217
|
+
feature: 'temperature',
|
|
218
|
+
details: `${temperature} is below anthropic minimum of 0. clamped to 0`,
|
|
219
|
+
});
|
|
220
|
+
temperature = 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (responseFormat?.type === 'json') {
|
|
224
|
+
if (responseFormat.schema == null) {
|
|
225
|
+
warnings.push({
|
|
226
|
+
type: 'unsupported',
|
|
227
|
+
feature: 'responseFormat',
|
|
228
|
+
details:
|
|
229
|
+
'JSON response format requires a schema. ' +
|
|
230
|
+
'The response format is ignored.',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const providerOptionsName = this.providerOptionsName;
|
|
236
|
+
|
|
237
|
+
// Parse provider options from both canonical 'anthropic' key and custom key
|
|
238
|
+
const canonicalOptions = await parseProviderOptions({
|
|
239
|
+
provider: 'anthropic',
|
|
240
|
+
providerOptions,
|
|
241
|
+
schema: anthropicLanguageModelOptions,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const customProviderOptions =
|
|
245
|
+
providerOptionsName !== 'anthropic'
|
|
246
|
+
? await parseProviderOptions({
|
|
247
|
+
provider: providerOptionsName,
|
|
248
|
+
providerOptions,
|
|
249
|
+
schema: anthropicLanguageModelOptions,
|
|
250
|
+
})
|
|
251
|
+
: null;
|
|
252
|
+
|
|
253
|
+
// Track if custom key was explicitly used
|
|
254
|
+
const usedCustomProviderKey = customProviderOptions != null;
|
|
255
|
+
|
|
256
|
+
// Merge options
|
|
257
|
+
const anthropicOptions = Object.assign(
|
|
258
|
+
{},
|
|
259
|
+
canonicalOptions ?? {},
|
|
260
|
+
customProviderOptions ?? {},
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const {
|
|
264
|
+
maxOutputTokens: maxOutputTokensForModel,
|
|
265
|
+
supportsStructuredOutput: modelSupportsStructuredOutput,
|
|
266
|
+
isKnownModel,
|
|
267
|
+
} = getModelCapabilities(this.modelId);
|
|
268
|
+
|
|
269
|
+
const supportsStructuredOutput =
|
|
270
|
+
(this.config.supportsNativeStructuredOutput ?? true) &&
|
|
271
|
+
modelSupportsStructuredOutput;
|
|
272
|
+
|
|
273
|
+
const structureOutputMode =
|
|
274
|
+
anthropicOptions?.structuredOutputMode ?? 'auto';
|
|
275
|
+
const useStructuredOutput =
|
|
276
|
+
structureOutputMode === 'outputFormat' ||
|
|
277
|
+
(structureOutputMode === 'auto' && supportsStructuredOutput);
|
|
278
|
+
|
|
279
|
+
const jsonResponseTool: LanguageModelV3FunctionTool | undefined =
|
|
280
|
+
responseFormat?.type === 'json' &&
|
|
281
|
+
responseFormat.schema != null &&
|
|
282
|
+
!useStructuredOutput
|
|
283
|
+
? {
|
|
284
|
+
type: 'function',
|
|
285
|
+
name: 'json',
|
|
286
|
+
description: 'Respond with a JSON object.',
|
|
287
|
+
inputSchema: responseFormat.schema,
|
|
288
|
+
}
|
|
289
|
+
: undefined;
|
|
290
|
+
|
|
291
|
+
const contextManagement = anthropicOptions?.contextManagement;
|
|
292
|
+
|
|
293
|
+
// Create a shared cache control validator to track breakpoints across tools and messages
|
|
294
|
+
const cacheControlValidator = new CacheControlValidator();
|
|
295
|
+
|
|
296
|
+
const toolNameMapping = createToolNameMapping({
|
|
297
|
+
tools,
|
|
298
|
+
providerToolNames: {
|
|
299
|
+
'anthropic.code_execution_20250522': 'code_execution',
|
|
300
|
+
'anthropic.code_execution_20250825': 'code_execution',
|
|
301
|
+
'anthropic.code_execution_20260120': 'code_execution',
|
|
302
|
+
'anthropic.computer_20241022': 'computer',
|
|
303
|
+
'anthropic.computer_20250124': 'computer',
|
|
304
|
+
'anthropic.text_editor_20241022': 'str_replace_editor',
|
|
305
|
+
'anthropic.text_editor_20250124': 'str_replace_editor',
|
|
306
|
+
'anthropic.text_editor_20250429': 'str_replace_based_edit_tool',
|
|
307
|
+
'anthropic.text_editor_20250728': 'str_replace_based_edit_tool',
|
|
308
|
+
'anthropic.bash_20241022': 'bash',
|
|
309
|
+
'anthropic.bash_20250124': 'bash',
|
|
310
|
+
'anthropic.memory_20250818': 'memory',
|
|
311
|
+
'anthropic.web_search_20250305': 'web_search',
|
|
312
|
+
'anthropic.web_search_20260209': 'web_search',
|
|
313
|
+
'anthropic.web_fetch_20250910': 'web_fetch',
|
|
314
|
+
'anthropic.web_fetch_20260209': 'web_fetch',
|
|
315
|
+
'anthropic.tool_search_regex_20251119': 'tool_search_tool_regex',
|
|
316
|
+
'anthropic.tool_search_bm25_20251119': 'tool_search_tool_bm25',
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const { prompt: messagesPrompt, betas } =
|
|
321
|
+
await convertToAnthropicMessagesPrompt({
|
|
322
|
+
prompt,
|
|
323
|
+
sendReasoning: anthropicOptions?.sendReasoning ?? true,
|
|
324
|
+
warnings,
|
|
325
|
+
cacheControlValidator,
|
|
326
|
+
toolNameMapping,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Handle assistant prefill: append a partial assistant message
|
|
330
|
+
if (anthropicOptions?.prefill) {
|
|
331
|
+
const lastMessage =
|
|
332
|
+
messagesPrompt.messages[messagesPrompt.messages.length - 1];
|
|
333
|
+
if (lastMessage?.role === 'assistant') {
|
|
334
|
+
// Append to existing assistant message
|
|
335
|
+
(lastMessage.content as any[]).push({
|
|
336
|
+
type: 'text',
|
|
337
|
+
text: anthropicOptions.prefill.trim(),
|
|
338
|
+
cache_control: undefined,
|
|
339
|
+
});
|
|
340
|
+
} else {
|
|
341
|
+
// Add new assistant message
|
|
342
|
+
messagesPrompt.messages.push({
|
|
343
|
+
role: 'assistant',
|
|
344
|
+
content: [
|
|
345
|
+
{
|
|
346
|
+
type: 'text',
|
|
347
|
+
text: anthropicOptions.prefill.trim(),
|
|
348
|
+
cache_control: undefined,
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const thinkingType = anthropicOptions?.thinking?.type;
|
|
356
|
+
const isThinking =
|
|
357
|
+
thinkingType === 'enabled' || thinkingType === 'adaptive';
|
|
358
|
+
let thinkingBudget =
|
|
359
|
+
thinkingType === 'enabled'
|
|
360
|
+
? anthropicOptions?.thinking?.budgetTokens
|
|
361
|
+
: undefined;
|
|
362
|
+
|
|
363
|
+
const maxTokens = maxOutputTokens ?? maxOutputTokensForModel;
|
|
364
|
+
|
|
365
|
+
const baseArgs = {
|
|
366
|
+
// model id:
|
|
367
|
+
model: this.modelId,
|
|
368
|
+
|
|
369
|
+
// standardized settings:
|
|
370
|
+
max_tokens: maxTokens,
|
|
371
|
+
temperature,
|
|
372
|
+
top_k: topK,
|
|
373
|
+
top_p: topP,
|
|
374
|
+
stop_sequences: stopSequences,
|
|
375
|
+
|
|
376
|
+
// provider specific settings:
|
|
377
|
+
...(isThinking && {
|
|
378
|
+
thinking: {
|
|
379
|
+
type: thinkingType,
|
|
380
|
+
...(thinkingBudget != null && { budget_tokens: thinkingBudget }),
|
|
381
|
+
...(anthropicOptions?.thinking?.type !== 'disabled' &&
|
|
382
|
+
anthropicOptions?.thinking?.display && {
|
|
383
|
+
display: anthropicOptions.thinking.display,
|
|
384
|
+
}),
|
|
385
|
+
},
|
|
386
|
+
}),
|
|
387
|
+
...((anthropicOptions?.effort ||
|
|
388
|
+
(useStructuredOutput &&
|
|
389
|
+
responseFormat?.type === 'json' &&
|
|
390
|
+
responseFormat.schema != null)) && {
|
|
391
|
+
output_config: {
|
|
392
|
+
...(anthropicOptions?.effort && {
|
|
393
|
+
effort: anthropicOptions.effort,
|
|
394
|
+
}),
|
|
395
|
+
...(useStructuredOutput &&
|
|
396
|
+
responseFormat?.type === 'json' &&
|
|
397
|
+
responseFormat.schema != null && {
|
|
398
|
+
format: {
|
|
399
|
+
type: 'json_schema',
|
|
400
|
+
schema: responseFormat.schema,
|
|
401
|
+
},
|
|
402
|
+
}),
|
|
403
|
+
},
|
|
404
|
+
}),
|
|
405
|
+
...(anthropicOptions?.speed && {
|
|
406
|
+
speed: anthropicOptions.speed,
|
|
407
|
+
}),
|
|
408
|
+
...(anthropicOptions?.cacheControl && {
|
|
409
|
+
cache_control: anthropicOptions.cacheControl,
|
|
410
|
+
}),
|
|
411
|
+
|
|
412
|
+
// mcp servers:
|
|
413
|
+
...(anthropicOptions?.mcpServers &&
|
|
414
|
+
anthropicOptions.mcpServers.length > 0 && {
|
|
415
|
+
mcp_servers: anthropicOptions.mcpServers.map(server => ({
|
|
416
|
+
type: server.type,
|
|
417
|
+
name: server.name,
|
|
418
|
+
url: server.url,
|
|
419
|
+
authorization_token: server.authorizationToken,
|
|
420
|
+
tool_configuration: server.toolConfiguration
|
|
421
|
+
? {
|
|
422
|
+
allowed_tools: server.toolConfiguration.allowedTools,
|
|
423
|
+
enabled: server.toolConfiguration.enabled,
|
|
424
|
+
}
|
|
425
|
+
: undefined,
|
|
426
|
+
})),
|
|
427
|
+
}),
|
|
428
|
+
|
|
429
|
+
// container: For programmatic tool calling (just an ID string) or agent skills (object with id and skills)
|
|
430
|
+
...(anthropicOptions?.container && {
|
|
431
|
+
container:
|
|
432
|
+
anthropicOptions.container.skills &&
|
|
433
|
+
anthropicOptions.container.skills.length > 0
|
|
434
|
+
? // Object format when skills are provided (agent skills feature)
|
|
435
|
+
({
|
|
436
|
+
id: anthropicOptions.container.id,
|
|
437
|
+
skills: anthropicOptions.container.skills.map(skill => ({
|
|
438
|
+
type: skill.type,
|
|
439
|
+
skill_id: skill.skillId,
|
|
440
|
+
version: skill.version,
|
|
441
|
+
})),
|
|
442
|
+
} satisfies AnthropicContainer)
|
|
443
|
+
: // String format for container ID only (programmatic tool calling)
|
|
444
|
+
anthropicOptions.container.id,
|
|
445
|
+
}),
|
|
446
|
+
|
|
447
|
+
// prompt:
|
|
448
|
+
system: messagesPrompt.system,
|
|
449
|
+
messages: messagesPrompt.messages,
|
|
450
|
+
|
|
451
|
+
...(contextManagement && {
|
|
452
|
+
context_management: {
|
|
453
|
+
edits: contextManagement.edits
|
|
454
|
+
.map(edit => {
|
|
455
|
+
const strategy = edit.type;
|
|
456
|
+
switch (strategy) {
|
|
457
|
+
case 'clear_tool_uses_20250919':
|
|
458
|
+
return {
|
|
459
|
+
type: edit.type,
|
|
460
|
+
...(edit.trigger !== undefined && {
|
|
461
|
+
trigger: edit.trigger,
|
|
462
|
+
}),
|
|
463
|
+
...(edit.keep !== undefined && { keep: edit.keep }),
|
|
464
|
+
...(edit.clearAtLeast !== undefined && {
|
|
465
|
+
clear_at_least: edit.clearAtLeast,
|
|
466
|
+
}),
|
|
467
|
+
...(edit.clearToolInputs !== undefined && {
|
|
468
|
+
clear_tool_inputs: edit.clearToolInputs,
|
|
469
|
+
}),
|
|
470
|
+
...(edit.excludeTools !== undefined && {
|
|
471
|
+
exclude_tools: edit.excludeTools,
|
|
472
|
+
}),
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
case 'clear_thinking_20251015':
|
|
476
|
+
return {
|
|
477
|
+
type: edit.type,
|
|
478
|
+
...(edit.keep !== undefined && { keep: edit.keep }),
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
case 'compact_20260112':
|
|
482
|
+
return {
|
|
483
|
+
type: edit.type,
|
|
484
|
+
...(edit.trigger !== undefined && {
|
|
485
|
+
trigger: edit.trigger,
|
|
486
|
+
}),
|
|
487
|
+
...(edit.pauseAfterCompaction !== undefined && {
|
|
488
|
+
pause_after_compaction: edit.pauseAfterCompaction,
|
|
489
|
+
}),
|
|
490
|
+
...(edit.instructions !== undefined && {
|
|
491
|
+
instructions: edit.instructions,
|
|
492
|
+
}),
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
default:
|
|
496
|
+
warnings.push({
|
|
497
|
+
type: 'other',
|
|
498
|
+
message: `Unknown context management strategy: ${strategy}`,
|
|
499
|
+
});
|
|
500
|
+
return undefined;
|
|
501
|
+
}
|
|
502
|
+
})
|
|
503
|
+
.filter(edit => edit !== undefined),
|
|
504
|
+
},
|
|
505
|
+
}),
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
if (isThinking) {
|
|
509
|
+
if (thinkingType === 'enabled' && thinkingBudget == null) {
|
|
510
|
+
warnings.push({
|
|
511
|
+
type: 'compatibility',
|
|
512
|
+
feature: 'extended thinking',
|
|
513
|
+
details:
|
|
514
|
+
'thinking budget is required when thinking is enabled. using default budget of 1024 tokens.',
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
baseArgs.thinking = {
|
|
518
|
+
type: 'enabled',
|
|
519
|
+
budget_tokens: 1024,
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
thinkingBudget = 1024;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (baseArgs.temperature != null) {
|
|
526
|
+
baseArgs.temperature = undefined;
|
|
527
|
+
warnings.push({
|
|
528
|
+
type: 'unsupported',
|
|
529
|
+
feature: 'temperature',
|
|
530
|
+
details: 'temperature is not supported when thinking is enabled',
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (topK != null) {
|
|
535
|
+
baseArgs.top_k = undefined;
|
|
536
|
+
warnings.push({
|
|
537
|
+
type: 'unsupported',
|
|
538
|
+
feature: 'topK',
|
|
539
|
+
details: 'topK is not supported when thinking is enabled',
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (topP != null) {
|
|
544
|
+
baseArgs.top_p = undefined;
|
|
545
|
+
warnings.push({
|
|
546
|
+
type: 'unsupported',
|
|
547
|
+
feature: 'topP',
|
|
548
|
+
details: 'topP is not supported when thinking is enabled',
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// adjust max tokens to account for thinking:
|
|
553
|
+
baseArgs.max_tokens = maxTokens + (thinkingBudget ?? 0);
|
|
554
|
+
} else {
|
|
555
|
+
// Only check temperature/topP mutual exclusivity when thinking is not enabled
|
|
556
|
+
if (topP != null && temperature != null) {
|
|
557
|
+
warnings.push({
|
|
558
|
+
type: 'unsupported',
|
|
559
|
+
feature: 'topP',
|
|
560
|
+
details: `topP is not supported when temperature is set. topP is ignored.`,
|
|
561
|
+
});
|
|
562
|
+
baseArgs.top_p = undefined;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// limit to max output tokens for known models to enable model switching without breaking it:
|
|
567
|
+
if (isKnownModel && baseArgs.max_tokens > maxOutputTokensForModel) {
|
|
568
|
+
// only warn if max output tokens is provided as input:
|
|
569
|
+
if (maxOutputTokens != null) {
|
|
570
|
+
warnings.push({
|
|
571
|
+
type: 'unsupported',
|
|
572
|
+
feature: 'maxOutputTokens',
|
|
573
|
+
details:
|
|
574
|
+
`${baseArgs.max_tokens} (maxOutputTokens + thinkingBudget) is greater than ${this.modelId} ${maxOutputTokensForModel} max output tokens. ` +
|
|
575
|
+
`The max output tokens have been limited to ${maxOutputTokensForModel}.`,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
baseArgs.max_tokens = maxOutputTokensForModel;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (
|
|
582
|
+
anthropicOptions?.mcpServers &&
|
|
583
|
+
anthropicOptions.mcpServers.length > 0
|
|
584
|
+
) {
|
|
585
|
+
betas.add('mcp-client-2025-04-04');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (contextManagement) {
|
|
589
|
+
betas.add('context-management-2025-06-27');
|
|
590
|
+
|
|
591
|
+
// Add compaction beta if compact edit is present
|
|
592
|
+
if (contextManagement.edits.some(e => e.type === 'compact_20260112')) {
|
|
593
|
+
betas.add('compact-2026-01-12');
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (
|
|
598
|
+
anthropicOptions?.container &&
|
|
599
|
+
anthropicOptions.container.skills &&
|
|
600
|
+
anthropicOptions.container.skills.length > 0
|
|
601
|
+
) {
|
|
602
|
+
betas.add('code-execution-2025-08-25');
|
|
603
|
+
betas.add('skills-2025-10-02');
|
|
604
|
+
betas.add('files-api-2025-04-14');
|
|
605
|
+
|
|
606
|
+
if (
|
|
607
|
+
!tools?.some(
|
|
608
|
+
tool =>
|
|
609
|
+
tool.type === 'provider' &&
|
|
610
|
+
(tool.id === 'anthropic.code_execution_20250825' ||
|
|
611
|
+
tool.id === 'anthropic.code_execution_20260120'),
|
|
612
|
+
)
|
|
613
|
+
) {
|
|
614
|
+
warnings.push({
|
|
615
|
+
type: 'other',
|
|
616
|
+
message: 'code execution tool is required when using skills',
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (anthropicOptions?.effort) {
|
|
622
|
+
betas.add('effort-2025-11-24');
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (anthropicOptions?.speed === 'fast') {
|
|
626
|
+
betas.add('fast-mode-2026-02-01');
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// only when streaming: enable fine-grained tool streaming
|
|
630
|
+
if (stream && (anthropicOptions?.toolStreaming ?? true)) {
|
|
631
|
+
betas.add('fine-grained-tool-streaming-2025-05-14');
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const {
|
|
635
|
+
tools: anthropicTools,
|
|
636
|
+
toolChoice: anthropicToolChoice,
|
|
637
|
+
toolWarnings,
|
|
638
|
+
betas: toolsBetas,
|
|
639
|
+
} = await prepareTools(
|
|
640
|
+
jsonResponseTool != null
|
|
641
|
+
? {
|
|
642
|
+
tools: [...(tools ?? []), jsonResponseTool],
|
|
643
|
+
toolChoice: { type: 'required' },
|
|
644
|
+
disableParallelToolUse: true,
|
|
645
|
+
cacheControlValidator,
|
|
646
|
+
supportsStructuredOutput: false,
|
|
647
|
+
}
|
|
648
|
+
: {
|
|
649
|
+
tools: tools ?? [],
|
|
650
|
+
toolChoice,
|
|
651
|
+
disableParallelToolUse: anthropicOptions?.disableParallelToolUse,
|
|
652
|
+
cacheControlValidator,
|
|
653
|
+
supportsStructuredOutput,
|
|
654
|
+
},
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
// Extract cache control warnings once at the end
|
|
658
|
+
const cacheWarnings = cacheControlValidator.getWarnings();
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
args: {
|
|
662
|
+
...baseArgs,
|
|
663
|
+
tools: anthropicTools,
|
|
664
|
+
tool_choice: anthropicToolChoice,
|
|
665
|
+
stream: stream === true ? true : undefined, // do not send when not streaming
|
|
666
|
+
},
|
|
667
|
+
warnings: [...warnings, ...toolWarnings, ...cacheWarnings],
|
|
668
|
+
betas: new Set([
|
|
669
|
+
...betas,
|
|
670
|
+
...toolsBetas,
|
|
671
|
+
...userSuppliedBetas,
|
|
672
|
+
...(anthropicOptions?.anthropicBeta ?? []),
|
|
673
|
+
]),
|
|
674
|
+
usesJsonResponseTool: jsonResponseTool != null,
|
|
675
|
+
toolNameMapping,
|
|
676
|
+
providerOptionsName,
|
|
677
|
+
usedCustomProviderKey,
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
private async getHeaders({
|
|
682
|
+
betas,
|
|
683
|
+
headers,
|
|
684
|
+
}: {
|
|
685
|
+
betas: Set<string>;
|
|
686
|
+
headers: Record<string, string | undefined> | undefined;
|
|
687
|
+
}) {
|
|
688
|
+
return combineHeaders(
|
|
689
|
+
await resolve(this.config.headers),
|
|
690
|
+
headers,
|
|
691
|
+
betas.size > 0 ? { 'anthropic-beta': Array.from(betas).join(',') } : {},
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
private async getBetasFromHeaders(
|
|
696
|
+
requestHeaders: Record<string, string | undefined> | undefined,
|
|
697
|
+
) {
|
|
698
|
+
const configHeaders = await resolve(this.config.headers);
|
|
699
|
+
|
|
700
|
+
const configBetaHeader = configHeaders['anthropic-beta'] ?? '';
|
|
701
|
+
const requestBetaHeader = requestHeaders?.['anthropic-beta'] ?? '';
|
|
702
|
+
|
|
703
|
+
return new Set(
|
|
704
|
+
[
|
|
705
|
+
...configBetaHeader.toLowerCase().split(','),
|
|
706
|
+
...requestBetaHeader.toLowerCase().split(','),
|
|
707
|
+
]
|
|
708
|
+
.map(beta => beta.trim())
|
|
709
|
+
.filter(beta => beta !== ''),
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
private buildRequestUrl(isStreaming: boolean): string {
|
|
714
|
+
return (
|
|
715
|
+
this.config.buildRequestUrl?.(this.config.baseURL, isStreaming) ??
|
|
716
|
+
`${this.config.baseURL}/messages`
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
private transformRequestBody(
|
|
721
|
+
args: Record<string, any>,
|
|
722
|
+
betas: Set<string>,
|
|
723
|
+
): Record<string, any> {
|
|
724
|
+
return this.config.transformRequestBody?.(args, betas) ?? args;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
private extractCitationDocuments(prompt: LanguageModelV3Prompt): Array<{
|
|
728
|
+
title: string;
|
|
729
|
+
filename?: string;
|
|
730
|
+
mediaType: string;
|
|
731
|
+
}> {
|
|
732
|
+
const isCitationPart = (part: {
|
|
733
|
+
type: string;
|
|
734
|
+
mediaType?: string;
|
|
735
|
+
providerOptions?: { anthropic?: { citations?: { enabled?: boolean } } };
|
|
736
|
+
}) => {
|
|
737
|
+
if (part.type !== 'file') {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (
|
|
742
|
+
part.mediaType !== 'application/pdf' &&
|
|
743
|
+
part.mediaType !== 'text/plain'
|
|
744
|
+
) {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const anthropic = part.providerOptions?.anthropic;
|
|
749
|
+
const citationsConfig = anthropic?.citations as
|
|
750
|
+
| { enabled?: boolean }
|
|
751
|
+
| undefined;
|
|
752
|
+
return citationsConfig?.enabled ?? false;
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
return prompt
|
|
756
|
+
.filter(message => message.role === 'user')
|
|
757
|
+
.flatMap(message => message.content)
|
|
758
|
+
.filter(isCitationPart)
|
|
759
|
+
.map(part => {
|
|
760
|
+
// TypeScript knows this is a file part due to our filter
|
|
761
|
+
const filePart = part as Extract<typeof part, { type: 'file' }>;
|
|
762
|
+
return {
|
|
763
|
+
title: filePart.filename ?? 'Untitled Document',
|
|
764
|
+
filename: filePart.filename,
|
|
765
|
+
mediaType: filePart.mediaType,
|
|
766
|
+
};
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
async doGenerate(
|
|
771
|
+
options: LanguageModelV3CallOptions,
|
|
772
|
+
): Promise<LanguageModelV3GenerateResult> {
|
|
773
|
+
const {
|
|
774
|
+
args,
|
|
775
|
+
warnings,
|
|
776
|
+
betas,
|
|
777
|
+
usesJsonResponseTool,
|
|
778
|
+
toolNameMapping,
|
|
779
|
+
providerOptionsName,
|
|
780
|
+
usedCustomProviderKey,
|
|
781
|
+
} = await this.getArgs({
|
|
782
|
+
...options,
|
|
783
|
+
stream: false,
|
|
784
|
+
userSuppliedBetas: await this.getBetasFromHeaders(options.headers),
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// Extract citation documents for response processing
|
|
788
|
+
const citationDocuments = [
|
|
789
|
+
...this.extractCitationDocuments(options.prompt),
|
|
790
|
+
];
|
|
791
|
+
|
|
792
|
+
const markCodeExecutionDynamic = hasWebTool20260209WithoutCodeExecution(
|
|
793
|
+
args.tools,
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
const {
|
|
797
|
+
responseHeaders,
|
|
798
|
+
value: response,
|
|
799
|
+
rawValue: rawResponse,
|
|
800
|
+
} = await postJsonToApi({
|
|
801
|
+
url: this.buildRequestUrl(false),
|
|
802
|
+
headers: await this.getHeaders({ betas, headers: options.headers }),
|
|
803
|
+
body: this.transformRequestBody(args, betas),
|
|
804
|
+
failedResponseHandler: anthropicFailedResponseHandler,
|
|
805
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
806
|
+
anthropicMessagesResponseSchema,
|
|
807
|
+
),
|
|
808
|
+
abortSignal: options.abortSignal,
|
|
809
|
+
fetch: this.config.fetch,
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
const content: Array<LanguageModelV3Content> = [];
|
|
813
|
+
const mcpToolCalls: Record<string, LanguageModelV3ToolCall> = {};
|
|
814
|
+
const serverToolCalls: Record<string, string> = {}; // tool_use_id -> provider tool name
|
|
815
|
+
let isJsonResponseFromTool = false;
|
|
816
|
+
|
|
817
|
+
// map response content to content array
|
|
818
|
+
for (const part of response.content) {
|
|
819
|
+
switch (part.type) {
|
|
820
|
+
case 'text': {
|
|
821
|
+
if (!usesJsonResponseTool) {
|
|
822
|
+
content.push({ type: 'text', text: part.text });
|
|
823
|
+
|
|
824
|
+
// Process citations if present
|
|
825
|
+
if (part.citations) {
|
|
826
|
+
for (const citation of part.citations) {
|
|
827
|
+
const source = createCitationSource(
|
|
828
|
+
citation,
|
|
829
|
+
citationDocuments,
|
|
830
|
+
this.generateId,
|
|
831
|
+
);
|
|
832
|
+
|
|
833
|
+
if (source) {
|
|
834
|
+
content.push(source);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
break;
|
|
840
|
+
}
|
|
841
|
+
case 'thinking': {
|
|
842
|
+
content.push({
|
|
843
|
+
type: 'reasoning',
|
|
844
|
+
text: part.thinking,
|
|
845
|
+
providerMetadata: {
|
|
846
|
+
anthropic: {
|
|
847
|
+
signature: part.signature,
|
|
848
|
+
} satisfies AnthropicReasoningMetadata,
|
|
849
|
+
},
|
|
850
|
+
});
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
case 'redacted_thinking': {
|
|
854
|
+
content.push({
|
|
855
|
+
type: 'reasoning',
|
|
856
|
+
text: '',
|
|
857
|
+
providerMetadata: {
|
|
858
|
+
anthropic: {
|
|
859
|
+
redactedData: part.data,
|
|
860
|
+
} satisfies AnthropicReasoningMetadata,
|
|
861
|
+
},
|
|
862
|
+
});
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
865
|
+
case 'compaction': {
|
|
866
|
+
content.push({
|
|
867
|
+
type: 'text',
|
|
868
|
+
text: part.content,
|
|
869
|
+
providerMetadata: {
|
|
870
|
+
anthropic: {
|
|
871
|
+
type: 'compaction',
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
case 'tool_use': {
|
|
878
|
+
const isJsonResponseTool =
|
|
879
|
+
usesJsonResponseTool && part.name === 'json';
|
|
880
|
+
|
|
881
|
+
if (isJsonResponseTool) {
|
|
882
|
+
isJsonResponseFromTool = true;
|
|
883
|
+
|
|
884
|
+
// when a json response tool is used, the tool call becomes the text:
|
|
885
|
+
content.push({
|
|
886
|
+
type: 'text',
|
|
887
|
+
text: JSON.stringify(part.input),
|
|
888
|
+
});
|
|
889
|
+
} else {
|
|
890
|
+
const caller = part.caller;
|
|
891
|
+
const callerInfo = caller
|
|
892
|
+
? {
|
|
893
|
+
type: caller.type,
|
|
894
|
+
toolId: 'tool_id' in caller ? caller.tool_id : undefined,
|
|
895
|
+
}
|
|
896
|
+
: undefined;
|
|
897
|
+
|
|
898
|
+
content.push({
|
|
899
|
+
type: 'tool-call',
|
|
900
|
+
toolCallId: part.id,
|
|
901
|
+
toolName: part.name,
|
|
902
|
+
input: JSON.stringify(part.input),
|
|
903
|
+
...(callerInfo && {
|
|
904
|
+
providerMetadata: {
|
|
905
|
+
anthropic: {
|
|
906
|
+
caller: callerInfo,
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
}),
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
break;
|
|
914
|
+
}
|
|
915
|
+
case 'server_tool_use': {
|
|
916
|
+
// code execution 20250825 needs mapping:
|
|
917
|
+
if (
|
|
918
|
+
part.name === 'text_editor_code_execution' ||
|
|
919
|
+
part.name === 'bash_code_execution'
|
|
920
|
+
) {
|
|
921
|
+
content.push({
|
|
922
|
+
type: 'tool-call',
|
|
923
|
+
toolCallId: part.id,
|
|
924
|
+
toolName: toolNameMapping.toCustomToolName('code_execution'),
|
|
925
|
+
input: JSON.stringify({ type: part.name, ...part.input }),
|
|
926
|
+
providerExecuted: true,
|
|
927
|
+
});
|
|
928
|
+
} else if (
|
|
929
|
+
part.name === 'web_search' ||
|
|
930
|
+
part.name === 'code_execution' ||
|
|
931
|
+
part.name === 'web_fetch'
|
|
932
|
+
) {
|
|
933
|
+
// For code_execution, inject 'programmatic-tool-call' type when input has { code } format
|
|
934
|
+
const inputToSerialize =
|
|
935
|
+
part.name === 'code_execution' &&
|
|
936
|
+
part.input != null &&
|
|
937
|
+
typeof part.input === 'object' &&
|
|
938
|
+
'code' in part.input &&
|
|
939
|
+
!('type' in part.input)
|
|
940
|
+
? { type: 'programmatic-tool-call', ...part.input }
|
|
941
|
+
: part.input;
|
|
942
|
+
|
|
943
|
+
content.push({
|
|
944
|
+
type: 'tool-call',
|
|
945
|
+
toolCallId: part.id,
|
|
946
|
+
toolName: toolNameMapping.toCustomToolName(part.name),
|
|
947
|
+
input: JSON.stringify(inputToSerialize),
|
|
948
|
+
providerExecuted: true,
|
|
949
|
+
// We want this 'code_execution' tool call to be allowed even if the tool is not explicitly provided.
|
|
950
|
+
// Since the validation generally bypasses dynamic tools, we mark this specific tool as dynamic.
|
|
951
|
+
...(markCodeExecutionDynamic && part.name === 'code_execution'
|
|
952
|
+
? { dynamic: true }
|
|
953
|
+
: {}),
|
|
954
|
+
});
|
|
955
|
+
} else if (
|
|
956
|
+
part.name === 'tool_search_tool_regex' ||
|
|
957
|
+
part.name === 'tool_search_tool_bm25'
|
|
958
|
+
) {
|
|
959
|
+
serverToolCalls[part.id] = part.name;
|
|
960
|
+
content.push({
|
|
961
|
+
type: 'tool-call',
|
|
962
|
+
toolCallId: part.id,
|
|
963
|
+
toolName: toolNameMapping.toCustomToolName(part.name),
|
|
964
|
+
input: JSON.stringify(part.input),
|
|
965
|
+
providerExecuted: true,
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
break;
|
|
970
|
+
}
|
|
971
|
+
case 'mcp_tool_use': {
|
|
972
|
+
mcpToolCalls[part.id] = {
|
|
973
|
+
type: 'tool-call',
|
|
974
|
+
toolCallId: part.id,
|
|
975
|
+
toolName: part.name,
|
|
976
|
+
input: JSON.stringify(part.input),
|
|
977
|
+
providerExecuted: true,
|
|
978
|
+
dynamic: true,
|
|
979
|
+
providerMetadata: {
|
|
980
|
+
anthropic: {
|
|
981
|
+
type: 'mcp-tool-use',
|
|
982
|
+
serverName: part.server_name,
|
|
983
|
+
},
|
|
984
|
+
},
|
|
985
|
+
};
|
|
986
|
+
content.push(mcpToolCalls[part.id]);
|
|
987
|
+
break;
|
|
988
|
+
}
|
|
989
|
+
case 'mcp_tool_result': {
|
|
990
|
+
content.push({
|
|
991
|
+
type: 'tool-result',
|
|
992
|
+
toolCallId: part.tool_use_id,
|
|
993
|
+
toolName: mcpToolCalls[part.tool_use_id].toolName,
|
|
994
|
+
isError: part.is_error,
|
|
995
|
+
result: part.content,
|
|
996
|
+
dynamic: true,
|
|
997
|
+
providerMetadata: mcpToolCalls[part.tool_use_id].providerMetadata,
|
|
998
|
+
});
|
|
999
|
+
break;
|
|
1000
|
+
}
|
|
1001
|
+
case 'web_fetch_tool_result': {
|
|
1002
|
+
if (part.content.type === 'web_fetch_result') {
|
|
1003
|
+
citationDocuments.push({
|
|
1004
|
+
title: part.content.content.title ?? part.content.url,
|
|
1005
|
+
mediaType: part.content.content.source.media_type,
|
|
1006
|
+
});
|
|
1007
|
+
content.push({
|
|
1008
|
+
type: 'tool-result',
|
|
1009
|
+
toolCallId: part.tool_use_id,
|
|
1010
|
+
toolName: toolNameMapping.toCustomToolName('web_fetch'),
|
|
1011
|
+
result: {
|
|
1012
|
+
type: 'web_fetch_result',
|
|
1013
|
+
url: part.content.url,
|
|
1014
|
+
retrievedAt: part.content.retrieved_at,
|
|
1015
|
+
content: {
|
|
1016
|
+
type: part.content.content.type,
|
|
1017
|
+
title: part.content.content.title,
|
|
1018
|
+
citations: part.content.content.citations,
|
|
1019
|
+
source: {
|
|
1020
|
+
type: part.content.content.source.type,
|
|
1021
|
+
mediaType: part.content.content.source.media_type,
|
|
1022
|
+
data: part.content.content.source.data,
|
|
1023
|
+
},
|
|
1024
|
+
},
|
|
1025
|
+
},
|
|
1026
|
+
});
|
|
1027
|
+
} else if (part.content.type === 'web_fetch_tool_result_error') {
|
|
1028
|
+
content.push({
|
|
1029
|
+
type: 'tool-result',
|
|
1030
|
+
toolCallId: part.tool_use_id,
|
|
1031
|
+
toolName: toolNameMapping.toCustomToolName('web_fetch'),
|
|
1032
|
+
isError: true,
|
|
1033
|
+
result: {
|
|
1034
|
+
type: 'web_fetch_tool_result_error',
|
|
1035
|
+
errorCode: part.content.error_code,
|
|
1036
|
+
},
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
break;
|
|
1040
|
+
}
|
|
1041
|
+
case 'web_search_tool_result': {
|
|
1042
|
+
if (Array.isArray(part.content)) {
|
|
1043
|
+
content.push({
|
|
1044
|
+
type: 'tool-result',
|
|
1045
|
+
toolCallId: part.tool_use_id,
|
|
1046
|
+
toolName: toolNameMapping.toCustomToolName('web_search'),
|
|
1047
|
+
result: part.content.map(result => ({
|
|
1048
|
+
url: result.url,
|
|
1049
|
+
title: result.title,
|
|
1050
|
+
pageAge: result.page_age ?? null,
|
|
1051
|
+
encryptedContent: result.encrypted_content,
|
|
1052
|
+
type: result.type,
|
|
1053
|
+
})),
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
for (const result of part.content) {
|
|
1057
|
+
content.push({
|
|
1058
|
+
type: 'source',
|
|
1059
|
+
sourceType: 'url',
|
|
1060
|
+
id: this.generateId(),
|
|
1061
|
+
url: result.url,
|
|
1062
|
+
title: result.title,
|
|
1063
|
+
providerMetadata: {
|
|
1064
|
+
anthropic: {
|
|
1065
|
+
pageAge: result.page_age ?? null,
|
|
1066
|
+
},
|
|
1067
|
+
},
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
} else {
|
|
1071
|
+
content.push({
|
|
1072
|
+
type: 'tool-result',
|
|
1073
|
+
toolCallId: part.tool_use_id,
|
|
1074
|
+
toolName: toolNameMapping.toCustomToolName('web_search'),
|
|
1075
|
+
isError: true,
|
|
1076
|
+
result: {
|
|
1077
|
+
type: 'web_search_tool_result_error',
|
|
1078
|
+
errorCode: part.content.error_code,
|
|
1079
|
+
},
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// code execution 20250522:
|
|
1086
|
+
case 'code_execution_tool_result': {
|
|
1087
|
+
if (part.content.type === 'code_execution_result') {
|
|
1088
|
+
content.push({
|
|
1089
|
+
type: 'tool-result',
|
|
1090
|
+
toolCallId: part.tool_use_id,
|
|
1091
|
+
toolName: toolNameMapping.toCustomToolName('code_execution'),
|
|
1092
|
+
result: {
|
|
1093
|
+
type: part.content.type,
|
|
1094
|
+
stdout: part.content.stdout,
|
|
1095
|
+
stderr: part.content.stderr,
|
|
1096
|
+
return_code: part.content.return_code,
|
|
1097
|
+
content: part.content.content ?? [],
|
|
1098
|
+
},
|
|
1099
|
+
});
|
|
1100
|
+
} else if (part.content.type === 'encrypted_code_execution_result') {
|
|
1101
|
+
content.push({
|
|
1102
|
+
type: 'tool-result',
|
|
1103
|
+
toolCallId: part.tool_use_id,
|
|
1104
|
+
toolName: toolNameMapping.toCustomToolName('code_execution'),
|
|
1105
|
+
result: {
|
|
1106
|
+
type: part.content.type,
|
|
1107
|
+
encrypted_stdout: part.content.encrypted_stdout,
|
|
1108
|
+
stderr: part.content.stderr,
|
|
1109
|
+
return_code: part.content.return_code,
|
|
1110
|
+
content: part.content.content ?? [],
|
|
1111
|
+
},
|
|
1112
|
+
});
|
|
1113
|
+
} else if (part.content.type === 'code_execution_tool_result_error') {
|
|
1114
|
+
content.push({
|
|
1115
|
+
type: 'tool-result',
|
|
1116
|
+
toolCallId: part.tool_use_id,
|
|
1117
|
+
toolName: toolNameMapping.toCustomToolName('code_execution'),
|
|
1118
|
+
isError: true,
|
|
1119
|
+
result: {
|
|
1120
|
+
type: 'code_execution_tool_result_error',
|
|
1121
|
+
errorCode: part.content.error_code,
|
|
1122
|
+
},
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
break;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// code execution 20250825:
|
|
1129
|
+
case 'bash_code_execution_tool_result':
|
|
1130
|
+
case 'text_editor_code_execution_tool_result': {
|
|
1131
|
+
content.push({
|
|
1132
|
+
type: 'tool-result',
|
|
1133
|
+
toolCallId: part.tool_use_id,
|
|
1134
|
+
toolName: toolNameMapping.toCustomToolName('code_execution'),
|
|
1135
|
+
result: part.content,
|
|
1136
|
+
});
|
|
1137
|
+
break;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// tool search tool results:
|
|
1141
|
+
case 'tool_search_tool_result': {
|
|
1142
|
+
let providerToolName = serverToolCalls[part.tool_use_id];
|
|
1143
|
+
|
|
1144
|
+
if (providerToolName == null) {
|
|
1145
|
+
const bm25CustomName = toolNameMapping.toCustomToolName(
|
|
1146
|
+
'tool_search_tool_bm25',
|
|
1147
|
+
);
|
|
1148
|
+
const regexCustomName = toolNameMapping.toCustomToolName(
|
|
1149
|
+
'tool_search_tool_regex',
|
|
1150
|
+
);
|
|
1151
|
+
|
|
1152
|
+
if (bm25CustomName !== 'tool_search_tool_bm25') {
|
|
1153
|
+
providerToolName = 'tool_search_tool_bm25';
|
|
1154
|
+
} else if (regexCustomName !== 'tool_search_tool_regex') {
|
|
1155
|
+
providerToolName = 'tool_search_tool_regex';
|
|
1156
|
+
} else {
|
|
1157
|
+
providerToolName = 'tool_search_tool_regex';
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if (part.content.type === 'tool_search_tool_search_result') {
|
|
1162
|
+
content.push({
|
|
1163
|
+
type: 'tool-result',
|
|
1164
|
+
toolCallId: part.tool_use_id,
|
|
1165
|
+
toolName: toolNameMapping.toCustomToolName(providerToolName),
|
|
1166
|
+
result: part.content.tool_references.map(ref => ({
|
|
1167
|
+
type: ref.type,
|
|
1168
|
+
toolName: ref.tool_name,
|
|
1169
|
+
})),
|
|
1170
|
+
});
|
|
1171
|
+
} else {
|
|
1172
|
+
content.push({
|
|
1173
|
+
type: 'tool-result',
|
|
1174
|
+
toolCallId: part.tool_use_id,
|
|
1175
|
+
toolName: toolNameMapping.toCustomToolName(providerToolName),
|
|
1176
|
+
isError: true,
|
|
1177
|
+
result: {
|
|
1178
|
+
type: 'tool_search_tool_result_error',
|
|
1179
|
+
errorCode: part.content.error_code,
|
|
1180
|
+
},
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
break;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
return {
|
|
1189
|
+
content,
|
|
1190
|
+
finishReason: {
|
|
1191
|
+
unified: mapAnthropicStopReason({
|
|
1192
|
+
finishReason: response.stop_reason,
|
|
1193
|
+
isJsonResponseFromTool,
|
|
1194
|
+
}),
|
|
1195
|
+
raw: response.stop_reason ?? undefined,
|
|
1196
|
+
},
|
|
1197
|
+
usage: convertAnthropicMessagesUsage({ usage: response.usage }),
|
|
1198
|
+
request: { body: args },
|
|
1199
|
+
response: {
|
|
1200
|
+
id: response.id ?? undefined,
|
|
1201
|
+
modelId: response.model ?? undefined,
|
|
1202
|
+
headers: responseHeaders,
|
|
1203
|
+
body: rawResponse,
|
|
1204
|
+
},
|
|
1205
|
+
warnings,
|
|
1206
|
+
providerMetadata: (() => {
|
|
1207
|
+
const anthropicMetadata = {
|
|
1208
|
+
usage: response.usage as JSONObject,
|
|
1209
|
+
cacheCreationInputTokens:
|
|
1210
|
+
response.usage.cache_creation_input_tokens ?? null,
|
|
1211
|
+
stopSequence: response.stop_sequence ?? null,
|
|
1212
|
+
|
|
1213
|
+
iterations: response.usage.iterations
|
|
1214
|
+
? response.usage.iterations.map(iter => ({
|
|
1215
|
+
type: iter.type,
|
|
1216
|
+
inputTokens: iter.input_tokens,
|
|
1217
|
+
outputTokens: iter.output_tokens,
|
|
1218
|
+
}))
|
|
1219
|
+
: null,
|
|
1220
|
+
container: response.container
|
|
1221
|
+
? {
|
|
1222
|
+
expiresAt: response.container.expires_at,
|
|
1223
|
+
id: response.container.id,
|
|
1224
|
+
skills:
|
|
1225
|
+
response.container.skills?.map(skill => ({
|
|
1226
|
+
type: skill.type,
|
|
1227
|
+
skillId: skill.skill_id,
|
|
1228
|
+
version: skill.version,
|
|
1229
|
+
})) ?? null,
|
|
1230
|
+
}
|
|
1231
|
+
: null,
|
|
1232
|
+
contextManagement:
|
|
1233
|
+
mapAnthropicResponseContextManagement(
|
|
1234
|
+
response.context_management,
|
|
1235
|
+
) ?? null,
|
|
1236
|
+
} satisfies AnthropicMessageMetadata;
|
|
1237
|
+
|
|
1238
|
+
const providerMetadata: SharedV3ProviderMetadata = {
|
|
1239
|
+
anthropic: anthropicMetadata,
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
if (usedCustomProviderKey && providerOptionsName !== 'anthropic') {
|
|
1243
|
+
providerMetadata[providerOptionsName] = anthropicMetadata;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
return providerMetadata;
|
|
1247
|
+
})(),
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
async doStream(
|
|
1252
|
+
options: LanguageModelV3CallOptions,
|
|
1253
|
+
): Promise<LanguageModelV3StreamResult> {
|
|
1254
|
+
const {
|
|
1255
|
+
args: body,
|
|
1256
|
+
warnings,
|
|
1257
|
+
betas,
|
|
1258
|
+
usesJsonResponseTool,
|
|
1259
|
+
toolNameMapping,
|
|
1260
|
+
providerOptionsName,
|
|
1261
|
+
usedCustomProviderKey,
|
|
1262
|
+
} = await this.getArgs({
|
|
1263
|
+
...options,
|
|
1264
|
+
stream: true,
|
|
1265
|
+
userSuppliedBetas: await this.getBetasFromHeaders(options.headers),
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
// Extract citation documents for response processing
|
|
1269
|
+
const citationDocuments = [
|
|
1270
|
+
...this.extractCitationDocuments(options.prompt),
|
|
1271
|
+
];
|
|
1272
|
+
|
|
1273
|
+
const markCodeExecutionDynamic = hasWebTool20260209WithoutCodeExecution(
|
|
1274
|
+
body.tools,
|
|
1275
|
+
);
|
|
1276
|
+
|
|
1277
|
+
const url = this.buildRequestUrl(true);
|
|
1278
|
+
const { responseHeaders, value: response } = await postJsonToApi({
|
|
1279
|
+
url,
|
|
1280
|
+
headers: await this.getHeaders({ betas, headers: options.headers }),
|
|
1281
|
+
body: this.transformRequestBody(body, betas),
|
|
1282
|
+
failedResponseHandler: anthropicFailedResponseHandler,
|
|
1283
|
+
successfulResponseHandler: createEventSourceResponseHandler(
|
|
1284
|
+
anthropicMessagesChunkSchema,
|
|
1285
|
+
),
|
|
1286
|
+
abortSignal: options.abortSignal,
|
|
1287
|
+
fetch: this.config.fetch,
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
let finishReason: LanguageModelV3FinishReason = {
|
|
1291
|
+
unified: 'other',
|
|
1292
|
+
raw: undefined,
|
|
1293
|
+
};
|
|
1294
|
+
const usage: AnthropicMessagesUsage = {
|
|
1295
|
+
input_tokens: 0,
|
|
1296
|
+
output_tokens: 0,
|
|
1297
|
+
cache_creation_input_tokens: 0,
|
|
1298
|
+
cache_read_input_tokens: 0,
|
|
1299
|
+
iterations: null,
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
const contentBlocks: Record<
|
|
1303
|
+
number,
|
|
1304
|
+
| {
|
|
1305
|
+
type: 'tool-call';
|
|
1306
|
+
toolCallId: string;
|
|
1307
|
+
toolName: string;
|
|
1308
|
+
input: string;
|
|
1309
|
+
providerExecuted?: boolean;
|
|
1310
|
+
firstDelta: boolean;
|
|
1311
|
+
providerToolName?: string;
|
|
1312
|
+
caller?: {
|
|
1313
|
+
type:
|
|
1314
|
+
| 'code_execution_20250825'
|
|
1315
|
+
| 'code_execution_20260120'
|
|
1316
|
+
| 'direct';
|
|
1317
|
+
toolId?: string;
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
| { type: 'text' | 'reasoning' }
|
|
1321
|
+
> = {};
|
|
1322
|
+
const mcpToolCalls: Record<string, LanguageModelV3ToolCall> = {};
|
|
1323
|
+
const serverToolCalls: Record<string, string> = {}; // tool_use_id -> provider tool name
|
|
1324
|
+
|
|
1325
|
+
let contextManagement:
|
|
1326
|
+
| AnthropicMessageMetadata['contextManagement']
|
|
1327
|
+
| null = null;
|
|
1328
|
+
let rawUsage: JSONObject | undefined = undefined;
|
|
1329
|
+
let cacheCreationInputTokens: number | null = null;
|
|
1330
|
+
let stopSequence: string | null = null;
|
|
1331
|
+
let container: AnthropicMessageMetadata['container'] | null = null;
|
|
1332
|
+
let isJsonResponseFromTool = false;
|
|
1333
|
+
|
|
1334
|
+
let blockType:
|
|
1335
|
+
| 'text'
|
|
1336
|
+
| 'thinking'
|
|
1337
|
+
| 'tool_use'
|
|
1338
|
+
| 'redacted_thinking'
|
|
1339
|
+
| 'server_tool_use'
|
|
1340
|
+
| 'web_fetch_tool_result'
|
|
1341
|
+
| 'web_search_tool_result'
|
|
1342
|
+
| 'code_execution_tool_result'
|
|
1343
|
+
| 'text_editor_code_execution_tool_result'
|
|
1344
|
+
| 'bash_code_execution_tool_result'
|
|
1345
|
+
| 'tool_search_tool_result'
|
|
1346
|
+
| 'mcp_tool_use'
|
|
1347
|
+
| 'mcp_tool_result'
|
|
1348
|
+
| 'compaction'
|
|
1349
|
+
| undefined = undefined;
|
|
1350
|
+
|
|
1351
|
+
const generateId = this.generateId;
|
|
1352
|
+
|
|
1353
|
+
const transformedStream = response.pipeThrough(
|
|
1354
|
+
new TransformStream<
|
|
1355
|
+
ParseResult<InferSchema<typeof anthropicMessagesChunkSchema>>,
|
|
1356
|
+
LanguageModelV3StreamPart
|
|
1357
|
+
>({
|
|
1358
|
+
start(controller) {
|
|
1359
|
+
controller.enqueue({ type: 'stream-start', warnings });
|
|
1360
|
+
},
|
|
1361
|
+
|
|
1362
|
+
transform(chunk, controller) {
|
|
1363
|
+
if (options.includeRawChunks) {
|
|
1364
|
+
controller.enqueue({ type: 'raw', rawValue: chunk.rawValue });
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
if (!chunk.success) {
|
|
1368
|
+
controller.enqueue({ type: 'error', error: chunk.error });
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
const value = chunk.value;
|
|
1373
|
+
|
|
1374
|
+
switch (value.type) {
|
|
1375
|
+
case 'ping': {
|
|
1376
|
+
return; // ignored
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
case 'content_block_start': {
|
|
1380
|
+
const part = value.content_block;
|
|
1381
|
+
const contentBlockType = part.type;
|
|
1382
|
+
blockType = contentBlockType;
|
|
1383
|
+
|
|
1384
|
+
switch (contentBlockType) {
|
|
1385
|
+
case 'text': {
|
|
1386
|
+
// when a json response tool is used, the tool call is returned as text,
|
|
1387
|
+
// so we ignore the text content:
|
|
1388
|
+
if (usesJsonResponseTool) {
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
contentBlocks[value.index] = { type: 'text' };
|
|
1393
|
+
controller.enqueue({
|
|
1394
|
+
type: 'text-start',
|
|
1395
|
+
id: String(value.index),
|
|
1396
|
+
});
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
case 'thinking': {
|
|
1401
|
+
contentBlocks[value.index] = { type: 'reasoning' };
|
|
1402
|
+
controller.enqueue({
|
|
1403
|
+
type: 'reasoning-start',
|
|
1404
|
+
id: String(value.index),
|
|
1405
|
+
});
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
case 'redacted_thinking': {
|
|
1410
|
+
contentBlocks[value.index] = { type: 'reasoning' };
|
|
1411
|
+
controller.enqueue({
|
|
1412
|
+
type: 'reasoning-start',
|
|
1413
|
+
id: String(value.index),
|
|
1414
|
+
providerMetadata: {
|
|
1415
|
+
anthropic: {
|
|
1416
|
+
redactedData: part.data,
|
|
1417
|
+
} satisfies AnthropicReasoningMetadata,
|
|
1418
|
+
},
|
|
1419
|
+
});
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
case 'compaction': {
|
|
1424
|
+
contentBlocks[value.index] = { type: 'text' };
|
|
1425
|
+
controller.enqueue({
|
|
1426
|
+
type: 'text-start',
|
|
1427
|
+
id: String(value.index),
|
|
1428
|
+
providerMetadata: {
|
|
1429
|
+
anthropic: {
|
|
1430
|
+
type: 'compaction',
|
|
1431
|
+
},
|
|
1432
|
+
},
|
|
1433
|
+
});
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
case 'tool_use': {
|
|
1438
|
+
const isJsonResponseTool =
|
|
1439
|
+
usesJsonResponseTool && part.name === 'json';
|
|
1440
|
+
|
|
1441
|
+
if (isJsonResponseTool) {
|
|
1442
|
+
isJsonResponseFromTool = true;
|
|
1443
|
+
|
|
1444
|
+
contentBlocks[value.index] = { type: 'text' };
|
|
1445
|
+
|
|
1446
|
+
controller.enqueue({
|
|
1447
|
+
type: 'text-start',
|
|
1448
|
+
id: String(value.index),
|
|
1449
|
+
});
|
|
1450
|
+
} else {
|
|
1451
|
+
// Extract caller info for type-safe access
|
|
1452
|
+
const caller = part.caller;
|
|
1453
|
+
const callerInfo = caller
|
|
1454
|
+
? {
|
|
1455
|
+
type: caller.type,
|
|
1456
|
+
toolId:
|
|
1457
|
+
'tool_id' in caller ? caller.tool_id : undefined,
|
|
1458
|
+
}
|
|
1459
|
+
: undefined;
|
|
1460
|
+
|
|
1461
|
+
// Programmatic tool calling: for deferred tool calls from code_execution,
|
|
1462
|
+
// input may be present directly in content_block_start.
|
|
1463
|
+
// Only use if non-empty (empty {} means input comes via deltas)
|
|
1464
|
+
const hasNonEmptyInput =
|
|
1465
|
+
part.input && Object.keys(part.input).length > 0;
|
|
1466
|
+
const initialInput = hasNonEmptyInput
|
|
1467
|
+
? JSON.stringify(part.input)
|
|
1468
|
+
: '';
|
|
1469
|
+
|
|
1470
|
+
contentBlocks[value.index] = {
|
|
1471
|
+
type: 'tool-call',
|
|
1472
|
+
toolCallId: part.id,
|
|
1473
|
+
toolName: part.name,
|
|
1474
|
+
input: initialInput,
|
|
1475
|
+
firstDelta: initialInput.length === 0,
|
|
1476
|
+
...(callerInfo && { caller: callerInfo }),
|
|
1477
|
+
};
|
|
1478
|
+
|
|
1479
|
+
controller.enqueue({
|
|
1480
|
+
type: 'tool-input-start',
|
|
1481
|
+
id: part.id,
|
|
1482
|
+
toolName: part.name,
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
case 'server_tool_use': {
|
|
1489
|
+
if (
|
|
1490
|
+
[
|
|
1491
|
+
'web_fetch',
|
|
1492
|
+
'web_search',
|
|
1493
|
+
// code execution 20250825:
|
|
1494
|
+
'code_execution',
|
|
1495
|
+
// code execution 20250825 text editor:
|
|
1496
|
+
'text_editor_code_execution',
|
|
1497
|
+
// code execution 20250825 bash:
|
|
1498
|
+
'bash_code_execution',
|
|
1499
|
+
].includes(part.name)
|
|
1500
|
+
) {
|
|
1501
|
+
// map tool names for the code execution 20250825 tool:
|
|
1502
|
+
const providerToolName =
|
|
1503
|
+
part.name === 'text_editor_code_execution' ||
|
|
1504
|
+
part.name === 'bash_code_execution'
|
|
1505
|
+
? 'code_execution'
|
|
1506
|
+
: part.name;
|
|
1507
|
+
|
|
1508
|
+
const customToolName =
|
|
1509
|
+
toolNameMapping.toCustomToolName(providerToolName);
|
|
1510
|
+
|
|
1511
|
+
// Tools like 'web_fetch_20260209' provide input data here.
|
|
1512
|
+
// Other tools like 'code_execution_20260120' provide input data via deltas.
|
|
1513
|
+
// So we only use this if it's non-empty to avoid conflicts.
|
|
1514
|
+
const finalInput =
|
|
1515
|
+
part.input != null &&
|
|
1516
|
+
typeof part.input === 'object' &&
|
|
1517
|
+
Object.keys(part.input).length > 0
|
|
1518
|
+
? JSON.stringify(part.input)
|
|
1519
|
+
: '';
|
|
1520
|
+
|
|
1521
|
+
contentBlocks[value.index] = {
|
|
1522
|
+
type: 'tool-call',
|
|
1523
|
+
toolCallId: part.id,
|
|
1524
|
+
toolName: customToolName,
|
|
1525
|
+
input: finalInput,
|
|
1526
|
+
providerExecuted: true,
|
|
1527
|
+
...(markCodeExecutionDynamic &&
|
|
1528
|
+
providerToolName === 'code_execution'
|
|
1529
|
+
? { dynamic: true }
|
|
1530
|
+
: {}),
|
|
1531
|
+
firstDelta: true,
|
|
1532
|
+
providerToolName: part.name,
|
|
1533
|
+
};
|
|
1534
|
+
|
|
1535
|
+
controller.enqueue({
|
|
1536
|
+
type: 'tool-input-start',
|
|
1537
|
+
id: part.id,
|
|
1538
|
+
toolName: customToolName,
|
|
1539
|
+
providerExecuted: true,
|
|
1540
|
+
...(markCodeExecutionDynamic &&
|
|
1541
|
+
providerToolName === 'code_execution'
|
|
1542
|
+
? { dynamic: true }
|
|
1543
|
+
: {}),
|
|
1544
|
+
});
|
|
1545
|
+
} else if (
|
|
1546
|
+
part.name === 'tool_search_tool_regex' ||
|
|
1547
|
+
part.name === 'tool_search_tool_bm25'
|
|
1548
|
+
) {
|
|
1549
|
+
serverToolCalls[part.id] = part.name;
|
|
1550
|
+
const customToolName = toolNameMapping.toCustomToolName(
|
|
1551
|
+
part.name,
|
|
1552
|
+
);
|
|
1553
|
+
|
|
1554
|
+
contentBlocks[value.index] = {
|
|
1555
|
+
type: 'tool-call',
|
|
1556
|
+
toolCallId: part.id,
|
|
1557
|
+
toolName: customToolName,
|
|
1558
|
+
input: '',
|
|
1559
|
+
providerExecuted: true,
|
|
1560
|
+
firstDelta: true,
|
|
1561
|
+
providerToolName: part.name,
|
|
1562
|
+
};
|
|
1563
|
+
|
|
1564
|
+
controller.enqueue({
|
|
1565
|
+
type: 'tool-input-start',
|
|
1566
|
+
id: part.id,
|
|
1567
|
+
toolName: customToolName,
|
|
1568
|
+
providerExecuted: true,
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
case 'web_fetch_tool_result': {
|
|
1576
|
+
if (part.content.type === 'web_fetch_result') {
|
|
1577
|
+
citationDocuments.push({
|
|
1578
|
+
title: part.content.content.title ?? part.content.url,
|
|
1579
|
+
mediaType: part.content.content.source.media_type,
|
|
1580
|
+
});
|
|
1581
|
+
controller.enqueue({
|
|
1582
|
+
type: 'tool-result',
|
|
1583
|
+
toolCallId: part.tool_use_id,
|
|
1584
|
+
toolName: toolNameMapping.toCustomToolName('web_fetch'),
|
|
1585
|
+
result: {
|
|
1586
|
+
type: 'web_fetch_result',
|
|
1587
|
+
url: part.content.url,
|
|
1588
|
+
retrievedAt: part.content.retrieved_at,
|
|
1589
|
+
content: {
|
|
1590
|
+
type: part.content.content.type,
|
|
1591
|
+
title: part.content.content.title,
|
|
1592
|
+
citations: part.content.content.citations,
|
|
1593
|
+
source: {
|
|
1594
|
+
type: part.content.content.source.type,
|
|
1595
|
+
mediaType: part.content.content.source.media_type,
|
|
1596
|
+
data: part.content.content.source.data,
|
|
1597
|
+
},
|
|
1598
|
+
},
|
|
1599
|
+
},
|
|
1600
|
+
});
|
|
1601
|
+
} else if (
|
|
1602
|
+
part.content.type === 'web_fetch_tool_result_error'
|
|
1603
|
+
) {
|
|
1604
|
+
controller.enqueue({
|
|
1605
|
+
type: 'tool-result',
|
|
1606
|
+
toolCallId: part.tool_use_id,
|
|
1607
|
+
toolName: toolNameMapping.toCustomToolName('web_fetch'),
|
|
1608
|
+
isError: true,
|
|
1609
|
+
result: {
|
|
1610
|
+
type: 'web_fetch_tool_result_error',
|
|
1611
|
+
errorCode: part.content.error_code,
|
|
1612
|
+
},
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
case 'web_search_tool_result': {
|
|
1620
|
+
if (Array.isArray(part.content)) {
|
|
1621
|
+
controller.enqueue({
|
|
1622
|
+
type: 'tool-result',
|
|
1623
|
+
toolCallId: part.tool_use_id,
|
|
1624
|
+
toolName: toolNameMapping.toCustomToolName('web_search'),
|
|
1625
|
+
result: part.content.map(result => ({
|
|
1626
|
+
url: result.url,
|
|
1627
|
+
title: result.title,
|
|
1628
|
+
pageAge: result.page_age ?? null,
|
|
1629
|
+
encryptedContent: result.encrypted_content,
|
|
1630
|
+
type: result.type,
|
|
1631
|
+
})),
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
for (const result of part.content) {
|
|
1635
|
+
controller.enqueue({
|
|
1636
|
+
type: 'source',
|
|
1637
|
+
sourceType: 'url',
|
|
1638
|
+
id: generateId(),
|
|
1639
|
+
url: result.url,
|
|
1640
|
+
title: result.title,
|
|
1641
|
+
providerMetadata: {
|
|
1642
|
+
anthropic: {
|
|
1643
|
+
pageAge: result.page_age ?? null,
|
|
1644
|
+
},
|
|
1645
|
+
},
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
} else {
|
|
1649
|
+
controller.enqueue({
|
|
1650
|
+
type: 'tool-result',
|
|
1651
|
+
toolCallId: part.tool_use_id,
|
|
1652
|
+
toolName: toolNameMapping.toCustomToolName('web_search'),
|
|
1653
|
+
isError: true,
|
|
1654
|
+
result: {
|
|
1655
|
+
type: 'web_search_tool_result_error',
|
|
1656
|
+
errorCode: part.content.error_code,
|
|
1657
|
+
},
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// code execution 20250522:
|
|
1664
|
+
case 'code_execution_tool_result': {
|
|
1665
|
+
if (part.content.type === 'code_execution_result') {
|
|
1666
|
+
controller.enqueue({
|
|
1667
|
+
type: 'tool-result',
|
|
1668
|
+
toolCallId: part.tool_use_id,
|
|
1669
|
+
toolName:
|
|
1670
|
+
toolNameMapping.toCustomToolName('code_execution'),
|
|
1671
|
+
result: {
|
|
1672
|
+
type: part.content.type,
|
|
1673
|
+
stdout: part.content.stdout,
|
|
1674
|
+
stderr: part.content.stderr,
|
|
1675
|
+
return_code: part.content.return_code,
|
|
1676
|
+
content: part.content.content ?? [],
|
|
1677
|
+
},
|
|
1678
|
+
});
|
|
1679
|
+
} else if (
|
|
1680
|
+
part.content.type === 'encrypted_code_execution_result'
|
|
1681
|
+
) {
|
|
1682
|
+
controller.enqueue({
|
|
1683
|
+
type: 'tool-result',
|
|
1684
|
+
toolCallId: part.tool_use_id,
|
|
1685
|
+
toolName:
|
|
1686
|
+
toolNameMapping.toCustomToolName('code_execution'),
|
|
1687
|
+
result: {
|
|
1688
|
+
type: part.content.type,
|
|
1689
|
+
encrypted_stdout: part.content.encrypted_stdout,
|
|
1690
|
+
stderr: part.content.stderr,
|
|
1691
|
+
return_code: part.content.return_code,
|
|
1692
|
+
content: part.content.content ?? [],
|
|
1693
|
+
},
|
|
1694
|
+
});
|
|
1695
|
+
} else if (
|
|
1696
|
+
part.content.type === 'code_execution_tool_result_error'
|
|
1697
|
+
) {
|
|
1698
|
+
controller.enqueue({
|
|
1699
|
+
type: 'tool-result',
|
|
1700
|
+
toolCallId: part.tool_use_id,
|
|
1701
|
+
toolName:
|
|
1702
|
+
toolNameMapping.toCustomToolName('code_execution'),
|
|
1703
|
+
isError: true,
|
|
1704
|
+
result: {
|
|
1705
|
+
type: 'code_execution_tool_result_error',
|
|
1706
|
+
errorCode: part.content.error_code,
|
|
1707
|
+
},
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// code execution 20250825:
|
|
1715
|
+
case 'bash_code_execution_tool_result':
|
|
1716
|
+
case 'text_editor_code_execution_tool_result': {
|
|
1717
|
+
controller.enqueue({
|
|
1718
|
+
type: 'tool-result',
|
|
1719
|
+
toolCallId: part.tool_use_id,
|
|
1720
|
+
toolName:
|
|
1721
|
+
toolNameMapping.toCustomToolName('code_execution'),
|
|
1722
|
+
result: part.content,
|
|
1723
|
+
});
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// tool search tool results:
|
|
1728
|
+
case 'tool_search_tool_result': {
|
|
1729
|
+
let providerToolName = serverToolCalls[part.tool_use_id];
|
|
1730
|
+
|
|
1731
|
+
if (providerToolName == null) {
|
|
1732
|
+
const bm25CustomName = toolNameMapping.toCustomToolName(
|
|
1733
|
+
'tool_search_tool_bm25',
|
|
1734
|
+
);
|
|
1735
|
+
const regexCustomName = toolNameMapping.toCustomToolName(
|
|
1736
|
+
'tool_search_tool_regex',
|
|
1737
|
+
);
|
|
1738
|
+
|
|
1739
|
+
if (bm25CustomName !== 'tool_search_tool_bm25') {
|
|
1740
|
+
providerToolName = 'tool_search_tool_bm25';
|
|
1741
|
+
} else if (regexCustomName !== 'tool_search_tool_regex') {
|
|
1742
|
+
providerToolName = 'tool_search_tool_regex';
|
|
1743
|
+
} else {
|
|
1744
|
+
providerToolName = 'tool_search_tool_regex';
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
if (part.content.type === 'tool_search_tool_search_result') {
|
|
1749
|
+
controller.enqueue({
|
|
1750
|
+
type: 'tool-result',
|
|
1751
|
+
toolCallId: part.tool_use_id,
|
|
1752
|
+
toolName:
|
|
1753
|
+
toolNameMapping.toCustomToolName(providerToolName),
|
|
1754
|
+
result: part.content.tool_references.map(ref => ({
|
|
1755
|
+
type: ref.type,
|
|
1756
|
+
toolName: ref.tool_name,
|
|
1757
|
+
})),
|
|
1758
|
+
});
|
|
1759
|
+
} else {
|
|
1760
|
+
controller.enqueue({
|
|
1761
|
+
type: 'tool-result',
|
|
1762
|
+
toolCallId: part.tool_use_id,
|
|
1763
|
+
toolName:
|
|
1764
|
+
toolNameMapping.toCustomToolName(providerToolName),
|
|
1765
|
+
isError: true,
|
|
1766
|
+
result: {
|
|
1767
|
+
type: 'tool_search_tool_result_error',
|
|
1768
|
+
errorCode: part.content.error_code,
|
|
1769
|
+
},
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
case 'mcp_tool_use': {
|
|
1776
|
+
mcpToolCalls[part.id] = {
|
|
1777
|
+
type: 'tool-call',
|
|
1778
|
+
toolCallId: part.id,
|
|
1779
|
+
toolName: part.name,
|
|
1780
|
+
input: JSON.stringify(part.input),
|
|
1781
|
+
providerExecuted: true,
|
|
1782
|
+
dynamic: true,
|
|
1783
|
+
providerMetadata: {
|
|
1784
|
+
anthropic: {
|
|
1785
|
+
type: 'mcp-tool-use',
|
|
1786
|
+
serverName: part.server_name,
|
|
1787
|
+
},
|
|
1788
|
+
},
|
|
1789
|
+
};
|
|
1790
|
+
controller.enqueue(mcpToolCalls[part.id]);
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
case 'mcp_tool_result': {
|
|
1795
|
+
controller.enqueue({
|
|
1796
|
+
type: 'tool-result',
|
|
1797
|
+
toolCallId: part.tool_use_id,
|
|
1798
|
+
toolName: mcpToolCalls[part.tool_use_id].toolName,
|
|
1799
|
+
isError: part.is_error,
|
|
1800
|
+
result: part.content,
|
|
1801
|
+
dynamic: true,
|
|
1802
|
+
providerMetadata:
|
|
1803
|
+
mcpToolCalls[part.tool_use_id].providerMetadata,
|
|
1804
|
+
});
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
default: {
|
|
1809
|
+
const _exhaustiveCheck: never = contentBlockType;
|
|
1810
|
+
throw new Error(
|
|
1811
|
+
`Unsupported content block type: ${_exhaustiveCheck}`,
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
case 'content_block_stop': {
|
|
1818
|
+
// when finishing a tool call block, send the full tool call:
|
|
1819
|
+
if (contentBlocks[value.index] != null) {
|
|
1820
|
+
const contentBlock = contentBlocks[value.index];
|
|
1821
|
+
|
|
1822
|
+
switch (contentBlock.type) {
|
|
1823
|
+
case 'text': {
|
|
1824
|
+
controller.enqueue({
|
|
1825
|
+
type: 'text-end',
|
|
1826
|
+
id: String(value.index),
|
|
1827
|
+
});
|
|
1828
|
+
break;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
case 'reasoning': {
|
|
1832
|
+
controller.enqueue({
|
|
1833
|
+
type: 'reasoning-end',
|
|
1834
|
+
id: String(value.index),
|
|
1835
|
+
});
|
|
1836
|
+
break;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
case 'tool-call':
|
|
1840
|
+
// when a json response tool is used, the tool call is returned as text,
|
|
1841
|
+
// so we ignore the tool call content:
|
|
1842
|
+
const isJsonResponseTool =
|
|
1843
|
+
usesJsonResponseTool && contentBlock.toolName === 'json';
|
|
1844
|
+
|
|
1845
|
+
if (!isJsonResponseTool) {
|
|
1846
|
+
controller.enqueue({
|
|
1847
|
+
type: 'tool-input-end',
|
|
1848
|
+
id: contentBlock.toolCallId,
|
|
1849
|
+
});
|
|
1850
|
+
|
|
1851
|
+
// For code_execution, inject 'programmatic-tool-call' type
|
|
1852
|
+
// when input has { code } format (programmatic tool calling)
|
|
1853
|
+
let finalInput =
|
|
1854
|
+
contentBlock.input === '' ? '{}' : contentBlock.input;
|
|
1855
|
+
if (contentBlock.providerToolName === 'code_execution') {
|
|
1856
|
+
try {
|
|
1857
|
+
const parsed = JSON.parse(finalInput);
|
|
1858
|
+
if (
|
|
1859
|
+
parsed != null &&
|
|
1860
|
+
typeof parsed === 'object' &&
|
|
1861
|
+
'code' in parsed &&
|
|
1862
|
+
!('type' in parsed)
|
|
1863
|
+
) {
|
|
1864
|
+
finalInput = JSON.stringify({
|
|
1865
|
+
type: 'programmatic-tool-call',
|
|
1866
|
+
...parsed,
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
} catch {
|
|
1870
|
+
// ignore parse errors, use original input
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
controller.enqueue({
|
|
1875
|
+
type: 'tool-call',
|
|
1876
|
+
toolCallId: contentBlock.toolCallId,
|
|
1877
|
+
toolName: contentBlock.toolName,
|
|
1878
|
+
input: finalInput,
|
|
1879
|
+
providerExecuted: contentBlock.providerExecuted,
|
|
1880
|
+
...(markCodeExecutionDynamic &&
|
|
1881
|
+
contentBlock.providerToolName === 'code_execution'
|
|
1882
|
+
? { dynamic: true }
|
|
1883
|
+
: {}),
|
|
1884
|
+
...(contentBlock.caller && {
|
|
1885
|
+
providerMetadata: {
|
|
1886
|
+
anthropic: {
|
|
1887
|
+
caller: contentBlock.caller,
|
|
1888
|
+
},
|
|
1889
|
+
},
|
|
1890
|
+
}),
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
break;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
delete contentBlocks[value.index];
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
blockType = undefined; // reset block type
|
|
1900
|
+
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
case 'content_block_delta': {
|
|
1905
|
+
const deltaType = value.delta.type;
|
|
1906
|
+
|
|
1907
|
+
switch (deltaType) {
|
|
1908
|
+
case 'text_delta': {
|
|
1909
|
+
// when a json response tool is used, the tool call is returned as text,
|
|
1910
|
+
// so we ignore the text content:
|
|
1911
|
+
if (usesJsonResponseTool) {
|
|
1912
|
+
return; // excluding the text-start will also exclude the text-end
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
controller.enqueue({
|
|
1916
|
+
type: 'text-delta',
|
|
1917
|
+
id: String(value.index),
|
|
1918
|
+
delta: value.delta.text,
|
|
1919
|
+
});
|
|
1920
|
+
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
case 'thinking_delta': {
|
|
1925
|
+
controller.enqueue({
|
|
1926
|
+
type: 'reasoning-delta',
|
|
1927
|
+
id: String(value.index),
|
|
1928
|
+
delta: value.delta.thinking,
|
|
1929
|
+
});
|
|
1930
|
+
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
case 'signature_delta': {
|
|
1935
|
+
// signature are only supported on thinking blocks:
|
|
1936
|
+
if (blockType === 'thinking') {
|
|
1937
|
+
controller.enqueue({
|
|
1938
|
+
type: 'reasoning-delta',
|
|
1939
|
+
id: String(value.index),
|
|
1940
|
+
delta: '',
|
|
1941
|
+
providerMetadata: {
|
|
1942
|
+
anthropic: {
|
|
1943
|
+
signature: value.delta.signature,
|
|
1944
|
+
} satisfies AnthropicReasoningMetadata,
|
|
1945
|
+
},
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
case 'compaction_delta': {
|
|
1953
|
+
if (value.delta.content != null) {
|
|
1954
|
+
controller.enqueue({
|
|
1955
|
+
type: 'text-delta',
|
|
1956
|
+
id: String(value.index),
|
|
1957
|
+
delta: value.delta.content,
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
case 'input_json_delta': {
|
|
1965
|
+
const contentBlock = contentBlocks[value.index];
|
|
1966
|
+
let delta = value.delta.partial_json;
|
|
1967
|
+
|
|
1968
|
+
// skip empty deltas to enable replacing the first character
|
|
1969
|
+
// in the code execution 20250825 tool.
|
|
1970
|
+
if (delta.length === 0) {
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
if (isJsonResponseFromTool) {
|
|
1975
|
+
if (contentBlock?.type !== 'text') {
|
|
1976
|
+
return; // exclude reasoning
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
controller.enqueue({
|
|
1980
|
+
type: 'text-delta',
|
|
1981
|
+
id: String(value.index),
|
|
1982
|
+
delta,
|
|
1983
|
+
});
|
|
1984
|
+
} else {
|
|
1985
|
+
if (contentBlock?.type !== 'tool-call') {
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
// for the code execution 20250825, we need to add
|
|
1990
|
+
// the type to the delta and change the tool name.
|
|
1991
|
+
if (
|
|
1992
|
+
contentBlock.firstDelta &&
|
|
1993
|
+
(contentBlock.providerToolName ===
|
|
1994
|
+
'bash_code_execution' ||
|
|
1995
|
+
contentBlock.providerToolName ===
|
|
1996
|
+
'text_editor_code_execution')
|
|
1997
|
+
) {
|
|
1998
|
+
delta = `{"type": "${contentBlock.providerToolName}",${delta.substring(1)}`;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
controller.enqueue({
|
|
2002
|
+
type: 'tool-input-delta',
|
|
2003
|
+
id: contentBlock.toolCallId,
|
|
2004
|
+
delta,
|
|
2005
|
+
});
|
|
2006
|
+
|
|
2007
|
+
contentBlock.input += delta;
|
|
2008
|
+
contentBlock.firstDelta = false;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
case 'citations_delta': {
|
|
2015
|
+
const citation = value.delta.citation;
|
|
2016
|
+
const source = createCitationSource(
|
|
2017
|
+
citation,
|
|
2018
|
+
citationDocuments,
|
|
2019
|
+
generateId,
|
|
2020
|
+
);
|
|
2021
|
+
|
|
2022
|
+
if (source) {
|
|
2023
|
+
controller.enqueue(source);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
default: {
|
|
2030
|
+
const _exhaustiveCheck: never = deltaType;
|
|
2031
|
+
throw new Error(
|
|
2032
|
+
`Unsupported delta type: ${_exhaustiveCheck}`,
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
case 'message_start': {
|
|
2039
|
+
usage.input_tokens = value.message.usage.input_tokens;
|
|
2040
|
+
usage.cache_read_input_tokens =
|
|
2041
|
+
value.message.usage.cache_read_input_tokens ?? 0;
|
|
2042
|
+
usage.cache_creation_input_tokens =
|
|
2043
|
+
value.message.usage.cache_creation_input_tokens ?? 0;
|
|
2044
|
+
|
|
2045
|
+
rawUsage = {
|
|
2046
|
+
...(value.message.usage as JSONObject),
|
|
2047
|
+
};
|
|
2048
|
+
|
|
2049
|
+
cacheCreationInputTokens =
|
|
2050
|
+
value.message.usage.cache_creation_input_tokens ?? null;
|
|
2051
|
+
|
|
2052
|
+
if (value.message.container != null) {
|
|
2053
|
+
container = {
|
|
2054
|
+
expiresAt: value.message.container.expires_at,
|
|
2055
|
+
id: value.message.container.id,
|
|
2056
|
+
skills: null,
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
if (value.message.stop_reason != null) {
|
|
2061
|
+
finishReason = {
|
|
2062
|
+
unified: mapAnthropicStopReason({
|
|
2063
|
+
finishReason: value.message.stop_reason,
|
|
2064
|
+
isJsonResponseFromTool,
|
|
2065
|
+
}),
|
|
2066
|
+
raw: value.message.stop_reason,
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
controller.enqueue({
|
|
2071
|
+
type: 'response-metadata',
|
|
2072
|
+
id: value.message.id ?? undefined,
|
|
2073
|
+
modelId: value.message.model ?? undefined,
|
|
2074
|
+
});
|
|
2075
|
+
|
|
2076
|
+
// Programmatic tool calling: process pre-populated content blocks
|
|
2077
|
+
// (for deferred tool calls, content may be in message_start)
|
|
2078
|
+
if (value.message.content != null) {
|
|
2079
|
+
for (
|
|
2080
|
+
let contentIndex = 0;
|
|
2081
|
+
contentIndex < value.message.content.length;
|
|
2082
|
+
contentIndex++
|
|
2083
|
+
) {
|
|
2084
|
+
const part = value.message.content[contentIndex];
|
|
2085
|
+
if (part.type === 'tool_use') {
|
|
2086
|
+
const caller = part.caller;
|
|
2087
|
+
const callerInfo = caller
|
|
2088
|
+
? {
|
|
2089
|
+
type: caller.type,
|
|
2090
|
+
toolId:
|
|
2091
|
+
'tool_id' in caller ? caller.tool_id : undefined,
|
|
2092
|
+
}
|
|
2093
|
+
: undefined;
|
|
2094
|
+
|
|
2095
|
+
controller.enqueue({
|
|
2096
|
+
type: 'tool-input-start',
|
|
2097
|
+
id: part.id,
|
|
2098
|
+
toolName: part.name,
|
|
2099
|
+
});
|
|
2100
|
+
|
|
2101
|
+
const inputStr = JSON.stringify(part.input ?? {});
|
|
2102
|
+
controller.enqueue({
|
|
2103
|
+
type: 'tool-input-delta',
|
|
2104
|
+
id: part.id,
|
|
2105
|
+
delta: inputStr,
|
|
2106
|
+
});
|
|
2107
|
+
|
|
2108
|
+
controller.enqueue({
|
|
2109
|
+
type: 'tool-input-end',
|
|
2110
|
+
id: part.id,
|
|
2111
|
+
});
|
|
2112
|
+
|
|
2113
|
+
controller.enqueue({
|
|
2114
|
+
type: 'tool-call',
|
|
2115
|
+
toolCallId: part.id,
|
|
2116
|
+
toolName: part.name,
|
|
2117
|
+
input: inputStr,
|
|
2118
|
+
...(callerInfo && {
|
|
2119
|
+
providerMetadata: {
|
|
2120
|
+
anthropic: {
|
|
2121
|
+
caller: callerInfo,
|
|
2122
|
+
},
|
|
2123
|
+
},
|
|
2124
|
+
}),
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
case 'message_delta': {
|
|
2134
|
+
if (
|
|
2135
|
+
value.usage.input_tokens != null &&
|
|
2136
|
+
usage.input_tokens !== value.usage.input_tokens
|
|
2137
|
+
) {
|
|
2138
|
+
usage.input_tokens = value.usage.input_tokens;
|
|
2139
|
+
}
|
|
2140
|
+
usage.output_tokens = value.usage.output_tokens;
|
|
2141
|
+
|
|
2142
|
+
if (value.usage.cache_read_input_tokens != null) {
|
|
2143
|
+
usage.cache_read_input_tokens =
|
|
2144
|
+
value.usage.cache_read_input_tokens;
|
|
2145
|
+
}
|
|
2146
|
+
if (value.usage.cache_creation_input_tokens != null) {
|
|
2147
|
+
usage.cache_creation_input_tokens =
|
|
2148
|
+
value.usage.cache_creation_input_tokens;
|
|
2149
|
+
cacheCreationInputTokens =
|
|
2150
|
+
value.usage.cache_creation_input_tokens;
|
|
2151
|
+
}
|
|
2152
|
+
if (value.usage.iterations != null) {
|
|
2153
|
+
usage.iterations = value.usage.iterations;
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
finishReason = {
|
|
2157
|
+
unified: mapAnthropicStopReason({
|
|
2158
|
+
finishReason: value.delta.stop_reason,
|
|
2159
|
+
isJsonResponseFromTool,
|
|
2160
|
+
}),
|
|
2161
|
+
raw: value.delta.stop_reason ?? undefined,
|
|
2162
|
+
};
|
|
2163
|
+
|
|
2164
|
+
stopSequence = value.delta.stop_sequence ?? null;
|
|
2165
|
+
container =
|
|
2166
|
+
value.delta.container != null
|
|
2167
|
+
? {
|
|
2168
|
+
expiresAt: value.delta.container.expires_at,
|
|
2169
|
+
id: value.delta.container.id,
|
|
2170
|
+
skills:
|
|
2171
|
+
value.delta.container.skills?.map(skill => ({
|
|
2172
|
+
type: skill.type,
|
|
2173
|
+
skillId: skill.skill_id,
|
|
2174
|
+
version: skill.version,
|
|
2175
|
+
})) ?? null,
|
|
2176
|
+
}
|
|
2177
|
+
: null;
|
|
2178
|
+
|
|
2179
|
+
if (value.context_management) {
|
|
2180
|
+
contextManagement = mapAnthropicResponseContextManagement(
|
|
2181
|
+
value.context_management,
|
|
2182
|
+
);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
rawUsage = {
|
|
2186
|
+
...rawUsage,
|
|
2187
|
+
...(value.usage as JSONObject),
|
|
2188
|
+
};
|
|
2189
|
+
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
case 'message_stop': {
|
|
2194
|
+
const anthropicMetadata = {
|
|
2195
|
+
usage: (rawUsage as JSONObject) ?? null,
|
|
2196
|
+
cacheCreationInputTokens,
|
|
2197
|
+
stopSequence,
|
|
2198
|
+
iterations: usage.iterations
|
|
2199
|
+
? usage.iterations.map(iter => ({
|
|
2200
|
+
type: iter.type,
|
|
2201
|
+
inputTokens: iter.input_tokens,
|
|
2202
|
+
outputTokens: iter.output_tokens,
|
|
2203
|
+
}))
|
|
2204
|
+
: null,
|
|
2205
|
+
container,
|
|
2206
|
+
contextManagement,
|
|
2207
|
+
} satisfies AnthropicMessageMetadata;
|
|
2208
|
+
|
|
2209
|
+
const providerMetadata: SharedV3ProviderMetadata = {
|
|
2210
|
+
anthropic: anthropicMetadata,
|
|
2211
|
+
};
|
|
2212
|
+
|
|
2213
|
+
if (
|
|
2214
|
+
usedCustomProviderKey &&
|
|
2215
|
+
providerOptionsName !== 'anthropic'
|
|
2216
|
+
) {
|
|
2217
|
+
providerMetadata[providerOptionsName] = anthropicMetadata;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
controller.enqueue({
|
|
2221
|
+
type: 'finish',
|
|
2222
|
+
finishReason,
|
|
2223
|
+
usage: convertAnthropicMessagesUsage({ usage, rawUsage }),
|
|
2224
|
+
providerMetadata,
|
|
2225
|
+
});
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
case 'error': {
|
|
2230
|
+
controller.enqueue({ type: 'error', error: value.error });
|
|
2231
|
+
return;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
default: {
|
|
2235
|
+
const _exhaustiveCheck: never = value;
|
|
2236
|
+
throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
},
|
|
2240
|
+
}),
|
|
2241
|
+
);
|
|
2242
|
+
|
|
2243
|
+
// The first chunk needs to be pulled immediately to check if it is an error
|
|
2244
|
+
const [streamForFirstChunk, streamForConsumer] = transformedStream.tee();
|
|
2245
|
+
|
|
2246
|
+
const firstChunkReader = streamForFirstChunk.getReader();
|
|
2247
|
+
try {
|
|
2248
|
+
await firstChunkReader.read(); // streamStart comes first, ignored
|
|
2249
|
+
|
|
2250
|
+
let result = await firstChunkReader.read();
|
|
2251
|
+
|
|
2252
|
+
// when raw chunks are enabled, the first chunk is a raw chunk, so we need to read the next chunk
|
|
2253
|
+
if (result.value?.type === 'raw') {
|
|
2254
|
+
result = await firstChunkReader.read();
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// The Anthropic API returns 200 responses when there are overloaded errors.
|
|
2258
|
+
// We handle the case where the first chunk is an error here and transform
|
|
2259
|
+
// it into an APICallError.
|
|
2260
|
+
if (result.value?.type === 'error') {
|
|
2261
|
+
const error = result.value.error as { message: string; type: string };
|
|
2262
|
+
|
|
2263
|
+
throw new APICallError({
|
|
2264
|
+
message: error.message,
|
|
2265
|
+
url,
|
|
2266
|
+
requestBodyValues: body,
|
|
2267
|
+
statusCode: error.type === 'overloaded_error' ? 529 : 500,
|
|
2268
|
+
responseHeaders,
|
|
2269
|
+
responseBody: JSON.stringify(error),
|
|
2270
|
+
isRetryable: error.type === 'overloaded_error',
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
} finally {
|
|
2274
|
+
firstChunkReader.cancel().catch(() => {});
|
|
2275
|
+
firstChunkReader.releaseLock();
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
return {
|
|
2279
|
+
stream: streamForConsumer,
|
|
2280
|
+
request: { body },
|
|
2281
|
+
response: { headers: responseHeaders },
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
/**
|
|
2287
|
+
* Returns the capabilities of a Claude model that are used for defaults and feature selection.
|
|
2288
|
+
*
|
|
2289
|
+
* @see https://docs.claude.com/en/docs/about-claude/models/overview#model-comparison-table
|
|
2290
|
+
* @see https://platform.claude.com/docs/en/build-with-claude/structured-outputs
|
|
2291
|
+
*/
|
|
2292
|
+
function getModelCapabilities(modelId: string): {
|
|
2293
|
+
maxOutputTokens: number;
|
|
2294
|
+
supportsStructuredOutput: boolean;
|
|
2295
|
+
isKnownModel: boolean;
|
|
2296
|
+
} {
|
|
2297
|
+
if (
|
|
2298
|
+
modelId.includes('claude-sonnet-4-6') ||
|
|
2299
|
+
modelId.includes('claude-opus-4-6')
|
|
2300
|
+
) {
|
|
2301
|
+
return {
|
|
2302
|
+
maxOutputTokens: 128000,
|
|
2303
|
+
supportsStructuredOutput: true,
|
|
2304
|
+
isKnownModel: true,
|
|
2305
|
+
};
|
|
2306
|
+
} else if (
|
|
2307
|
+
modelId.includes('claude-sonnet-4-5') ||
|
|
2308
|
+
modelId.includes('claude-opus-4-5') ||
|
|
2309
|
+
modelId.includes('claude-haiku-4-5')
|
|
2310
|
+
) {
|
|
2311
|
+
return {
|
|
2312
|
+
maxOutputTokens: 64000,
|
|
2313
|
+
supportsStructuredOutput: true,
|
|
2314
|
+
isKnownModel: true,
|
|
2315
|
+
};
|
|
2316
|
+
} else if (modelId.includes('claude-opus-4-1')) {
|
|
2317
|
+
return {
|
|
2318
|
+
maxOutputTokens: 32000,
|
|
2319
|
+
supportsStructuredOutput: true,
|
|
2320
|
+
isKnownModel: true,
|
|
2321
|
+
};
|
|
2322
|
+
} else if (modelId.includes('claude-sonnet-4-')) {
|
|
2323
|
+
return {
|
|
2324
|
+
maxOutputTokens: 64000,
|
|
2325
|
+
supportsStructuredOutput: false,
|
|
2326
|
+
isKnownModel: true,
|
|
2327
|
+
};
|
|
2328
|
+
} else if (modelId.includes('claude-opus-4-')) {
|
|
2329
|
+
return {
|
|
2330
|
+
maxOutputTokens: 32000,
|
|
2331
|
+
supportsStructuredOutput: false,
|
|
2332
|
+
isKnownModel: true,
|
|
2333
|
+
};
|
|
2334
|
+
} else if (modelId.includes('claude-3-haiku')) {
|
|
2335
|
+
return {
|
|
2336
|
+
maxOutputTokens: 4096,
|
|
2337
|
+
supportsStructuredOutput: false,
|
|
2338
|
+
isKnownModel: true,
|
|
2339
|
+
};
|
|
2340
|
+
} else {
|
|
2341
|
+
return {
|
|
2342
|
+
maxOutputTokens: 4096,
|
|
2343
|
+
supportsStructuredOutput: false,
|
|
2344
|
+
isKnownModel: false,
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
function hasWebTool20260209WithoutCodeExecution(
|
|
2350
|
+
tools: AnthropicTool[] | undefined,
|
|
2351
|
+
): boolean {
|
|
2352
|
+
if (!tools) {
|
|
2353
|
+
return false;
|
|
2354
|
+
}
|
|
2355
|
+
let hasWebTool20260209 = false;
|
|
2356
|
+
let hasCodeExecutionTool = false;
|
|
2357
|
+
for (const tool of tools) {
|
|
2358
|
+
if (
|
|
2359
|
+
'type' in tool &&
|
|
2360
|
+
(tool.type === 'web_fetch_20260209' ||
|
|
2361
|
+
tool.type === 'web_search_20260209')
|
|
2362
|
+
) {
|
|
2363
|
+
hasWebTool20260209 = true;
|
|
2364
|
+
continue;
|
|
2365
|
+
}
|
|
2366
|
+
if (tool.name === 'code_execution') {
|
|
2367
|
+
hasCodeExecutionTool = true;
|
|
2368
|
+
break;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
return hasWebTool20260209 && !hasCodeExecutionTool;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
function mapAnthropicResponseContextManagement(
|
|
2375
|
+
contextManagement: AnthropicResponseContextManagement | null | undefined,
|
|
2376
|
+
): AnthropicMessageMetadata['contextManagement'] | null {
|
|
2377
|
+
return contextManagement
|
|
2378
|
+
? {
|
|
2379
|
+
appliedEdits: contextManagement.applied_edits
|
|
2380
|
+
.map(edit => {
|
|
2381
|
+
const strategy = edit.type;
|
|
2382
|
+
|
|
2383
|
+
switch (strategy) {
|
|
2384
|
+
case 'clear_tool_uses_20250919':
|
|
2385
|
+
return {
|
|
2386
|
+
type: edit.type,
|
|
2387
|
+
clearedToolUses: edit.cleared_tool_uses,
|
|
2388
|
+
clearedInputTokens: edit.cleared_input_tokens,
|
|
2389
|
+
};
|
|
2390
|
+
|
|
2391
|
+
case 'clear_thinking_20251015':
|
|
2392
|
+
return {
|
|
2393
|
+
type: edit.type,
|
|
2394
|
+
clearedThinkingTurns: edit.cleared_thinking_turns,
|
|
2395
|
+
clearedInputTokens: edit.cleared_input_tokens,
|
|
2396
|
+
};
|
|
2397
|
+
|
|
2398
|
+
case 'compact_20260112':
|
|
2399
|
+
return {
|
|
2400
|
+
type: edit.type,
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
})
|
|
2404
|
+
.filter(edit => edit !== undefined),
|
|
2405
|
+
}
|
|
2406
|
+
: null;
|
|
2407
|
+
}
|