@better-agent/providers 0.1.0-canary.0
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/README.md +3 -0
- package/dist/anthropic/index.d.mts +1196 -0
- package/dist/anthropic/index.d.mts.map +1 -0
- package/dist/anthropic/index.mjs +1909 -0
- package/dist/anthropic/index.mjs.map +1 -0
- package/dist/fetch-CW0dWlVC.mjs +84 -0
- package/dist/fetch-CW0dWlVC.mjs.map +1 -0
- package/dist/openai/index.d.mts +1629 -0
- package/dist/openai/index.d.mts.map +1 -0
- package/dist/openai/index.mjs +6690 -0
- package/dist/openai/index.mjs.map +1 -0
- package/dist/xai/index.d.mts +382 -0
- package/dist/xai/index.d.mts.map +1 -0
- package/dist/xai/index.mjs +1277 -0
- package/dist/xai/index.mjs.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,1909 @@
|
|
|
1
|
+
import { n as extractPassthroughOptions, r as omitNullish, t as baFetch } from "../fetch-CW0dWlVC.mjs";
|
|
2
|
+
import { Events } from "@better-agent/core/events";
|
|
3
|
+
import { BetterAgentError } from "@better-agent/shared/errors";
|
|
4
|
+
import { err, ok } from "@better-agent/shared/neverthrow";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { safeJsonParse } from "@better-agent/shared/utils";
|
|
7
|
+
import { TOOL_JSON_SCHEMA, isCallableToolDefinition } from "@better-agent/core";
|
|
8
|
+
|
|
9
|
+
//#region src/anthropic/client/auth.ts
|
|
10
|
+
const buildAnthropicHeaders = (config, options = {}) => {
|
|
11
|
+
const headers = {
|
|
12
|
+
"anthropic-version": config.anthropicVersion ?? "2023-06-01",
|
|
13
|
+
...config.headers ?? {},
|
|
14
|
+
...options.headers ?? {}
|
|
15
|
+
};
|
|
16
|
+
if (config.authToken) headers.Authorization = `Bearer ${config.authToken}`;
|
|
17
|
+
else if (config.apiKey) headers["x-api-key"] = config.apiKey;
|
|
18
|
+
if (options.beta?.length) headers["anthropic-beta"] = options.beta.join(",");
|
|
19
|
+
return headers;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/anthropic/client/errors.ts
|
|
24
|
+
const mapAnthropicHttpError = (httpError, ctx) => {
|
|
25
|
+
if (!httpError) return BetterAgentError.fromCode("UPSTREAM_FAILED", "Anthropic request failed", { context: { provider: "anthropic" } }).at({
|
|
26
|
+
at: ctx.at,
|
|
27
|
+
data: { path: ctx.path }
|
|
28
|
+
});
|
|
29
|
+
const { status, statusText, error } = httpError;
|
|
30
|
+
const message = error?.message ?? statusText ?? "Anthropic request failed";
|
|
31
|
+
const code = String(error?.type ?? status);
|
|
32
|
+
return BetterAgentError.fromCode("UPSTREAM_FAILED", message, {
|
|
33
|
+
status,
|
|
34
|
+
context: {
|
|
35
|
+
provider: "anthropic",
|
|
36
|
+
upstreamCode: code,
|
|
37
|
+
raw: error
|
|
38
|
+
}
|
|
39
|
+
}).at({
|
|
40
|
+
at: ctx.at,
|
|
41
|
+
data: {
|
|
42
|
+
path: ctx.path,
|
|
43
|
+
status
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/anthropic/responses/schemas.ts
|
|
50
|
+
const ANTHROPIC_KNOWN_RESPONSE_MODELS = [
|
|
51
|
+
"claude-3-haiku-20240307",
|
|
52
|
+
"claude-haiku-4-5-20251001",
|
|
53
|
+
"claude-haiku-4-5",
|
|
54
|
+
"claude-opus-4-0",
|
|
55
|
+
"claude-opus-4-20250514",
|
|
56
|
+
"claude-opus-4-1-20250805",
|
|
57
|
+
"claude-opus-4-1",
|
|
58
|
+
"claude-opus-4-5",
|
|
59
|
+
"claude-opus-4-5-20251101",
|
|
60
|
+
"claude-sonnet-4-0",
|
|
61
|
+
"claude-sonnet-4-20250514",
|
|
62
|
+
"claude-sonnet-4-5-20250929",
|
|
63
|
+
"claude-sonnet-4-5",
|
|
64
|
+
"claude-sonnet-4-6",
|
|
65
|
+
"claude-opus-4-6"
|
|
66
|
+
];
|
|
67
|
+
const AnthropicKnownResponseModelsSchema = z.enum(ANTHROPIC_KNOWN_RESPONSE_MODELS);
|
|
68
|
+
const AnthropicResponseModelIdSchema = z.string();
|
|
69
|
+
const AnthropicCacheControlSchema = z.object({
|
|
70
|
+
type: z.literal("ephemeral"),
|
|
71
|
+
ttl: z.enum(["5m", "1h"]).optional()
|
|
72
|
+
});
|
|
73
|
+
const AnthropicTextContentSchema = z.object({
|
|
74
|
+
type: z.literal("text"),
|
|
75
|
+
text: z.string(),
|
|
76
|
+
citations: z.array(z.unknown()).optional(),
|
|
77
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
78
|
+
});
|
|
79
|
+
const AnthropicThinkingContentSchema = z.object({
|
|
80
|
+
type: z.literal("thinking"),
|
|
81
|
+
thinking: z.string(),
|
|
82
|
+
signature: z.string()
|
|
83
|
+
});
|
|
84
|
+
const AnthropicRedactedThinkingContentSchema = z.object({
|
|
85
|
+
type: z.literal("redacted_thinking"),
|
|
86
|
+
data: z.string()
|
|
87
|
+
});
|
|
88
|
+
const AnthropicContentSourceSchema = z.discriminatedUnion("type", [
|
|
89
|
+
z.object({
|
|
90
|
+
type: z.literal("base64"),
|
|
91
|
+
media_type: z.string(),
|
|
92
|
+
data: z.string()
|
|
93
|
+
}),
|
|
94
|
+
z.object({
|
|
95
|
+
type: z.literal("url"),
|
|
96
|
+
url: z.string()
|
|
97
|
+
}),
|
|
98
|
+
z.object({
|
|
99
|
+
type: z.literal("text"),
|
|
100
|
+
media_type: z.literal("text/plain"),
|
|
101
|
+
data: z.string()
|
|
102
|
+
})
|
|
103
|
+
]);
|
|
104
|
+
const AnthropicImageContentSchema = z.object({
|
|
105
|
+
type: z.literal("image"),
|
|
106
|
+
source: AnthropicContentSourceSchema,
|
|
107
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
108
|
+
});
|
|
109
|
+
const AnthropicDocumentContentSchema = z.object({
|
|
110
|
+
type: z.literal("document"),
|
|
111
|
+
source: AnthropicContentSourceSchema,
|
|
112
|
+
title: z.string().optional(),
|
|
113
|
+
context: z.string().optional(),
|
|
114
|
+
citations: z.object({ enabled: z.boolean() }).optional(),
|
|
115
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
116
|
+
});
|
|
117
|
+
const AnthropicToolResultContentSchema = z.object({
|
|
118
|
+
type: z.literal("tool_result"),
|
|
119
|
+
tool_use_id: z.string(),
|
|
120
|
+
content: z.union([z.string(), z.array(z.unknown())]),
|
|
121
|
+
is_error: z.boolean().optional(),
|
|
122
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
123
|
+
});
|
|
124
|
+
const AnthropicToolCallContentSchema = z.object({
|
|
125
|
+
type: z.literal("tool_use"),
|
|
126
|
+
id: z.string(),
|
|
127
|
+
name: z.string(),
|
|
128
|
+
input: z.unknown(),
|
|
129
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
130
|
+
});
|
|
131
|
+
const AnthropicServerToolUseContentSchema = z.object({
|
|
132
|
+
type: z.literal("server_tool_use"),
|
|
133
|
+
id: z.string(),
|
|
134
|
+
name: z.enum([
|
|
135
|
+
"web_fetch",
|
|
136
|
+
"web_search",
|
|
137
|
+
"code_execution",
|
|
138
|
+
"bash_code_execution",
|
|
139
|
+
"text_editor_code_execution",
|
|
140
|
+
"tool_search_tool_regex",
|
|
141
|
+
"tool_search_tool_bm25"
|
|
142
|
+
]),
|
|
143
|
+
input: z.unknown(),
|
|
144
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
145
|
+
});
|
|
146
|
+
const AnthropicProviderToolResultSchema = z.object({
|
|
147
|
+
tool_use_id: z.string(),
|
|
148
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
149
|
+
}).passthrough();
|
|
150
|
+
const AnthropicWebSearchToolResultContentSchema = AnthropicProviderToolResultSchema.extend({
|
|
151
|
+
type: z.literal("web_search_tool_result"),
|
|
152
|
+
content: z.array(z.object({
|
|
153
|
+
url: z.string(),
|
|
154
|
+
title: z.string().nullable(),
|
|
155
|
+
page_age: z.string().nullable(),
|
|
156
|
+
encrypted_content: z.string(),
|
|
157
|
+
type: z.string()
|
|
158
|
+
}))
|
|
159
|
+
});
|
|
160
|
+
const AnthropicWebFetchToolResultContentSchema = AnthropicProviderToolResultSchema.extend({
|
|
161
|
+
type: z.literal("web_fetch_tool_result"),
|
|
162
|
+
content: z.unknown()
|
|
163
|
+
});
|
|
164
|
+
const AnthropicToolSearchToolResultContentSchema = AnthropicProviderToolResultSchema.extend({
|
|
165
|
+
type: z.literal("tool_search_tool_result"),
|
|
166
|
+
content: z.unknown()
|
|
167
|
+
});
|
|
168
|
+
const AnthropicCodeExecutionToolResultContentSchema = AnthropicProviderToolResultSchema.extend({
|
|
169
|
+
type: z.literal("code_execution_tool_result"),
|
|
170
|
+
content: z.unknown()
|
|
171
|
+
});
|
|
172
|
+
const AnthropicBashCodeExecutionToolResultContentSchema = AnthropicProviderToolResultSchema.extend({
|
|
173
|
+
type: z.literal("bash_code_execution_tool_result"),
|
|
174
|
+
content: z.unknown()
|
|
175
|
+
});
|
|
176
|
+
const AnthropicTextEditorCodeExecutionToolResultContentSchema = AnthropicProviderToolResultSchema.extend({
|
|
177
|
+
type: z.literal("text_editor_code_execution_tool_result"),
|
|
178
|
+
content: z.unknown()
|
|
179
|
+
});
|
|
180
|
+
const AnthropicMcpToolUseContentSchema = z.object({
|
|
181
|
+
type: z.literal("mcp_tool_use"),
|
|
182
|
+
id: z.string(),
|
|
183
|
+
name: z.string(),
|
|
184
|
+
server_name: z.string().optional(),
|
|
185
|
+
input: z.unknown(),
|
|
186
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
187
|
+
}).passthrough();
|
|
188
|
+
const AnthropicMcpToolResultContentSchema = z.object({
|
|
189
|
+
type: z.literal("mcp_tool_result"),
|
|
190
|
+
tool_use_id: z.string(),
|
|
191
|
+
content: z.unknown(),
|
|
192
|
+
is_error: z.boolean().optional(),
|
|
193
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
194
|
+
}).passthrough();
|
|
195
|
+
const AnthropicCompactionContentSchema = z.object({
|
|
196
|
+
type: z.literal("compaction"),
|
|
197
|
+
content: z.string(),
|
|
198
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
199
|
+
});
|
|
200
|
+
const AnthropicResponseContentBlockSchema = z.discriminatedUnion("type", [
|
|
201
|
+
AnthropicTextContentSchema,
|
|
202
|
+
AnthropicThinkingContentSchema,
|
|
203
|
+
AnthropicRedactedThinkingContentSchema,
|
|
204
|
+
AnthropicImageContentSchema,
|
|
205
|
+
AnthropicDocumentContentSchema,
|
|
206
|
+
AnthropicToolResultContentSchema,
|
|
207
|
+
AnthropicToolCallContentSchema,
|
|
208
|
+
AnthropicServerToolUseContentSchema,
|
|
209
|
+
AnthropicWebSearchToolResultContentSchema,
|
|
210
|
+
AnthropicWebFetchToolResultContentSchema,
|
|
211
|
+
AnthropicToolSearchToolResultContentSchema,
|
|
212
|
+
AnthropicCodeExecutionToolResultContentSchema,
|
|
213
|
+
AnthropicBashCodeExecutionToolResultContentSchema,
|
|
214
|
+
AnthropicTextEditorCodeExecutionToolResultContentSchema,
|
|
215
|
+
AnthropicMcpToolUseContentSchema,
|
|
216
|
+
AnthropicMcpToolResultContentSchema,
|
|
217
|
+
AnthropicCompactionContentSchema
|
|
218
|
+
]);
|
|
219
|
+
const AnthropicMessageSchema = z.object({
|
|
220
|
+
role: z.enum(["user", "assistant"]),
|
|
221
|
+
content: z.array(z.union([
|
|
222
|
+
AnthropicTextContentSchema,
|
|
223
|
+
AnthropicImageContentSchema,
|
|
224
|
+
AnthropicDocumentContentSchema,
|
|
225
|
+
AnthropicToolResultContentSchema,
|
|
226
|
+
AnthropicThinkingContentSchema,
|
|
227
|
+
AnthropicRedactedThinkingContentSchema,
|
|
228
|
+
AnthropicToolCallContentSchema,
|
|
229
|
+
AnthropicServerToolUseContentSchema,
|
|
230
|
+
AnthropicWebSearchToolResultContentSchema,
|
|
231
|
+
AnthropicWebFetchToolResultContentSchema,
|
|
232
|
+
AnthropicToolSearchToolResultContentSchema,
|
|
233
|
+
AnthropicCodeExecutionToolResultContentSchema,
|
|
234
|
+
AnthropicBashCodeExecutionToolResultContentSchema,
|
|
235
|
+
AnthropicTextEditorCodeExecutionToolResultContentSchema,
|
|
236
|
+
AnthropicMcpToolUseContentSchema,
|
|
237
|
+
AnthropicMcpToolResultContentSchema,
|
|
238
|
+
AnthropicCompactionContentSchema
|
|
239
|
+
]))
|
|
240
|
+
});
|
|
241
|
+
const AnthropicMetadataSchema = z.object({ user_id: z.string().optional() });
|
|
242
|
+
const AnthropicThinkingConfigSchema = z.discriminatedUnion("type", [
|
|
243
|
+
z.object({ type: z.literal("adaptive") }),
|
|
244
|
+
z.object({
|
|
245
|
+
type: z.literal("enabled"),
|
|
246
|
+
budget_tokens: z.number().optional()
|
|
247
|
+
}),
|
|
248
|
+
z.object({ type: z.literal("disabled") })
|
|
249
|
+
]);
|
|
250
|
+
const AnthropicToolChoiceSchema = z.discriminatedUnion("type", [
|
|
251
|
+
z.object({
|
|
252
|
+
type: z.literal("auto"),
|
|
253
|
+
disable_parallel_tool_use: z.boolean().optional()
|
|
254
|
+
}),
|
|
255
|
+
z.object({
|
|
256
|
+
type: z.literal("any"),
|
|
257
|
+
disable_parallel_tool_use: z.boolean().optional()
|
|
258
|
+
}),
|
|
259
|
+
z.object({
|
|
260
|
+
type: z.literal("tool"),
|
|
261
|
+
name: z.string(),
|
|
262
|
+
disable_parallel_tool_use: z.boolean().optional()
|
|
263
|
+
})
|
|
264
|
+
]);
|
|
265
|
+
const AnthropicFunctionToolSchema = z.object({
|
|
266
|
+
name: z.string(),
|
|
267
|
+
description: z.string().optional(),
|
|
268
|
+
input_schema: z.record(z.string(), z.unknown()),
|
|
269
|
+
cache_control: AnthropicCacheControlSchema.optional(),
|
|
270
|
+
strict: z.boolean().optional(),
|
|
271
|
+
eager_input_streaming: z.boolean().optional(),
|
|
272
|
+
defer_loading: z.boolean().optional(),
|
|
273
|
+
allowed_callers: z.array(z.enum([
|
|
274
|
+
"direct",
|
|
275
|
+
"code_execution_20250825",
|
|
276
|
+
"code_execution_20260120"
|
|
277
|
+
])).optional(),
|
|
278
|
+
input_examples: z.array(z.unknown()).optional()
|
|
279
|
+
});
|
|
280
|
+
const AnthropicCodeExecution20250522ToolSchema = z.object({
|
|
281
|
+
type: z.literal("code_execution_20250522"),
|
|
282
|
+
name: z.literal("code_execution"),
|
|
283
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
284
|
+
});
|
|
285
|
+
const AnthropicCodeExecution20250825ToolSchema = z.object({
|
|
286
|
+
type: z.literal("code_execution_20250825"),
|
|
287
|
+
name: z.literal("code_execution")
|
|
288
|
+
});
|
|
289
|
+
const AnthropicCodeExecution20260120ToolSchema = z.object({
|
|
290
|
+
type: z.literal("code_execution_20260120"),
|
|
291
|
+
name: z.literal("code_execution")
|
|
292
|
+
});
|
|
293
|
+
const AnthropicComputerBaseToolSchema = z.object({
|
|
294
|
+
display_width_px: z.number().int(),
|
|
295
|
+
display_height_px: z.number().int(),
|
|
296
|
+
display_number: z.number().int().optional(),
|
|
297
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
298
|
+
});
|
|
299
|
+
const AnthropicComputer20241022ToolSchema = AnthropicComputerBaseToolSchema.extend({
|
|
300
|
+
type: z.literal("computer_20241022"),
|
|
301
|
+
name: z.literal("computer")
|
|
302
|
+
});
|
|
303
|
+
const AnthropicComputer20250124ToolSchema = AnthropicComputerBaseToolSchema.extend({
|
|
304
|
+
type: z.literal("computer_20250124"),
|
|
305
|
+
name: z.literal("computer")
|
|
306
|
+
});
|
|
307
|
+
const AnthropicComputer20251124ToolSchema = AnthropicComputerBaseToolSchema.extend({
|
|
308
|
+
type: z.literal("computer_20251124"),
|
|
309
|
+
name: z.literal("computer"),
|
|
310
|
+
enable_zoom: z.boolean().optional()
|
|
311
|
+
});
|
|
312
|
+
const AnthropicTextEditor20241022ToolSchema = z.object({
|
|
313
|
+
type: z.literal("text_editor_20241022"),
|
|
314
|
+
name: z.literal("str_replace_editor"),
|
|
315
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
316
|
+
});
|
|
317
|
+
const AnthropicTextEditor20250124ToolSchema = z.object({
|
|
318
|
+
type: z.literal("text_editor_20250124"),
|
|
319
|
+
name: z.literal("str_replace_editor"),
|
|
320
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
321
|
+
});
|
|
322
|
+
const AnthropicTextEditor20250429ToolSchema = z.object({
|
|
323
|
+
type: z.literal("text_editor_20250429"),
|
|
324
|
+
name: z.literal("str_replace_based_edit_tool"),
|
|
325
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
326
|
+
});
|
|
327
|
+
const AnthropicTextEditor20250728ToolSchema = z.object({
|
|
328
|
+
type: z.literal("text_editor_20250728"),
|
|
329
|
+
name: z.literal("str_replace_based_edit_tool"),
|
|
330
|
+
max_characters: z.number().optional(),
|
|
331
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
332
|
+
});
|
|
333
|
+
const AnthropicBash20241022ToolSchema = z.object({
|
|
334
|
+
type: z.literal("bash_20241022"),
|
|
335
|
+
name: z.literal("bash"),
|
|
336
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
337
|
+
});
|
|
338
|
+
const AnthropicBash20250124ToolSchema = z.object({
|
|
339
|
+
type: z.literal("bash_20250124"),
|
|
340
|
+
name: z.literal("bash"),
|
|
341
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
342
|
+
});
|
|
343
|
+
const AnthropicMemory20250818ToolSchema = z.object({
|
|
344
|
+
type: z.literal("memory_20250818"),
|
|
345
|
+
name: z.literal("memory")
|
|
346
|
+
});
|
|
347
|
+
const AnthropicWebSearchUserLocationSchema = z.object({
|
|
348
|
+
type: z.literal("approximate"),
|
|
349
|
+
city: z.string().optional(),
|
|
350
|
+
region: z.string().optional(),
|
|
351
|
+
country: z.string().optional(),
|
|
352
|
+
timezone: z.string().optional()
|
|
353
|
+
});
|
|
354
|
+
const AnthropicWebSearch20250305ToolSchema = z.object({
|
|
355
|
+
type: z.literal("web_search_20250305"),
|
|
356
|
+
name: z.literal("web_search"),
|
|
357
|
+
max_uses: z.number().optional(),
|
|
358
|
+
allowed_domains: z.array(z.string()).optional(),
|
|
359
|
+
blocked_domains: z.array(z.string()).optional(),
|
|
360
|
+
user_location: AnthropicWebSearchUserLocationSchema.optional(),
|
|
361
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
362
|
+
});
|
|
363
|
+
const AnthropicWebSearch20260209ToolSchema = z.object({
|
|
364
|
+
type: z.literal("web_search_20260209"),
|
|
365
|
+
name: z.literal("web_search"),
|
|
366
|
+
max_uses: z.number().optional(),
|
|
367
|
+
allowed_domains: z.array(z.string()).optional(),
|
|
368
|
+
blocked_domains: z.array(z.string()).optional(),
|
|
369
|
+
user_location: AnthropicWebSearchUserLocationSchema.optional(),
|
|
370
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
371
|
+
});
|
|
372
|
+
const AnthropicWebFetchBaseToolSchema = z.object({
|
|
373
|
+
max_uses: z.number().optional(),
|
|
374
|
+
allowed_domains: z.array(z.string()).optional(),
|
|
375
|
+
blocked_domains: z.array(z.string()).optional(),
|
|
376
|
+
citations: z.object({ enabled: z.boolean() }).optional(),
|
|
377
|
+
max_content_tokens: z.number().optional(),
|
|
378
|
+
cache_control: AnthropicCacheControlSchema.optional()
|
|
379
|
+
});
|
|
380
|
+
const AnthropicWebFetch20250910ToolSchema = AnthropicWebFetchBaseToolSchema.extend({
|
|
381
|
+
type: z.literal("web_fetch_20250910"),
|
|
382
|
+
name: z.literal("web_fetch")
|
|
383
|
+
});
|
|
384
|
+
const AnthropicWebFetch20260209ToolSchema = AnthropicWebFetchBaseToolSchema.extend({
|
|
385
|
+
type: z.literal("web_fetch_20260209"),
|
|
386
|
+
name: z.literal("web_fetch")
|
|
387
|
+
});
|
|
388
|
+
const AnthropicToolSearchRegex20251119ToolSchema = z.object({
|
|
389
|
+
type: z.literal("tool_search_tool_regex_20251119"),
|
|
390
|
+
name: z.literal("tool_search_tool_regex")
|
|
391
|
+
});
|
|
392
|
+
const AnthropicToolSearchBm2520251119ToolSchema = z.object({
|
|
393
|
+
type: z.literal("tool_search_tool_bm25_20251119"),
|
|
394
|
+
name: z.literal("tool_search_tool_bm25")
|
|
395
|
+
});
|
|
396
|
+
const AnthropicHostedToolRequestSchema = z.union([
|
|
397
|
+
AnthropicCodeExecution20250522ToolSchema,
|
|
398
|
+
AnthropicCodeExecution20250825ToolSchema,
|
|
399
|
+
AnthropicCodeExecution20260120ToolSchema,
|
|
400
|
+
AnthropicComputer20241022ToolSchema,
|
|
401
|
+
AnthropicComputer20250124ToolSchema,
|
|
402
|
+
AnthropicComputer20251124ToolSchema,
|
|
403
|
+
AnthropicTextEditor20241022ToolSchema,
|
|
404
|
+
AnthropicTextEditor20250124ToolSchema,
|
|
405
|
+
AnthropicTextEditor20250429ToolSchema,
|
|
406
|
+
AnthropicTextEditor20250728ToolSchema,
|
|
407
|
+
AnthropicBash20241022ToolSchema,
|
|
408
|
+
AnthropicBash20250124ToolSchema,
|
|
409
|
+
AnthropicMemory20250818ToolSchema,
|
|
410
|
+
AnthropicWebSearch20250305ToolSchema,
|
|
411
|
+
AnthropicWebSearch20260209ToolSchema,
|
|
412
|
+
AnthropicWebFetch20250910ToolSchema,
|
|
413
|
+
AnthropicWebFetch20260209ToolSchema,
|
|
414
|
+
AnthropicToolSearchRegex20251119ToolSchema,
|
|
415
|
+
AnthropicToolSearchBm2520251119ToolSchema
|
|
416
|
+
]);
|
|
417
|
+
const AnthropicMessagesToolSchema = z.union([AnthropicFunctionToolSchema, AnthropicHostedToolRequestSchema]);
|
|
418
|
+
const AnthropicMessagesRequestSchema = z.object({
|
|
419
|
+
model: AnthropicResponseModelIdSchema,
|
|
420
|
+
max_tokens: z.number().int().positive(),
|
|
421
|
+
messages: z.array(AnthropicMessageSchema),
|
|
422
|
+
system: z.union([z.string(), z.array(AnthropicTextContentSchema)]).optional(),
|
|
423
|
+
cache_control: AnthropicCacheControlSchema.optional(),
|
|
424
|
+
metadata: AnthropicMetadataSchema.optional(),
|
|
425
|
+
output_config: z.object({
|
|
426
|
+
effort: z.enum([
|
|
427
|
+
"low",
|
|
428
|
+
"medium",
|
|
429
|
+
"high",
|
|
430
|
+
"max"
|
|
431
|
+
]).optional(),
|
|
432
|
+
format: z.object({
|
|
433
|
+
type: z.literal("json_schema"),
|
|
434
|
+
schema: z.record(z.string(), z.unknown())
|
|
435
|
+
}).optional()
|
|
436
|
+
}).optional(),
|
|
437
|
+
stop_sequences: z.array(z.string()).optional(),
|
|
438
|
+
stream: z.boolean().optional(),
|
|
439
|
+
speed: z.enum(["fast", "standard"]).optional(),
|
|
440
|
+
temperature: z.number().optional(),
|
|
441
|
+
thinking: AnthropicThinkingConfigSchema.optional(),
|
|
442
|
+
tool_choice: AnthropicToolChoiceSchema.optional(),
|
|
443
|
+
tools: z.array(AnthropicMessagesToolSchema).optional(),
|
|
444
|
+
top_k: z.number().optional(),
|
|
445
|
+
top_p: z.number().optional(),
|
|
446
|
+
mcp_servers: z.array(z.object({
|
|
447
|
+
type: z.literal("url"),
|
|
448
|
+
name: z.string(),
|
|
449
|
+
url: z.string(),
|
|
450
|
+
authorization_token: z.string().nullish(),
|
|
451
|
+
tool_configuration: z.object({
|
|
452
|
+
enabled: z.boolean().nullish(),
|
|
453
|
+
allowed_tools: z.array(z.string()).nullish()
|
|
454
|
+
}).nullish()
|
|
455
|
+
})).optional(),
|
|
456
|
+
container: z.object({
|
|
457
|
+
id: z.string().optional(),
|
|
458
|
+
skills: z.array(z.object({
|
|
459
|
+
type: z.enum(["anthropic", "custom"]),
|
|
460
|
+
skill_id: z.string(),
|
|
461
|
+
version: z.string().optional()
|
|
462
|
+
})).optional()
|
|
463
|
+
}).optional(),
|
|
464
|
+
context_management: z.object({ edits: z.array(z.unknown()) }).optional()
|
|
465
|
+
}).passthrough();
|
|
466
|
+
const AnthropicUsageSchema = z.object({
|
|
467
|
+
input_tokens: z.number().int().optional(),
|
|
468
|
+
output_tokens: z.number().int().optional(),
|
|
469
|
+
cache_creation_input_tokens: z.number().int().optional(),
|
|
470
|
+
cache_read_input_tokens: z.number().int().optional(),
|
|
471
|
+
iterations: z.array(z.object({
|
|
472
|
+
type: z.enum(["compaction", "message"]),
|
|
473
|
+
input_tokens: z.number().int(),
|
|
474
|
+
output_tokens: z.number().int()
|
|
475
|
+
})).optional()
|
|
476
|
+
});
|
|
477
|
+
const AnthropicMessagesResponseSchema = z.object({
|
|
478
|
+
id: z.string(),
|
|
479
|
+
type: z.literal("message"),
|
|
480
|
+
role: z.literal("assistant"),
|
|
481
|
+
model: AnthropicResponseModelIdSchema,
|
|
482
|
+
content: z.array(AnthropicResponseContentBlockSchema),
|
|
483
|
+
stop_reason: z.string().nullable().optional(),
|
|
484
|
+
stop_sequence: z.string().nullable().optional(),
|
|
485
|
+
usage: AnthropicUsageSchema.default({})
|
|
486
|
+
}).passthrough();
|
|
487
|
+
const AnthropicContentBlockDeltaSchema = z.discriminatedUnion("type", [
|
|
488
|
+
z.object({
|
|
489
|
+
type: z.literal("text_delta"),
|
|
490
|
+
text: z.string()
|
|
491
|
+
}),
|
|
492
|
+
z.object({
|
|
493
|
+
type: z.literal("thinking_delta"),
|
|
494
|
+
thinking: z.string()
|
|
495
|
+
}),
|
|
496
|
+
z.object({
|
|
497
|
+
type: z.literal("input_json_delta"),
|
|
498
|
+
partial_json: z.string()
|
|
499
|
+
}),
|
|
500
|
+
z.object({
|
|
501
|
+
type: z.literal("signature_delta"),
|
|
502
|
+
signature: z.string()
|
|
503
|
+
}),
|
|
504
|
+
z.object({
|
|
505
|
+
type: z.literal("citations_delta"),
|
|
506
|
+
citation: z.unknown()
|
|
507
|
+
}),
|
|
508
|
+
z.object({
|
|
509
|
+
type: z.literal("compaction_delta"),
|
|
510
|
+
content: z.string().optional()
|
|
511
|
+
})
|
|
512
|
+
]);
|
|
513
|
+
const AnthropicResponseStreamEventSchema = z.discriminatedUnion("type", [
|
|
514
|
+
z.object({ type: z.literal("ping") }),
|
|
515
|
+
z.object({
|
|
516
|
+
type: z.literal("message_start"),
|
|
517
|
+
message: AnthropicMessagesResponseSchema
|
|
518
|
+
}),
|
|
519
|
+
z.object({
|
|
520
|
+
type: z.literal("message_delta"),
|
|
521
|
+
delta: z.object({
|
|
522
|
+
stop_reason: z.string().nullable().optional(),
|
|
523
|
+
stop_sequence: z.string().nullable().optional()
|
|
524
|
+
}).passthrough(),
|
|
525
|
+
usage: z.object({
|
|
526
|
+
input_tokens: z.number().int().optional(),
|
|
527
|
+
output_tokens: z.number().int().optional(),
|
|
528
|
+
cache_creation_input_tokens: z.number().int().optional(),
|
|
529
|
+
cache_read_input_tokens: z.number().int().optional(),
|
|
530
|
+
iterations: z.array(z.object({
|
|
531
|
+
type: z.enum(["compaction", "message"]),
|
|
532
|
+
input_tokens: z.number().int(),
|
|
533
|
+
output_tokens: z.number().int()
|
|
534
|
+
})).optional()
|
|
535
|
+
}).passthrough().optional()
|
|
536
|
+
}),
|
|
537
|
+
z.object({ type: z.literal("message_stop") }),
|
|
538
|
+
z.object({
|
|
539
|
+
type: z.literal("content_block_start"),
|
|
540
|
+
index: z.number().int(),
|
|
541
|
+
content_block: AnthropicResponseContentBlockSchema
|
|
542
|
+
}),
|
|
543
|
+
z.object({
|
|
544
|
+
type: z.literal("content_block_delta"),
|
|
545
|
+
index: z.number().int(),
|
|
546
|
+
delta: AnthropicContentBlockDeltaSchema
|
|
547
|
+
}),
|
|
548
|
+
z.object({
|
|
549
|
+
type: z.literal("content_block_stop"),
|
|
550
|
+
index: z.number().int()
|
|
551
|
+
}),
|
|
552
|
+
z.object({
|
|
553
|
+
type: z.literal("error"),
|
|
554
|
+
error: z.object({
|
|
555
|
+
type: z.string().optional(),
|
|
556
|
+
message: z.string().optional()
|
|
557
|
+
}).passthrough()
|
|
558
|
+
})
|
|
559
|
+
]);
|
|
560
|
+
|
|
561
|
+
//#endregion
|
|
562
|
+
//#region src/anthropic/client/stream.ts
|
|
563
|
+
const parseSSERecord = (record) => {
|
|
564
|
+
const lines = record.split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean);
|
|
565
|
+
if (!lines.length) return ok(null);
|
|
566
|
+
const data = lines.filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
567
|
+
if (!data) return ok(null);
|
|
568
|
+
if (data === "[DONE]") return ok(null);
|
|
569
|
+
const parsed = safeJsonParse(data);
|
|
570
|
+
if (parsed.isErr()) return err(BetterAgentError.wrap({
|
|
571
|
+
err: parsed.error,
|
|
572
|
+
message: "Anthropic stream returned invalid JSON",
|
|
573
|
+
opts: {
|
|
574
|
+
code: "UPSTREAM_FAILED",
|
|
575
|
+
context: {
|
|
576
|
+
provider: "anthropic",
|
|
577
|
+
raw: data
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}).at({ at: "anthropic.stream.parse" }));
|
|
581
|
+
const event = AnthropicResponseStreamEventSchema.safeParse(parsed.value);
|
|
582
|
+
if (!event.success) return err(BetterAgentError.fromCode("UPSTREAM_FAILED", "Anthropic stream returned an unknown event payload", { context: {
|
|
583
|
+
provider: "anthropic",
|
|
584
|
+
issues: event.error.issues
|
|
585
|
+
} }).at({ at: "anthropic.stream.validate" }));
|
|
586
|
+
return ok(event.data);
|
|
587
|
+
};
|
|
588
|
+
async function* parseAnthropicSSEStream(stream) {
|
|
589
|
+
const reader = stream.getReader();
|
|
590
|
+
const decoder = new TextDecoder();
|
|
591
|
+
let buffer = "";
|
|
592
|
+
try {
|
|
593
|
+
while (true) {
|
|
594
|
+
const { done, value } = await reader.read();
|
|
595
|
+
if (done) break;
|
|
596
|
+
buffer += decoder.decode(value, { stream: true });
|
|
597
|
+
while (true) {
|
|
598
|
+
const recordBoundary = buffer.indexOf("\n\n");
|
|
599
|
+
if (recordBoundary === -1) break;
|
|
600
|
+
const record = buffer.slice(0, recordBoundary);
|
|
601
|
+
buffer = buffer.slice(recordBoundary + 2);
|
|
602
|
+
const parsed = parseSSERecord(record);
|
|
603
|
+
if (parsed.isErr()) {
|
|
604
|
+
yield err(parsed.error);
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (parsed.value) yield ok(parsed.value);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
buffer += decoder.decode();
|
|
611
|
+
const remaining = buffer.trim();
|
|
612
|
+
if (!remaining) return;
|
|
613
|
+
const parsed = parseSSERecord(remaining);
|
|
614
|
+
if (parsed.isErr()) {
|
|
615
|
+
yield err(parsed.error);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if (parsed.value) yield ok(parsed.value);
|
|
619
|
+
} finally {
|
|
620
|
+
reader.releaseLock();
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/anthropic/client/create-client.ts
|
|
626
|
+
const createAnthropicClient = (config = {}) => {
|
|
627
|
+
const baseUrl = (config.baseURL ?? "https://api.anthropic.com/v1").replace(/\/+$/, "");
|
|
628
|
+
const post = async (path, body, at, options) => {
|
|
629
|
+
try {
|
|
630
|
+
const result = await baFetch(`${baseUrl}${path}`, {
|
|
631
|
+
method: "POST",
|
|
632
|
+
body: JSON.stringify(body),
|
|
633
|
+
headers: {
|
|
634
|
+
...buildAnthropicHeaders(config, options),
|
|
635
|
+
"Content-Type": "application/json"
|
|
636
|
+
},
|
|
637
|
+
signal: options?.signal ?? null,
|
|
638
|
+
throw: false
|
|
639
|
+
});
|
|
640
|
+
if (result.error) return err(mapAnthropicHttpError(result.error, {
|
|
641
|
+
at,
|
|
642
|
+
path
|
|
643
|
+
}));
|
|
644
|
+
if (!result.data) return err(BetterAgentError.fromCode("UPSTREAM_FAILED", "Anthropic returned no data", { context: { provider: "anthropic" } }).at({
|
|
645
|
+
at,
|
|
646
|
+
data: { path }
|
|
647
|
+
}));
|
|
648
|
+
return ok(result.data);
|
|
649
|
+
} catch (e) {
|
|
650
|
+
return err(BetterAgentError.wrap({
|
|
651
|
+
err: e,
|
|
652
|
+
message: "Anthropic request failed",
|
|
653
|
+
opts: {
|
|
654
|
+
code: "UPSTREAM_FAILED",
|
|
655
|
+
context: { provider: "anthropic" }
|
|
656
|
+
}
|
|
657
|
+
}).at({
|
|
658
|
+
at,
|
|
659
|
+
data: { path }
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
const stream = async (path, body, at, options) => {
|
|
664
|
+
try {
|
|
665
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
666
|
+
method: "POST",
|
|
667
|
+
body: JSON.stringify(body),
|
|
668
|
+
headers: {
|
|
669
|
+
...buildAnthropicHeaders(config, options),
|
|
670
|
+
Accept: "text/event-stream",
|
|
671
|
+
"Content-Type": "application/json"
|
|
672
|
+
},
|
|
673
|
+
signal: options?.signal ?? null
|
|
674
|
+
});
|
|
675
|
+
if (!response.ok) {
|
|
676
|
+
const parsed = safeJsonParse(await response.text());
|
|
677
|
+
const error = parsed.isOk() && parsed.value && typeof parsed.value === "object" ? parsed.value.error ? parsed.value : { error: parsed.value } : void 0;
|
|
678
|
+
return err(mapAnthropicHttpError({
|
|
679
|
+
status: response.status,
|
|
680
|
+
statusText: response.statusText,
|
|
681
|
+
error: error?.error
|
|
682
|
+
}, {
|
|
683
|
+
at,
|
|
684
|
+
path
|
|
685
|
+
}));
|
|
686
|
+
}
|
|
687
|
+
if (!response.body) return err(BetterAgentError.fromCode("UPSTREAM_FAILED", "Anthropic stream response did not include a body", { context: { provider: "anthropic" } }).at({
|
|
688
|
+
at,
|
|
689
|
+
data: { path }
|
|
690
|
+
}));
|
|
691
|
+
return ok(parseAnthropicSSEStream(response.body));
|
|
692
|
+
} catch (e) {
|
|
693
|
+
return err(BetterAgentError.wrap({
|
|
694
|
+
err: e,
|
|
695
|
+
message: "Anthropic streaming request failed",
|
|
696
|
+
opts: {
|
|
697
|
+
code: "UPSTREAM_FAILED",
|
|
698
|
+
context: { provider: "anthropic" }
|
|
699
|
+
}
|
|
700
|
+
}).at({
|
|
701
|
+
at,
|
|
702
|
+
data: { path }
|
|
703
|
+
}));
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
return { messages: {
|
|
707
|
+
create: (body, options) => post("/messages", body, "anthropic.messages.create", options),
|
|
708
|
+
stream: (body, options) => stream("/messages", body, "anthropic.messages.stream", options)
|
|
709
|
+
} };
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region src/anthropic/tools/index.ts
|
|
714
|
+
function createAnthropicNativeToolBuilders() {
|
|
715
|
+
return {
|
|
716
|
+
codeExecution_20250522: (config = {}) => createNativeTool("code_execution_20250522", config),
|
|
717
|
+
codeExecution_20250825: (config = {}) => createNativeTool("code_execution_20250825", config),
|
|
718
|
+
codeExecution_20260120: (config = {}) => createNativeTool("code_execution_20260120", config),
|
|
719
|
+
computer_20241022: (config) => createNativeTool("computer_20241022", config),
|
|
720
|
+
computer_20250124: (config) => createNativeTool("computer_20250124", config),
|
|
721
|
+
computer_20251124: (config) => createNativeTool("computer_20251124", config),
|
|
722
|
+
textEditor_20241022: (config = {}) => createNativeTool("text_editor_20241022", config),
|
|
723
|
+
textEditor_20250124: (config = {}) => createNativeTool("text_editor_20250124", config),
|
|
724
|
+
textEditor_20250429: (config = {}) => createNativeTool("text_editor_20250429", config),
|
|
725
|
+
textEditor_20250728: (config = {}) => createNativeTool("text_editor_20250728", config),
|
|
726
|
+
bash_20241022: (config = {}) => createNativeTool("bash_20241022", config),
|
|
727
|
+
bash_20250124: (config = {}) => createNativeTool("bash_20250124", config),
|
|
728
|
+
memory_20250818: (config = {}) => createNativeTool("memory_20250818", config),
|
|
729
|
+
webSearch_20250305: (config = {}) => createNativeTool("web_search_20250305", config),
|
|
730
|
+
webSearch_20260209: (config = {}) => createNativeTool("web_search_20260209", config),
|
|
731
|
+
webFetch_20250910: (config = {}) => createNativeTool("web_fetch_20250910", config),
|
|
732
|
+
webFetch_20260209: (config = {}) => createNativeTool("web_fetch_20260209", config),
|
|
733
|
+
toolSearchRegex_20251119: (config = {}) => createNativeTool("tool_search_tool_regex_20251119", config),
|
|
734
|
+
toolSearchBm25_20251119: (config = {}) => createNativeTool("tool_search_tool_bm25_20251119", config)
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
function createNativeTool(type, config) {
|
|
738
|
+
return {
|
|
739
|
+
kind: "hosted",
|
|
740
|
+
provider: "anthropic",
|
|
741
|
+
type,
|
|
742
|
+
config
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function isAnthropicNativeToolDefinition(tool) {
|
|
746
|
+
if (!tool || typeof tool !== "object") return false;
|
|
747
|
+
const t = tool;
|
|
748
|
+
return t.kind === "hosted" && t.provider === "anthropic" && typeof t.type === "string";
|
|
749
|
+
}
|
|
750
|
+
function mapAnthropicNativeToolToRequest(tool) {
|
|
751
|
+
switch (tool.type) {
|
|
752
|
+
case "code_execution_20250522": return {
|
|
753
|
+
type: "code_execution_20250522",
|
|
754
|
+
name: "code_execution",
|
|
755
|
+
...tool.config
|
|
756
|
+
};
|
|
757
|
+
case "code_execution_20250825": return {
|
|
758
|
+
type: "code_execution_20250825",
|
|
759
|
+
name: "code_execution",
|
|
760
|
+
...tool.config
|
|
761
|
+
};
|
|
762
|
+
case "code_execution_20260120": return {
|
|
763
|
+
type: "code_execution_20260120",
|
|
764
|
+
name: "code_execution",
|
|
765
|
+
...tool.config
|
|
766
|
+
};
|
|
767
|
+
case "computer_20241022": return {
|
|
768
|
+
type: "computer_20241022",
|
|
769
|
+
name: "computer",
|
|
770
|
+
...tool.config
|
|
771
|
+
};
|
|
772
|
+
case "computer_20250124": return {
|
|
773
|
+
type: "computer_20250124",
|
|
774
|
+
name: "computer",
|
|
775
|
+
...tool.config
|
|
776
|
+
};
|
|
777
|
+
case "computer_20251124": return {
|
|
778
|
+
type: "computer_20251124",
|
|
779
|
+
name: "computer",
|
|
780
|
+
...tool.config
|
|
781
|
+
};
|
|
782
|
+
case "text_editor_20241022": return {
|
|
783
|
+
type: "text_editor_20241022",
|
|
784
|
+
name: "str_replace_editor",
|
|
785
|
+
...tool.config
|
|
786
|
+
};
|
|
787
|
+
case "text_editor_20250124": return {
|
|
788
|
+
type: "text_editor_20250124",
|
|
789
|
+
name: "str_replace_editor",
|
|
790
|
+
...tool.config
|
|
791
|
+
};
|
|
792
|
+
case "text_editor_20250429": return {
|
|
793
|
+
type: "text_editor_20250429",
|
|
794
|
+
name: "str_replace_based_edit_tool",
|
|
795
|
+
...tool.config
|
|
796
|
+
};
|
|
797
|
+
case "text_editor_20250728": return {
|
|
798
|
+
type: "text_editor_20250728",
|
|
799
|
+
name: "str_replace_based_edit_tool",
|
|
800
|
+
...tool.config
|
|
801
|
+
};
|
|
802
|
+
case "bash_20241022": return {
|
|
803
|
+
type: "bash_20241022",
|
|
804
|
+
name: "bash",
|
|
805
|
+
...tool.config
|
|
806
|
+
};
|
|
807
|
+
case "bash_20250124": return {
|
|
808
|
+
type: "bash_20250124",
|
|
809
|
+
name: "bash",
|
|
810
|
+
...tool.config
|
|
811
|
+
};
|
|
812
|
+
case "memory_20250818": return {
|
|
813
|
+
type: "memory_20250818",
|
|
814
|
+
name: "memory",
|
|
815
|
+
...tool.config
|
|
816
|
+
};
|
|
817
|
+
case "web_search_20250305": return {
|
|
818
|
+
type: "web_search_20250305",
|
|
819
|
+
name: "web_search",
|
|
820
|
+
...tool.config
|
|
821
|
+
};
|
|
822
|
+
case "web_search_20260209": return {
|
|
823
|
+
type: "web_search_20260209",
|
|
824
|
+
name: "web_search",
|
|
825
|
+
...tool.config
|
|
826
|
+
};
|
|
827
|
+
case "web_fetch_20250910": return {
|
|
828
|
+
type: "web_fetch_20250910",
|
|
829
|
+
name: "web_fetch",
|
|
830
|
+
...tool.config
|
|
831
|
+
};
|
|
832
|
+
case "web_fetch_20260209": return {
|
|
833
|
+
type: "web_fetch_20260209",
|
|
834
|
+
name: "web_fetch",
|
|
835
|
+
...tool.config
|
|
836
|
+
};
|
|
837
|
+
case "tool_search_tool_regex_20251119": return {
|
|
838
|
+
type: "tool_search_tool_regex_20251119",
|
|
839
|
+
name: "tool_search_tool_regex",
|
|
840
|
+
...tool.config
|
|
841
|
+
};
|
|
842
|
+
case "tool_search_tool_bm25_20251119": return {
|
|
843
|
+
type: "tool_search_tool_bm25_20251119",
|
|
844
|
+
name: "tool_search_tool_bm25",
|
|
845
|
+
...tool.config
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
//#endregion
|
|
851
|
+
//#region src/anthropic/responses/mappers.ts
|
|
852
|
+
/**
|
|
853
|
+
* Keys explicitly handled by the Anthropic responses mapper.
|
|
854
|
+
*/
|
|
855
|
+
const ANTHROPIC_RESPONSE_KNOWN_KEYS = new Set([
|
|
856
|
+
"input",
|
|
857
|
+
"tools",
|
|
858
|
+
"toolChoice",
|
|
859
|
+
"modalities",
|
|
860
|
+
"structured_output",
|
|
861
|
+
"anthropicBeta",
|
|
862
|
+
"cacheControl",
|
|
863
|
+
"container",
|
|
864
|
+
"contextManagement",
|
|
865
|
+
"disableParallelToolUse",
|
|
866
|
+
"effort",
|
|
867
|
+
"max_tokens",
|
|
868
|
+
"mcpServers",
|
|
869
|
+
"metadata",
|
|
870
|
+
"speed",
|
|
871
|
+
"stop_sequences",
|
|
872
|
+
"structuredOutputMode",
|
|
873
|
+
"temperature",
|
|
874
|
+
"thinking",
|
|
875
|
+
"toolStreaming",
|
|
876
|
+
"top_k",
|
|
877
|
+
"top_p"
|
|
878
|
+
]);
|
|
879
|
+
const DEFAULT_MAX_TOKENS = 4096;
|
|
880
|
+
const PDF_INPUT_BETA = "pdfs-2024-09-25";
|
|
881
|
+
const FINE_GRAINED_TOOL_STREAMING_BETA = "fine-grained-tool-streaming-2025-05-14";
|
|
882
|
+
const normalizeProviderToolName = (name) => {
|
|
883
|
+
if (name === "bash_code_execution" || name === "text_editor_code_execution") return "code_execution";
|
|
884
|
+
return name;
|
|
885
|
+
};
|
|
886
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
887
|
+
const decodeBase64Text = (data) => Buffer.from(data, "base64").toString("utf8");
|
|
888
|
+
const isUrlSource = (source) => isRecord(source) && source.kind === "url" && typeof source.url === "string";
|
|
889
|
+
const isBase64Source = (source) => isRecord(source) && source.kind === "base64" && typeof source.data === "string" && typeof source.mimeType === "string";
|
|
890
|
+
const buildBinarySource = (source) => {
|
|
891
|
+
if (isUrlSource(source)) return {
|
|
892
|
+
type: "url",
|
|
893
|
+
url: source.url
|
|
894
|
+
};
|
|
895
|
+
if (isBase64Source(source)) return {
|
|
896
|
+
type: "base64",
|
|
897
|
+
media_type: source.mimeType,
|
|
898
|
+
data: source.data
|
|
899
|
+
};
|
|
900
|
+
return null;
|
|
901
|
+
};
|
|
902
|
+
const serializeToolResultContent = (result) => typeof result === "string" ? result : JSON.stringify(result);
|
|
903
|
+
const parseMaybeJson = (value) => {
|
|
904
|
+
if (!value.trim()) return {};
|
|
905
|
+
const parsed = safeJsonParse(value);
|
|
906
|
+
return parsed.isOk() ? parsed.value : value;
|
|
907
|
+
};
|
|
908
|
+
const getAnthropicModelCapabilities = (modelId) => {
|
|
909
|
+
if (modelId.includes("claude-sonnet-4-6") || modelId.includes("claude-opus-4-6")) return { supportsStructuredOutput: true };
|
|
910
|
+
if (modelId.includes("claude-sonnet-4-5") || modelId.includes("claude-opus-4-5") || modelId.includes("claude-haiku-4-5")) return { supportsStructuredOutput: true };
|
|
911
|
+
if (modelId.includes("claude-opus-4-1")) return { supportsStructuredOutput: true };
|
|
912
|
+
return { supportsStructuredOutput: false };
|
|
913
|
+
};
|
|
914
|
+
const getAnthropicPartProviderMetadata = (part) => {
|
|
915
|
+
const providerMetadata = part.providerMetadata;
|
|
916
|
+
if (!isRecord(providerMetadata)) return void 0;
|
|
917
|
+
const anthropic = providerMetadata.anthropic;
|
|
918
|
+
if (!isRecord(anthropic)) return void 0;
|
|
919
|
+
return {
|
|
920
|
+
cacheControl: isRecord(anthropic.cacheControl) && anthropic.cacheControl.type === "ephemeral" && (anthropic.cacheControl.ttl == null || anthropic.cacheControl.ttl === "5m" || anthropic.cacheControl.ttl === "1h") ? {
|
|
921
|
+
type: "ephemeral",
|
|
922
|
+
...anthropic.cacheControl.ttl != null ? { ttl: anthropic.cacheControl.ttl } : {}
|
|
923
|
+
} : void 0,
|
|
924
|
+
citations: isRecord(anthropic.citations) && typeof anthropic.citations.enabled === "boolean" ? { enabled: anthropic.citations.enabled } : void 0,
|
|
925
|
+
context: typeof anthropic.context === "string" ? anthropic.context : void 0,
|
|
926
|
+
title: typeof anthropic.title === "string" ? anthropic.title : void 0
|
|
927
|
+
};
|
|
928
|
+
};
|
|
929
|
+
const getAnthropicTextProviderMetadata = (citations) => citations?.length ? { anthropic: { citations } } : void 0;
|
|
930
|
+
const mapAnthropicStopReason = (stopReason, isJsonResponseFromTool) => {
|
|
931
|
+
switch (stopReason) {
|
|
932
|
+
case "pause_turn":
|
|
933
|
+
case "end_turn":
|
|
934
|
+
case "stop_sequence": return "stop";
|
|
935
|
+
case "refusal": return "content-filter";
|
|
936
|
+
case "tool_use": return isJsonResponseFromTool ? "stop" : "tool-calls";
|
|
937
|
+
case "max_tokens":
|
|
938
|
+
case "model_context_window_exceeded": return "length";
|
|
939
|
+
case "compaction": return "other";
|
|
940
|
+
default: return "other";
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
const mapAnthropicUsage = (usage) => {
|
|
944
|
+
const cacheCreation = usage?.cache_creation_input_tokens ?? 0;
|
|
945
|
+
const cacheRead = usage?.cache_read_input_tokens ?? 0;
|
|
946
|
+
const iteratedInput = usage?.iterations?.reduce((sum, item) => sum + item.input_tokens, 0) ?? usage?.input_tokens;
|
|
947
|
+
const iteratedOutput = usage?.iterations?.reduce((sum, item) => sum + item.output_tokens, 0) ?? usage?.output_tokens;
|
|
948
|
+
const inputTokens = typeof iteratedInput === "number" ? iteratedInput + cacheCreation + cacheRead : void 0;
|
|
949
|
+
const outputTokens = typeof iteratedOutput === "number" ? iteratedOutput : void 0;
|
|
950
|
+
return omitNullish({
|
|
951
|
+
inputTokens,
|
|
952
|
+
outputTokens,
|
|
953
|
+
totalTokens: typeof inputTokens === "number" && typeof outputTokens === "number" ? inputTokens + outputTokens : void 0,
|
|
954
|
+
cachedInputTokens: cacheRead || void 0
|
|
955
|
+
});
|
|
956
|
+
};
|
|
957
|
+
const mapContextManagementEdit = (edit) => {
|
|
958
|
+
if (!isRecord(edit) || typeof edit.type !== "string") return edit;
|
|
959
|
+
switch (edit.type) {
|
|
960
|
+
case "clear_tool_uses_20250919": return omitNullish({
|
|
961
|
+
type: edit.type,
|
|
962
|
+
trigger: edit.trigger,
|
|
963
|
+
keep: edit.keep,
|
|
964
|
+
clear_at_least: edit.clearAtLeast,
|
|
965
|
+
clear_tool_inputs: edit.clearToolInputs,
|
|
966
|
+
exclude_tools: edit.excludeTools
|
|
967
|
+
});
|
|
968
|
+
case "clear_thinking_20251015": return omitNullish({
|
|
969
|
+
type: edit.type,
|
|
970
|
+
keep: edit.keep
|
|
971
|
+
});
|
|
972
|
+
case "compact_20260112": return omitNullish({
|
|
973
|
+
type: edit.type,
|
|
974
|
+
trigger: edit.trigger,
|
|
975
|
+
pause_after_compaction: edit.pauseAfterCompaction,
|
|
976
|
+
instructions: edit.instructions
|
|
977
|
+
});
|
|
978
|
+
default: return edit;
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
const mapHostedToolBeta = (type) => {
|
|
982
|
+
switch (type) {
|
|
983
|
+
case "code_execution_20250522": return "code-execution-2025-05-22";
|
|
984
|
+
case "code_execution_20250825": return "code-execution-2025-08-25";
|
|
985
|
+
case "computer_20241022":
|
|
986
|
+
case "text_editor_20241022":
|
|
987
|
+
case "bash_20241022": return "computer-use-2024-10-22";
|
|
988
|
+
case "computer_20250124":
|
|
989
|
+
case "text_editor_20250124":
|
|
990
|
+
case "text_editor_20250429":
|
|
991
|
+
case "bash_20250124": return "computer-use-2025-01-24";
|
|
992
|
+
case "computer_20251124": return "computer-use-2025-11-24";
|
|
993
|
+
case "memory_20250818": return "context-management-2025-06-27";
|
|
994
|
+
case "web_fetch_20250910": return "web-fetch-2025-09-10";
|
|
995
|
+
case "web_fetch_20260209":
|
|
996
|
+
case "web_search_20260209": return "code-execution-web-tools-2026-02-09";
|
|
997
|
+
default: return null;
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
const mapMessagePartsToAnthropicContent = (content, role, modelId, betas) => {
|
|
1001
|
+
const toValidationError = (message, at, context) => BetterAgentError.fromCode("VALIDATION_FAILED", message, { context: {
|
|
1002
|
+
provider: "anthropic",
|
|
1003
|
+
model: modelId,
|
|
1004
|
+
role,
|
|
1005
|
+
...context ?? {}
|
|
1006
|
+
} }).at({ at });
|
|
1007
|
+
const parts = typeof content === "string" ? [{
|
|
1008
|
+
type: "text",
|
|
1009
|
+
text: content
|
|
1010
|
+
}] : content;
|
|
1011
|
+
if (!Array.isArray(parts)) return err(toValidationError("Message content must be a string or array.", "anthropic.map.input.content"));
|
|
1012
|
+
const anthropicContent = [];
|
|
1013
|
+
for (const part of parts) {
|
|
1014
|
+
if (!isRecord(part) || typeof part.type !== "string") return err(toValidationError("Message part must be an object with a type.", "anthropic.map.input.part"));
|
|
1015
|
+
if (part.type === "text") {
|
|
1016
|
+
if (typeof part.text !== "string") return err(toValidationError("Text message parts require a text string.", "anthropic.map.input.text"));
|
|
1017
|
+
const anthropicProviderOptions = getAnthropicPartProviderMetadata(part);
|
|
1018
|
+
anthropicContent.push({
|
|
1019
|
+
type: "text",
|
|
1020
|
+
text: part.text,
|
|
1021
|
+
...anthropicProviderOptions?.cacheControl != null ? { cache_control: anthropicProviderOptions.cacheControl } : {}
|
|
1022
|
+
});
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
if (role === "assistant" || role === "system" || role === "developer") return err(toValidationError(`Role '${role}' only supports text content for Anthropic.`, "anthropic.map.input.roleContent", { partType: part.type }));
|
|
1026
|
+
if (part.type === "image") {
|
|
1027
|
+
if (!isRecord(part.source) || typeof part.source.kind !== "string") return err(toValidationError("Image parts require a valid source.", "anthropic.map.input.imageSource"));
|
|
1028
|
+
if (part.source.kind === "provider-file") return err(toValidationError("Anthropic Messages API does not support provider-file image inputs in this adapter.", "anthropic.map.input.imageProviderFile"));
|
|
1029
|
+
const anthropicProviderOptions = getAnthropicPartProviderMetadata(part);
|
|
1030
|
+
const imageSource = isUrlSource(part.source) || isBase64Source(part.source) ? buildBinarySource(part.source) : null;
|
|
1031
|
+
if (imageSource == null) return err(toValidationError("Image parts require a URL or base64 source.", "anthropic.map.input.imageSourceKind"));
|
|
1032
|
+
anthropicContent.push({
|
|
1033
|
+
type: "image",
|
|
1034
|
+
source: imageSource,
|
|
1035
|
+
...anthropicProviderOptions?.cacheControl != null ? { cache_control: anthropicProviderOptions.cacheControl } : {}
|
|
1036
|
+
});
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (part.type === "file") {
|
|
1040
|
+
if (!isRecord(part.source) || typeof part.source.kind !== "string") return err(toValidationError("File parts require a valid source.", "anthropic.map.input.fileSource"));
|
|
1041
|
+
if (part.source.kind === "provider-file") return err(toValidationError("Anthropic Messages API does not support provider-file document inputs in this adapter.", "anthropic.map.input.fileProviderFile"));
|
|
1042
|
+
const mimeType = typeof part.source.mimeType === "string" ? part.source.mimeType : void 0;
|
|
1043
|
+
const filename = typeof part.source.filename === "string" ? part.source.filename : void 0;
|
|
1044
|
+
const anthropicProviderOptions = getAnthropicPartProviderMetadata(part);
|
|
1045
|
+
const citationsEnabled = anthropicProviderOptions?.citations?.enabled;
|
|
1046
|
+
const documentContext = anthropicProviderOptions?.context;
|
|
1047
|
+
const documentTitle = anthropicProviderOptions?.title ?? filename;
|
|
1048
|
+
const cacheControl = anthropicProviderOptions?.cacheControl;
|
|
1049
|
+
if (typeof mimeType === "string" && mimeType.startsWith("image/")) {
|
|
1050
|
+
const imageSource = buildBinarySource(part.source);
|
|
1051
|
+
if (imageSource == null) return err(toValidationError("Image file inputs require a URL or base64 source.", "anthropic.map.input.fileImageSource"));
|
|
1052
|
+
anthropicContent.push({
|
|
1053
|
+
type: "image",
|
|
1054
|
+
source: imageSource,
|
|
1055
|
+
...cacheControl != null ? { cache_control: cacheControl } : {}
|
|
1056
|
+
});
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
if (mimeType === "application/pdf") {
|
|
1060
|
+
betas.add(PDF_INPUT_BETA);
|
|
1061
|
+
const documentSource = buildBinarySource(part.source);
|
|
1062
|
+
if (documentSource == null) return err(toValidationError("PDF inputs require a URL or base64 source.", "anthropic.map.input.filePdfSource"));
|
|
1063
|
+
anthropicContent.push({
|
|
1064
|
+
type: "document",
|
|
1065
|
+
source: documentSource,
|
|
1066
|
+
...documentTitle != null ? { title: documentTitle } : {},
|
|
1067
|
+
...documentContext != null ? { context: documentContext } : {},
|
|
1068
|
+
...citationsEnabled != null ? { citations: { enabled: citationsEnabled } } : {},
|
|
1069
|
+
...cacheControl != null ? { cache_control: cacheControl } : {}
|
|
1070
|
+
});
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
if (mimeType === "text/plain") {
|
|
1074
|
+
const documentSource = isUrlSource(part.source) ? {
|
|
1075
|
+
type: "url",
|
|
1076
|
+
url: part.source.url
|
|
1077
|
+
} : isBase64Source(part.source) ? {
|
|
1078
|
+
type: "text",
|
|
1079
|
+
media_type: "text/plain",
|
|
1080
|
+
data: decodeBase64Text(part.source.data)
|
|
1081
|
+
} : null;
|
|
1082
|
+
if (documentSource == null) return err(toValidationError("Text document inputs require a URL or base64 source.", "anthropic.map.input.fileTextSource"));
|
|
1083
|
+
anthropicContent.push({
|
|
1084
|
+
type: "document",
|
|
1085
|
+
source: documentSource,
|
|
1086
|
+
...documentTitle != null ? { title: documentTitle } : {},
|
|
1087
|
+
...documentContext != null ? { context: documentContext } : {},
|
|
1088
|
+
...citationsEnabled != null ? { citations: { enabled: citationsEnabled } } : {},
|
|
1089
|
+
...cacheControl != null ? { cache_control: cacheControl } : {}
|
|
1090
|
+
});
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
return err(toValidationError("Anthropic file inputs currently support image/*, application/pdf, and text/plain only.", "anthropic.map.input.fileUnsupported", { mimeType }));
|
|
1094
|
+
}
|
|
1095
|
+
return err(toValidationError(`Unsupported Anthropic input part type '${part.type}'.`, "anthropic.map.input.unsupportedPart"));
|
|
1096
|
+
}
|
|
1097
|
+
return ok(anthropicContent);
|
|
1098
|
+
};
|
|
1099
|
+
function mapToAnthropicMessagesRequest(args) {
|
|
1100
|
+
try {
|
|
1101
|
+
const { modelId } = args;
|
|
1102
|
+
const o = args.options;
|
|
1103
|
+
const stream = args.stream === true;
|
|
1104
|
+
const betas = new Set(o.anthropicBeta ?? []);
|
|
1105
|
+
const systemParts = [];
|
|
1106
|
+
const messages = [];
|
|
1107
|
+
const inputItems = typeof o.input === "string" ? [{
|
|
1108
|
+
type: "message",
|
|
1109
|
+
role: "user",
|
|
1110
|
+
content: o.input
|
|
1111
|
+
}] : o.input;
|
|
1112
|
+
if (Array.isArray(inputItems)) for (const item of inputItems) {
|
|
1113
|
+
if (typeof item === "string") {
|
|
1114
|
+
messages.push({
|
|
1115
|
+
role: "user",
|
|
1116
|
+
content: [{
|
|
1117
|
+
type: "text",
|
|
1118
|
+
text: item
|
|
1119
|
+
}]
|
|
1120
|
+
});
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
if (!isRecord(item) || typeof item.type !== "string") return err(BetterAgentError.fromCode("VALIDATION_FAILED", "Anthropic input items must be messages or tool-call results.", { context: {
|
|
1124
|
+
provider: "anthropic",
|
|
1125
|
+
model: modelId
|
|
1126
|
+
} }).at({ at: "anthropic.map.input.item" }));
|
|
1127
|
+
if (item.type === "message") {
|
|
1128
|
+
const role = typeof item.role === "string" ? item.role : "user";
|
|
1129
|
+
const content = mapMessagePartsToAnthropicContent(item.content, role, String(modelId), betas);
|
|
1130
|
+
if (content.isErr()) return err(content.error);
|
|
1131
|
+
if (role === "system" || role === "developer") {
|
|
1132
|
+
for (const part of content.value) if (part.type === "text") systemParts.push({
|
|
1133
|
+
type: "text",
|
|
1134
|
+
text: part.text
|
|
1135
|
+
});
|
|
1136
|
+
} else if (role === "assistant" || role === "user") messages.push({
|
|
1137
|
+
role,
|
|
1138
|
+
content: content.value
|
|
1139
|
+
});
|
|
1140
|
+
else return err(BetterAgentError.fromCode("VALIDATION_FAILED", `Anthropic does not support role '${role}'.`, { context: {
|
|
1141
|
+
provider: "anthropic",
|
|
1142
|
+
model: modelId,
|
|
1143
|
+
role
|
|
1144
|
+
} }).at({ at: "anthropic.map.input.role" }));
|
|
1145
|
+
continue;
|
|
1146
|
+
}
|
|
1147
|
+
if (item.type === "tool-call" && "result" in item) {
|
|
1148
|
+
const toolResult = item;
|
|
1149
|
+
messages.push({
|
|
1150
|
+
role: "assistant",
|
|
1151
|
+
content: [{
|
|
1152
|
+
type: "tool_use",
|
|
1153
|
+
id: toolResult.callId,
|
|
1154
|
+
name: toolResult.name,
|
|
1155
|
+
input: parseMaybeJson(toolResult.arguments ?? "{}")
|
|
1156
|
+
}]
|
|
1157
|
+
});
|
|
1158
|
+
messages.push({
|
|
1159
|
+
role: "user",
|
|
1160
|
+
content: [{
|
|
1161
|
+
type: "tool_result",
|
|
1162
|
+
tool_use_id: toolResult.callId,
|
|
1163
|
+
content: serializeToolResultContent(toolResult.result),
|
|
1164
|
+
is_error: toolResult.isError
|
|
1165
|
+
}]
|
|
1166
|
+
});
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
return err(BetterAgentError.fromCode("VALIDATION_FAILED", "Anthropic input items must be messages or completed tool-call results.", { context: {
|
|
1170
|
+
provider: "anthropic",
|
|
1171
|
+
model: modelId
|
|
1172
|
+
} }).at({ at: "anthropic.map.input.unsupportedItem" }));
|
|
1173
|
+
}
|
|
1174
|
+
const anthropicTools = [];
|
|
1175
|
+
const tools = ("tools" in o ? o.tools : []) ?? [];
|
|
1176
|
+
const modelSupportsStructuredOutput = getAnthropicModelCapabilities(String(modelId)).supportsStructuredOutput;
|
|
1177
|
+
for (const tool of tools) {
|
|
1178
|
+
if (isAnthropicNativeToolDefinition(tool)) {
|
|
1179
|
+
anthropicTools.push(mapAnthropicNativeToolToRequest(tool));
|
|
1180
|
+
const beta = mapHostedToolBeta(tool.type);
|
|
1181
|
+
if (beta) betas.add(beta);
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
const callableTool = tool;
|
|
1185
|
+
if (!callableTool || !isCallableToolDefinition(callableTool)) continue;
|
|
1186
|
+
const inputSchema = callableTool[TOOL_JSON_SCHEMA];
|
|
1187
|
+
if (!isRecord(inputSchema) || inputSchema.type !== "object") return err(BetterAgentError.fromCode("VALIDATION_FAILED", "Anthropic custom tools require an object JSON schema.", { context: {
|
|
1188
|
+
provider: "anthropic",
|
|
1189
|
+
model: modelId,
|
|
1190
|
+
toolName: callableTool.name
|
|
1191
|
+
} }).at({ at: "anthropic.map.tools.schema" }));
|
|
1192
|
+
anthropicTools.push(omitNullish({
|
|
1193
|
+
name: String(callableTool.name ?? ""),
|
|
1194
|
+
description: typeof callableTool.description === "string" ? callableTool.description : void 0,
|
|
1195
|
+
input_schema: inputSchema,
|
|
1196
|
+
strict: modelSupportsStructuredOutput && typeof callableTool.strict === "boolean" ? callableTool.strict : void 0
|
|
1197
|
+
}));
|
|
1198
|
+
if (modelSupportsStructuredOutput) betas.add("structured-outputs-2025-11-13");
|
|
1199
|
+
}
|
|
1200
|
+
const structuredOutput = "structured_output" in o ? o.structured_output : void 0;
|
|
1201
|
+
const structuredOutputMode = o.structuredOutputMode ?? "auto";
|
|
1202
|
+
const useStructuredOutput = structuredOutputMode === "outputFormat" || structuredOutputMode === "auto" && modelSupportsStructuredOutput;
|
|
1203
|
+
let usesJsonResponseTool = false;
|
|
1204
|
+
let outputConfig = o.effort != null ? { effort: o.effort } : void 0;
|
|
1205
|
+
if (structuredOutput) {
|
|
1206
|
+
if (!isRecord(structuredOutput.schema) || structuredOutput.schema.type !== "object") return err(BetterAgentError.fromCode("VALIDATION_FAILED", "Anthropic structured output schema must be a JSON object schema.", { context: {
|
|
1207
|
+
provider: "anthropic",
|
|
1208
|
+
model: modelId
|
|
1209
|
+
} }).at({ at: "anthropic.map.structuredOutput.schema" }));
|
|
1210
|
+
if (!useStructuredOutput) {
|
|
1211
|
+
usesJsonResponseTool = true;
|
|
1212
|
+
anthropicTools.push({
|
|
1213
|
+
name: "json",
|
|
1214
|
+
description: "Respond with a JSON object.",
|
|
1215
|
+
input_schema: structuredOutput.schema
|
|
1216
|
+
});
|
|
1217
|
+
} else outputConfig = {
|
|
1218
|
+
...outputConfig ?? {},
|
|
1219
|
+
format: {
|
|
1220
|
+
type: "json_schema",
|
|
1221
|
+
schema: structuredOutput.schema
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
let toolChoice;
|
|
1226
|
+
if (usesJsonResponseTool) toolChoice = {
|
|
1227
|
+
type: "tool",
|
|
1228
|
+
name: "json",
|
|
1229
|
+
disable_parallel_tool_use: true
|
|
1230
|
+
};
|
|
1231
|
+
else if ("toolChoice" in o && o.toolChoice) switch (o.toolChoice.type) {
|
|
1232
|
+
case "auto":
|
|
1233
|
+
toolChoice = o.disableParallelToolUse ? {
|
|
1234
|
+
type: "auto",
|
|
1235
|
+
disable_parallel_tool_use: true
|
|
1236
|
+
} : { type: "auto" };
|
|
1237
|
+
break;
|
|
1238
|
+
case "required":
|
|
1239
|
+
toolChoice = {
|
|
1240
|
+
type: "any",
|
|
1241
|
+
...o.disableParallelToolUse ? { disable_parallel_tool_use: true } : {}
|
|
1242
|
+
};
|
|
1243
|
+
break;
|
|
1244
|
+
case "tool":
|
|
1245
|
+
toolChoice = {
|
|
1246
|
+
type: "tool",
|
|
1247
|
+
name: o.toolChoice.name,
|
|
1248
|
+
...o.disableParallelToolUse ? { disable_parallel_tool_use: true } : {}
|
|
1249
|
+
};
|
|
1250
|
+
break;
|
|
1251
|
+
case "none": break;
|
|
1252
|
+
}
|
|
1253
|
+
else if (o.disableParallelToolUse) toolChoice = {
|
|
1254
|
+
type: "auto",
|
|
1255
|
+
disable_parallel_tool_use: true
|
|
1256
|
+
};
|
|
1257
|
+
if (o.mcpServers?.length) betas.add("mcp-client-2025-04-04");
|
|
1258
|
+
if (o.contextManagement) {
|
|
1259
|
+
betas.add("context-management-2025-06-27");
|
|
1260
|
+
if (o.contextManagement.edits.some((edit) => isRecord(edit) && edit.type === "compact_20260112")) betas.add("compact-2026-01-12");
|
|
1261
|
+
}
|
|
1262
|
+
if (o.container?.skills?.length) {
|
|
1263
|
+
betas.add("code-execution-2025-08-25");
|
|
1264
|
+
betas.add("skills-2025-10-02");
|
|
1265
|
+
betas.add("files-api-2025-04-14");
|
|
1266
|
+
}
|
|
1267
|
+
if (o.effort) betas.add("effort-2025-11-24");
|
|
1268
|
+
if (o.speed === "fast") betas.add("fast-mode-2026-02-01");
|
|
1269
|
+
if (stream && (o.toolStreaming ?? true)) betas.add(FINE_GRAINED_TOOL_STREAMING_BETA);
|
|
1270
|
+
return ok({
|
|
1271
|
+
request: {
|
|
1272
|
+
...extractPassthroughOptions(o, ANTHROPIC_RESPONSE_KNOWN_KEYS),
|
|
1273
|
+
model: modelId,
|
|
1274
|
+
max_tokens: o.max_tokens ?? DEFAULT_MAX_TOKENS,
|
|
1275
|
+
messages,
|
|
1276
|
+
...omitNullish({
|
|
1277
|
+
system: systemParts.length ? systemParts : void 0,
|
|
1278
|
+
cache_control: o.cacheControl,
|
|
1279
|
+
metadata: o.metadata?.userId ? { user_id: o.metadata.userId } : void 0,
|
|
1280
|
+
output_config: outputConfig,
|
|
1281
|
+
stop_sequences: o.stop_sequences,
|
|
1282
|
+
temperature: o.temperature,
|
|
1283
|
+
stream: false,
|
|
1284
|
+
speed: o.speed,
|
|
1285
|
+
thinking: o.thinking == null ? void 0 : o.thinking.type === "enabled" ? {
|
|
1286
|
+
type: "enabled",
|
|
1287
|
+
budget_tokens: o.thinking.budgetTokens
|
|
1288
|
+
} : o.thinking,
|
|
1289
|
+
tool_choice: o.toolChoice?.type === "none" && !usesJsonResponseTool ? void 0 : toolChoice,
|
|
1290
|
+
tools: o.toolChoice?.type === "none" && !usesJsonResponseTool ? void 0 : anthropicTools.length ? anthropicTools : void 0,
|
|
1291
|
+
top_k: o.top_k,
|
|
1292
|
+
top_p: o.top_p,
|
|
1293
|
+
mcp_servers: o.mcpServers?.map((server) => ({
|
|
1294
|
+
type: server.type,
|
|
1295
|
+
name: server.name,
|
|
1296
|
+
url: server.url,
|
|
1297
|
+
authorization_token: server.authorizationToken,
|
|
1298
|
+
tool_configuration: server.toolConfiguration ? {
|
|
1299
|
+
enabled: server.toolConfiguration.enabled,
|
|
1300
|
+
allowed_tools: server.toolConfiguration.allowedTools
|
|
1301
|
+
} : void 0
|
|
1302
|
+
})),
|
|
1303
|
+
container: o.container ? {
|
|
1304
|
+
...o.container.id != null ? { id: o.container.id } : {},
|
|
1305
|
+
...o.container.skills?.length ? { skills: o.container.skills.map((skill) => ({
|
|
1306
|
+
type: skill.type,
|
|
1307
|
+
skill_id: skill.skillId,
|
|
1308
|
+
...skill.version != null ? { version: skill.version } : {}
|
|
1309
|
+
})) } : {}
|
|
1310
|
+
} : void 0,
|
|
1311
|
+
context_management: o.contextManagement ? { edits: o.contextManagement.edits.map(mapContextManagementEdit) } : void 0
|
|
1312
|
+
})
|
|
1313
|
+
},
|
|
1314
|
+
betas: [...betas],
|
|
1315
|
+
usesJsonResponseTool
|
|
1316
|
+
});
|
|
1317
|
+
} catch (e) {
|
|
1318
|
+
return err(BetterAgentError.wrap({
|
|
1319
|
+
err: e,
|
|
1320
|
+
message: "Failed to map Anthropic Messages request",
|
|
1321
|
+
opts: {
|
|
1322
|
+
code: "INTERNAL",
|
|
1323
|
+
context: {
|
|
1324
|
+
provider: "anthropic",
|
|
1325
|
+
model: args.modelId
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}).at({ at: "anthropic.messages.mapToRequest" }));
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
const mapProviderToolResultName = (part, serverToolCalls, mcpToolCalls) => {
|
|
1332
|
+
if (part.type === "server_tool_use" && typeof part.name === "string") return normalizeProviderToolName(part.name);
|
|
1333
|
+
if (part.type === "mcp_tool_use") return typeof part.name === "string" ? part.name : "mcp";
|
|
1334
|
+
if (part.type === "mcp_tool_result") return mcpToolCalls[part.tool_use_id ?? ""] ?? "mcp";
|
|
1335
|
+
const resolvedServerName = serverToolCalls[part.tool_use_id ?? ""];
|
|
1336
|
+
if (resolvedServerName) return normalizeProviderToolName(resolvedServerName);
|
|
1337
|
+
switch (part.type) {
|
|
1338
|
+
case "web_fetch_tool_result": return "web_fetch";
|
|
1339
|
+
case "web_search_tool_result": return "web_search";
|
|
1340
|
+
case "code_execution_tool_result":
|
|
1341
|
+
case "bash_code_execution_tool_result":
|
|
1342
|
+
case "text_editor_code_execution_tool_result": return "code_execution";
|
|
1343
|
+
case "tool_search_tool_result": return "tool_search_tool_regex";
|
|
1344
|
+
default: return part.type;
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
function mapFromAnthropicMessagesResponse(args) {
|
|
1348
|
+
const assistantParts = [];
|
|
1349
|
+
const outputItems = [];
|
|
1350
|
+
const serverToolCalls = {};
|
|
1351
|
+
const mcpToolCalls = {};
|
|
1352
|
+
let isJsonResponseFromTool = false;
|
|
1353
|
+
for (const part of args.response.content) switch (part.type) {
|
|
1354
|
+
case "text":
|
|
1355
|
+
assistantParts.push({
|
|
1356
|
+
type: "text",
|
|
1357
|
+
text: part.text,
|
|
1358
|
+
...part.citations?.length ? { providerMetadata: getAnthropicTextProviderMetadata(part.citations) } : {}
|
|
1359
|
+
});
|
|
1360
|
+
break;
|
|
1361
|
+
case "compaction":
|
|
1362
|
+
assistantParts.push({
|
|
1363
|
+
type: "text",
|
|
1364
|
+
text: part.content
|
|
1365
|
+
});
|
|
1366
|
+
break;
|
|
1367
|
+
case "tool_use":
|
|
1368
|
+
if (args.usesJsonResponseTool && part.name === "json") {
|
|
1369
|
+
isJsonResponseFromTool = true;
|
|
1370
|
+
assistantParts.push({
|
|
1371
|
+
type: "text",
|
|
1372
|
+
text: JSON.stringify(part.input)
|
|
1373
|
+
});
|
|
1374
|
+
} else outputItems.push({
|
|
1375
|
+
type: "tool-call",
|
|
1376
|
+
name: part.name,
|
|
1377
|
+
arguments: JSON.stringify(part.input ?? {}),
|
|
1378
|
+
callId: part.id
|
|
1379
|
+
});
|
|
1380
|
+
break;
|
|
1381
|
+
case "server_tool_use":
|
|
1382
|
+
serverToolCalls[part.id] = part.name;
|
|
1383
|
+
outputItems.push({
|
|
1384
|
+
type: "provider-tool-result",
|
|
1385
|
+
name: normalizeProviderToolName(part.name),
|
|
1386
|
+
callId: part.id,
|
|
1387
|
+
result: part
|
|
1388
|
+
});
|
|
1389
|
+
break;
|
|
1390
|
+
case "mcp_tool_use":
|
|
1391
|
+
mcpToolCalls[part.id] = part.name;
|
|
1392
|
+
outputItems.push({
|
|
1393
|
+
type: "provider-tool-result",
|
|
1394
|
+
name: part.name,
|
|
1395
|
+
callId: part.id,
|
|
1396
|
+
result: part
|
|
1397
|
+
});
|
|
1398
|
+
break;
|
|
1399
|
+
case "mcp_tool_result":
|
|
1400
|
+
case "web_fetch_tool_result":
|
|
1401
|
+
case "web_search_tool_result":
|
|
1402
|
+
case "code_execution_tool_result":
|
|
1403
|
+
case "bash_code_execution_tool_result":
|
|
1404
|
+
case "text_editor_code_execution_tool_result":
|
|
1405
|
+
case "tool_search_tool_result":
|
|
1406
|
+
outputItems.push({
|
|
1407
|
+
type: "provider-tool-result",
|
|
1408
|
+
name: mapProviderToolResultName(part, serverToolCalls, mcpToolCalls),
|
|
1409
|
+
callId: part.tool_use_id,
|
|
1410
|
+
result: part,
|
|
1411
|
+
isError: "is_error" in part && typeof part.is_error === "boolean" ? part.is_error : void 0
|
|
1412
|
+
});
|
|
1413
|
+
break;
|
|
1414
|
+
case "thinking":
|
|
1415
|
+
case "redacted_thinking": break;
|
|
1416
|
+
}
|
|
1417
|
+
if (assistantParts.length) outputItems.unshift({
|
|
1418
|
+
type: "message",
|
|
1419
|
+
role: "assistant",
|
|
1420
|
+
content: assistantParts
|
|
1421
|
+
});
|
|
1422
|
+
return {
|
|
1423
|
+
output: outputItems,
|
|
1424
|
+
finishReason: mapAnthropicStopReason(args.response.stop_reason, isJsonResponseFromTool),
|
|
1425
|
+
usage: mapAnthropicUsage(args.response.usage),
|
|
1426
|
+
response: { body: args.response }
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
const createAnthropicStreamState = (messageId, usesJsonResponseTool = false) => ({
|
|
1430
|
+
messageId,
|
|
1431
|
+
outputItems: [],
|
|
1432
|
+
assistantParts: [],
|
|
1433
|
+
blocks: {},
|
|
1434
|
+
serverToolCalls: {},
|
|
1435
|
+
mcpToolCalls: {},
|
|
1436
|
+
finishReasonRaw: void 0,
|
|
1437
|
+
usage: {},
|
|
1438
|
+
usesJsonResponseTool
|
|
1439
|
+
});
|
|
1440
|
+
const createToolStartEvent = (parentMessageId, toolCallId, toolCallName) => ({
|
|
1441
|
+
type: Events.TOOL_CALL_START,
|
|
1442
|
+
parentMessageId,
|
|
1443
|
+
toolCallId,
|
|
1444
|
+
toolCallName,
|
|
1445
|
+
timestamp: Date.now()
|
|
1446
|
+
});
|
|
1447
|
+
const createToolArgsEvent = (parentMessageId, toolCallId, toolCallName, delta) => ({
|
|
1448
|
+
type: Events.TOOL_CALL_ARGS,
|
|
1449
|
+
parentMessageId,
|
|
1450
|
+
toolCallId,
|
|
1451
|
+
toolCallName,
|
|
1452
|
+
delta,
|
|
1453
|
+
timestamp: Date.now()
|
|
1454
|
+
});
|
|
1455
|
+
const createToolEndEvent = (parentMessageId, toolCallId, toolCallName) => ({
|
|
1456
|
+
type: Events.TOOL_CALL_END,
|
|
1457
|
+
parentMessageId,
|
|
1458
|
+
toolCallId,
|
|
1459
|
+
toolCallName,
|
|
1460
|
+
timestamp: Date.now()
|
|
1461
|
+
});
|
|
1462
|
+
const createToolResultEvent = (parentMessageId, toolCallId, toolCallName, result, isError) => ({
|
|
1463
|
+
type: Events.TOOL_CALL_RESULT,
|
|
1464
|
+
parentMessageId,
|
|
1465
|
+
toolCallId,
|
|
1466
|
+
toolCallName,
|
|
1467
|
+
result,
|
|
1468
|
+
isError,
|
|
1469
|
+
timestamp: Date.now()
|
|
1470
|
+
});
|
|
1471
|
+
const finalizeStreamResponse = (state) => {
|
|
1472
|
+
const output = [...state.outputItems];
|
|
1473
|
+
if (state.assistantParts.length) output.unshift({
|
|
1474
|
+
type: "message",
|
|
1475
|
+
role: "assistant",
|
|
1476
|
+
content: state.assistantParts
|
|
1477
|
+
});
|
|
1478
|
+
const hasToolCalls = output.some((item) => item.type === "tool-call" && "arguments" in item);
|
|
1479
|
+
return {
|
|
1480
|
+
output,
|
|
1481
|
+
finishReason: state.finishReasonRaw == null && hasToolCalls ? "tool-calls" : mapAnthropicStopReason(state.finishReasonRaw, state.usesJsonResponseTool),
|
|
1482
|
+
usage: mapAnthropicUsage(state.usage)
|
|
1483
|
+
};
|
|
1484
|
+
};
|
|
1485
|
+
function mapFromAnthropicStreamEvent(event, state) {
|
|
1486
|
+
switch (event.type) {
|
|
1487
|
+
case "ping": return ok(null);
|
|
1488
|
+
case "message_start":
|
|
1489
|
+
state.usage = {
|
|
1490
|
+
...state.usage,
|
|
1491
|
+
...event.message.usage
|
|
1492
|
+
};
|
|
1493
|
+
state.finishReasonRaw = event.message.stop_reason;
|
|
1494
|
+
return ok(null);
|
|
1495
|
+
case "message_delta":
|
|
1496
|
+
if (event.delta.stop_reason != null) state.finishReasonRaw = event.delta.stop_reason;
|
|
1497
|
+
state.usage = {
|
|
1498
|
+
...state.usage,
|
|
1499
|
+
...event.usage ?? {}
|
|
1500
|
+
};
|
|
1501
|
+
return ok(null);
|
|
1502
|
+
case "content_block_start": {
|
|
1503
|
+
const { index, content_block: part } = event;
|
|
1504
|
+
switch (part.type) {
|
|
1505
|
+
case "text":
|
|
1506
|
+
case "compaction": {
|
|
1507
|
+
const textMessageId = `${state.messageId}:text:${index}`;
|
|
1508
|
+
state.blocks[index] = {
|
|
1509
|
+
kind: "text",
|
|
1510
|
+
messageId: textMessageId,
|
|
1511
|
+
text: part.type === "compaction" ? part.content : "",
|
|
1512
|
+
...part.type === "text" && part.citations?.length ? { citations: [...part.citations] } : {}
|
|
1513
|
+
};
|
|
1514
|
+
return ok({
|
|
1515
|
+
kind: "event",
|
|
1516
|
+
event: {
|
|
1517
|
+
type: Events.TEXT_MESSAGE_START,
|
|
1518
|
+
messageId: textMessageId,
|
|
1519
|
+
role: "assistant",
|
|
1520
|
+
timestamp: Date.now()
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
case "thinking":
|
|
1525
|
+
case "redacted_thinking": {
|
|
1526
|
+
const reasoningMessageId = `${state.messageId}:reasoning:${index}`;
|
|
1527
|
+
state.blocks[index] = {
|
|
1528
|
+
kind: "reasoning",
|
|
1529
|
+
messageId: reasoningMessageId
|
|
1530
|
+
};
|
|
1531
|
+
return ok({
|
|
1532
|
+
kind: "event",
|
|
1533
|
+
event: {
|
|
1534
|
+
type: Events.REASONING_MESSAGE_START,
|
|
1535
|
+
messageId: reasoningMessageId,
|
|
1536
|
+
role: "assistant",
|
|
1537
|
+
visibility: "full",
|
|
1538
|
+
timestamp: Date.now()
|
|
1539
|
+
}
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
case "tool_use": {
|
|
1543
|
+
const outputAsText = state.usesJsonResponseTool && part.name === "json";
|
|
1544
|
+
const initialInput = JSON.stringify(part.input ?? {});
|
|
1545
|
+
state.blocks[index] = {
|
|
1546
|
+
kind: "tool",
|
|
1547
|
+
callId: part.id,
|
|
1548
|
+
toolName: part.name,
|
|
1549
|
+
rawToolName: part.name,
|
|
1550
|
+
rawType: "tool_use",
|
|
1551
|
+
input: initialInput === "{}" ? "" : initialInput,
|
|
1552
|
+
outputAsText,
|
|
1553
|
+
providerExecuted: false
|
|
1554
|
+
};
|
|
1555
|
+
if (outputAsText) {
|
|
1556
|
+
const textMessageId = `${state.messageId}:text:${index}`;
|
|
1557
|
+
state.blocks[index] = {
|
|
1558
|
+
...state.blocks[index],
|
|
1559
|
+
kind: "tool"
|
|
1560
|
+
};
|
|
1561
|
+
return ok({
|
|
1562
|
+
kind: "event",
|
|
1563
|
+
event: {
|
|
1564
|
+
type: Events.TEXT_MESSAGE_START,
|
|
1565
|
+
messageId: textMessageId,
|
|
1566
|
+
role: "assistant",
|
|
1567
|
+
timestamp: Date.now()
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
return ok({
|
|
1572
|
+
kind: "event",
|
|
1573
|
+
event: createToolStartEvent(state.messageId, part.id, part.name)
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
case "server_tool_use":
|
|
1577
|
+
state.serverToolCalls[part.id] = part.name;
|
|
1578
|
+
state.blocks[index] = {
|
|
1579
|
+
kind: "tool",
|
|
1580
|
+
callId: part.id,
|
|
1581
|
+
toolName: normalizeProviderToolName(part.name),
|
|
1582
|
+
rawToolName: part.name,
|
|
1583
|
+
rawType: "server_tool_use",
|
|
1584
|
+
input: JSON.stringify(part.input ?? {}) === "{}" ? "" : JSON.stringify(part.input ?? {}),
|
|
1585
|
+
outputAsText: false,
|
|
1586
|
+
providerExecuted: true
|
|
1587
|
+
};
|
|
1588
|
+
return ok({
|
|
1589
|
+
kind: "event",
|
|
1590
|
+
event: createToolStartEvent(state.messageId, part.id, normalizeProviderToolName(part.name))
|
|
1591
|
+
});
|
|
1592
|
+
case "mcp_tool_use":
|
|
1593
|
+
state.mcpToolCalls[part.id] = part.name;
|
|
1594
|
+
state.blocks[index] = {
|
|
1595
|
+
kind: "tool",
|
|
1596
|
+
callId: part.id,
|
|
1597
|
+
toolName: part.name,
|
|
1598
|
+
rawToolName: part.name,
|
|
1599
|
+
rawType: "mcp_tool_use",
|
|
1600
|
+
input: JSON.stringify(part.input ?? {}) === "{}" ? "" : JSON.stringify(part.input ?? {}),
|
|
1601
|
+
outputAsText: false,
|
|
1602
|
+
providerExecuted: true,
|
|
1603
|
+
extra: part.server_name != null ? { server_name: part.server_name } : void 0
|
|
1604
|
+
};
|
|
1605
|
+
return ok({
|
|
1606
|
+
kind: "event",
|
|
1607
|
+
event: createToolStartEvent(state.messageId, part.id, part.name)
|
|
1608
|
+
});
|
|
1609
|
+
case "mcp_tool_result":
|
|
1610
|
+
case "web_fetch_tool_result":
|
|
1611
|
+
case "web_search_tool_result":
|
|
1612
|
+
case "code_execution_tool_result":
|
|
1613
|
+
case "bash_code_execution_tool_result":
|
|
1614
|
+
case "text_editor_code_execution_tool_result":
|
|
1615
|
+
case "tool_search_tool_result": {
|
|
1616
|
+
const toolName = mapProviderToolResultName(part, state.serverToolCalls, state.mcpToolCalls);
|
|
1617
|
+
state.outputItems.push({
|
|
1618
|
+
type: "provider-tool-result",
|
|
1619
|
+
name: toolName,
|
|
1620
|
+
callId: part.tool_use_id,
|
|
1621
|
+
result: part,
|
|
1622
|
+
isError: "is_error" in part && typeof part.is_error === "boolean" ? part.is_error : void 0
|
|
1623
|
+
});
|
|
1624
|
+
return ok({
|
|
1625
|
+
kind: "event",
|
|
1626
|
+
event: createToolResultEvent(state.messageId, part.tool_use_id, toolName, part, "is_error" in part && typeof part.is_error === "boolean" ? part.is_error : void 0)
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
return ok(null);
|
|
1631
|
+
}
|
|
1632
|
+
case "content_block_delta": {
|
|
1633
|
+
const block = state.blocks[event.index];
|
|
1634
|
+
if (!block) return ok(null);
|
|
1635
|
+
switch (event.delta.type) {
|
|
1636
|
+
case "text_delta":
|
|
1637
|
+
if (block.kind !== "text") return ok(null);
|
|
1638
|
+
block.text += event.delta.text;
|
|
1639
|
+
return ok({
|
|
1640
|
+
kind: "event",
|
|
1641
|
+
event: {
|
|
1642
|
+
type: Events.TEXT_MESSAGE_CONTENT,
|
|
1643
|
+
messageId: block.messageId,
|
|
1644
|
+
delta: event.delta.text,
|
|
1645
|
+
timestamp: Date.now()
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
case "compaction_delta":
|
|
1649
|
+
if (block.kind !== "text") return ok(null);
|
|
1650
|
+
if (event.delta.content) {
|
|
1651
|
+
block.text += event.delta.content;
|
|
1652
|
+
return ok({
|
|
1653
|
+
kind: "event",
|
|
1654
|
+
event: {
|
|
1655
|
+
type: Events.TEXT_MESSAGE_CONTENT,
|
|
1656
|
+
messageId: block.messageId,
|
|
1657
|
+
delta: event.delta.content,
|
|
1658
|
+
timestamp: Date.now()
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
return ok(null);
|
|
1663
|
+
case "thinking_delta":
|
|
1664
|
+
if (block.kind !== "reasoning") return ok(null);
|
|
1665
|
+
return ok({
|
|
1666
|
+
kind: "event",
|
|
1667
|
+
event: {
|
|
1668
|
+
type: Events.REASONING_MESSAGE_CONTENT,
|
|
1669
|
+
messageId: block.messageId,
|
|
1670
|
+
visibility: "full",
|
|
1671
|
+
delta: event.delta.thinking,
|
|
1672
|
+
timestamp: Date.now()
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
case "signature_delta": return ok(null);
|
|
1676
|
+
case "input_json_delta":
|
|
1677
|
+
if (block.kind !== "tool") return ok(null);
|
|
1678
|
+
block.input += event.delta.partial_json;
|
|
1679
|
+
if (block.outputAsText) return ok({
|
|
1680
|
+
kind: "event",
|
|
1681
|
+
event: {
|
|
1682
|
+
type: Events.TEXT_MESSAGE_CONTENT,
|
|
1683
|
+
messageId: `${state.messageId}:text:${event.index}`,
|
|
1684
|
+
delta: event.delta.partial_json,
|
|
1685
|
+
timestamp: Date.now()
|
|
1686
|
+
}
|
|
1687
|
+
});
|
|
1688
|
+
return ok({
|
|
1689
|
+
kind: "event",
|
|
1690
|
+
event: createToolArgsEvent(state.messageId, block.callId, block.toolName, event.delta.partial_json)
|
|
1691
|
+
});
|
|
1692
|
+
case "citations_delta":
|
|
1693
|
+
if (block.kind !== "text") return ok(null);
|
|
1694
|
+
block.citations = [...block.citations ?? [], event.delta.citation];
|
|
1695
|
+
return ok(null);
|
|
1696
|
+
}
|
|
1697
|
+
return ok(null);
|
|
1698
|
+
}
|
|
1699
|
+
case "content_block_stop": {
|
|
1700
|
+
const block = state.blocks[event.index];
|
|
1701
|
+
if (!block) return ok(null);
|
|
1702
|
+
delete state.blocks[event.index];
|
|
1703
|
+
if (block.kind === "text") {
|
|
1704
|
+
if (block.text.length > 0) state.assistantParts.push({
|
|
1705
|
+
type: "text",
|
|
1706
|
+
text: block.text,
|
|
1707
|
+
...block.citations?.length ? { providerMetadata: getAnthropicTextProviderMetadata(block.citations) } : {}
|
|
1708
|
+
});
|
|
1709
|
+
return ok({
|
|
1710
|
+
kind: "event",
|
|
1711
|
+
event: {
|
|
1712
|
+
type: Events.TEXT_MESSAGE_END,
|
|
1713
|
+
messageId: block.messageId,
|
|
1714
|
+
timestamp: Date.now()
|
|
1715
|
+
}
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
if (block.kind === "reasoning") return ok({
|
|
1719
|
+
kind: "event",
|
|
1720
|
+
event: {
|
|
1721
|
+
type: Events.REASONING_MESSAGE_END,
|
|
1722
|
+
messageId: block.messageId,
|
|
1723
|
+
visibility: "full",
|
|
1724
|
+
timestamp: Date.now()
|
|
1725
|
+
}
|
|
1726
|
+
});
|
|
1727
|
+
if (block.outputAsText) {
|
|
1728
|
+
const finalText = block.input.trim() ? block.input : "{}";
|
|
1729
|
+
state.assistantParts.push({
|
|
1730
|
+
type: "text",
|
|
1731
|
+
text: finalText
|
|
1732
|
+
});
|
|
1733
|
+
state.usesJsonResponseTool = true;
|
|
1734
|
+
return ok({
|
|
1735
|
+
kind: "event",
|
|
1736
|
+
event: {
|
|
1737
|
+
type: Events.TEXT_MESSAGE_END,
|
|
1738
|
+
messageId: `${state.messageId}:text:${event.index}`,
|
|
1739
|
+
timestamp: Date.now()
|
|
1740
|
+
}
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
if (block.rawType === "tool_use") state.outputItems.push({
|
|
1744
|
+
type: "tool-call",
|
|
1745
|
+
name: block.toolName,
|
|
1746
|
+
arguments: block.input.trim() ? block.input : "{}",
|
|
1747
|
+
callId: block.callId
|
|
1748
|
+
});
|
|
1749
|
+
else state.outputItems.push({
|
|
1750
|
+
type: "provider-tool-result",
|
|
1751
|
+
name: block.toolName,
|
|
1752
|
+
callId: block.callId,
|
|
1753
|
+
result: omitNullish({
|
|
1754
|
+
type: block.rawType,
|
|
1755
|
+
id: block.callId,
|
|
1756
|
+
name: block.rawToolName,
|
|
1757
|
+
input: parseMaybeJson(block.input),
|
|
1758
|
+
...block.extra ?? {}
|
|
1759
|
+
})
|
|
1760
|
+
});
|
|
1761
|
+
return ok({
|
|
1762
|
+
kind: "event",
|
|
1763
|
+
event: createToolEndEvent(state.messageId, block.callId, block.toolName)
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
case "message_stop": return ok({
|
|
1767
|
+
kind: "final",
|
|
1768
|
+
response: finalizeStreamResponse(state)
|
|
1769
|
+
});
|
|
1770
|
+
case "error": return err(BetterAgentError.fromCode("UPSTREAM_FAILED", event.error.message ?? "Anthropic streaming error", { context: {
|
|
1771
|
+
provider: "anthropic",
|
|
1772
|
+
upstreamCode: event.error.type ?? "STREAM_ERROR",
|
|
1773
|
+
raw: event
|
|
1774
|
+
} }).at({ at: "anthropic.messages.stream.event" }));
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
//#endregion
|
|
1779
|
+
//#region src/anthropic/responses/model.ts
|
|
1780
|
+
const ANTHROPIC_RESPONSE_CAPS = {
|
|
1781
|
+
inputModalities: {
|
|
1782
|
+
text: true,
|
|
1783
|
+
image: true,
|
|
1784
|
+
file: true
|
|
1785
|
+
},
|
|
1786
|
+
inputShape: "chat",
|
|
1787
|
+
replayMode: "multi_turn",
|
|
1788
|
+
supportsInstruction: true,
|
|
1789
|
+
outputModalities: { text: { options: {} } },
|
|
1790
|
+
tools: true,
|
|
1791
|
+
structured_output: true,
|
|
1792
|
+
additionalSupportedRoles: ["developer"]
|
|
1793
|
+
};
|
|
1794
|
+
const createDeferred = () => {
|
|
1795
|
+
let resolve;
|
|
1796
|
+
let reject;
|
|
1797
|
+
return {
|
|
1798
|
+
promise: new Promise((res, rej) => {
|
|
1799
|
+
resolve = res;
|
|
1800
|
+
reject = rej;
|
|
1801
|
+
}),
|
|
1802
|
+
resolve,
|
|
1803
|
+
reject
|
|
1804
|
+
};
|
|
1805
|
+
};
|
|
1806
|
+
const createAnthropicResponsesModel = (modelId, client) => {
|
|
1807
|
+
const doGenerate = async (options, ctx) => {
|
|
1808
|
+
const mappedRequest = mapToAnthropicMessagesRequest({
|
|
1809
|
+
modelId,
|
|
1810
|
+
options,
|
|
1811
|
+
stream: false
|
|
1812
|
+
});
|
|
1813
|
+
if (mappedRequest.isErr()) return err(mappedRequest.error.at({ at: "anthropic.generate.mapRequest" }));
|
|
1814
|
+
const raw = await client.messages.create(mappedRequest.value.request, {
|
|
1815
|
+
signal: ctx.signal ?? null,
|
|
1816
|
+
beta: mappedRequest.value.betas
|
|
1817
|
+
});
|
|
1818
|
+
if (raw.isErr()) return err(raw.error.at({ at: "anthropic.generate.http" }));
|
|
1819
|
+
return ok({ response: {
|
|
1820
|
+
...mapFromAnthropicMessagesResponse({
|
|
1821
|
+
response: raw.value,
|
|
1822
|
+
usesJsonResponseTool: mappedRequest.value.usesJsonResponseTool
|
|
1823
|
+
}),
|
|
1824
|
+
request: { body: mappedRequest.value.request }
|
|
1825
|
+
} });
|
|
1826
|
+
};
|
|
1827
|
+
const doGenerateStream = async (options, ctx) => {
|
|
1828
|
+
const mappedRequest = mapToAnthropicMessagesRequest({
|
|
1829
|
+
modelId,
|
|
1830
|
+
options,
|
|
1831
|
+
stream: true
|
|
1832
|
+
});
|
|
1833
|
+
if (mappedRequest.isErr()) return err(mappedRequest.error.at({ at: "anthropic.generateStream.mapRequest" }));
|
|
1834
|
+
const streamResult = await client.messages.stream({
|
|
1835
|
+
...mappedRequest.value.request,
|
|
1836
|
+
stream: true
|
|
1837
|
+
}, {
|
|
1838
|
+
signal: ctx.signal ?? null,
|
|
1839
|
+
beta: mappedRequest.value.betas
|
|
1840
|
+
});
|
|
1841
|
+
if (streamResult.isErr()) return err(streamResult.error.at({ at: "anthropic.generateStream.http" }));
|
|
1842
|
+
const { promise: final, resolve: resolveFinal, reject: rejectFinal } = createDeferred();
|
|
1843
|
+
return ok({
|
|
1844
|
+
events: (async function* () {
|
|
1845
|
+
const state = createAnthropicStreamState(ctx.generateMessageId(), mappedRequest.value.usesJsonResponseTool);
|
|
1846
|
+
let sawFinal = false;
|
|
1847
|
+
try {
|
|
1848
|
+
for await (const raw of streamResult.value) {
|
|
1849
|
+
if (raw.isErr()) {
|
|
1850
|
+
rejectFinal(raw.error);
|
|
1851
|
+
yield err(raw.error);
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
const mapped = mapFromAnthropicStreamEvent(raw.value, state);
|
|
1855
|
+
if (mapped.isErr()) {
|
|
1856
|
+
const error = mapped.error.at({ at: "anthropic.generateStream.mapEvent" });
|
|
1857
|
+
rejectFinal(error);
|
|
1858
|
+
yield err(error);
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
if (!mapped.value) continue;
|
|
1862
|
+
if (mapped.value.kind === "final") {
|
|
1863
|
+
sawFinal = true;
|
|
1864
|
+
resolveFinal({
|
|
1865
|
+
...mapped.value.response,
|
|
1866
|
+
request: { body: mappedRequest.value.request }
|
|
1867
|
+
});
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
yield ok(mapped.value.event);
|
|
1871
|
+
}
|
|
1872
|
+
} finally {
|
|
1873
|
+
if (!sawFinal) rejectFinal(BetterAgentError.fromCode("UPSTREAM_FAILED", "Anthropic stream ended without a final response event.", { context: {
|
|
1874
|
+
provider: "anthropic",
|
|
1875
|
+
model: String(modelId)
|
|
1876
|
+
} }).at({ at: "anthropic.generateStream.final" }));
|
|
1877
|
+
}
|
|
1878
|
+
})(),
|
|
1879
|
+
final
|
|
1880
|
+
});
|
|
1881
|
+
};
|
|
1882
|
+
return {
|
|
1883
|
+
providerId: "anthropic",
|
|
1884
|
+
modelId,
|
|
1885
|
+
caps: ANTHROPIC_RESPONSE_CAPS,
|
|
1886
|
+
doGenerate,
|
|
1887
|
+
doGenerateStream
|
|
1888
|
+
};
|
|
1889
|
+
};
|
|
1890
|
+
|
|
1891
|
+
//#endregion
|
|
1892
|
+
//#region src/anthropic/provider.ts
|
|
1893
|
+
const createAnthropic = (config) => {
|
|
1894
|
+
const httpClient = createAnthropicClient(config);
|
|
1895
|
+
return {
|
|
1896
|
+
id: "anthropic",
|
|
1897
|
+
tools: createAnthropicNativeToolBuilders(),
|
|
1898
|
+
model(modelId) {
|
|
1899
|
+
return createAnthropicResponsesModel(modelId, httpClient);
|
|
1900
|
+
},
|
|
1901
|
+
text(modelId) {
|
|
1902
|
+
return createAnthropicResponsesModel(modelId, httpClient);
|
|
1903
|
+
}
|
|
1904
|
+
};
|
|
1905
|
+
};
|
|
1906
|
+
|
|
1907
|
+
//#endregion
|
|
1908
|
+
export { createAnthropic };
|
|
1909
|
+
//# sourceMappingURL=index.mjs.map
|