veryfront 0.1.381 → 0.1.382

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.
Files changed (33) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/provider/index.d.ts +3 -0
  3. package/esm/src/provider/index.d.ts.map +1 -1
  4. package/esm/src/provider/index.js +1 -0
  5. package/esm/src/provider/runtime-loader.d.ts +1 -11
  6. package/esm/src/provider/runtime-loader.d.ts.map +1 -1
  7. package/esm/src/provider/runtime-loader.js +2 -775
  8. package/esm/src/provider/shared/index.d.ts +2 -1
  9. package/esm/src/provider/shared/index.d.ts.map +1 -1
  10. package/esm/src/provider/shared/index.js +3 -1
  11. package/esm/src/provider/veryfront-cloud/model-catalog.d.ts +31 -0
  12. package/esm/src/provider/veryfront-cloud/model-catalog.d.ts.map +1 -0
  13. package/esm/src/provider/veryfront-cloud/model-catalog.js +163 -0
  14. package/esm/src/provider/veryfront-cloud/provider.d.ts.map +1 -1
  15. package/esm/src/provider/veryfront-cloud/provider.js +1 -7
  16. package/esm/src/utils/version-constant.d.ts +1 -1
  17. package/esm/src/utils/version-constant.js +1 -1
  18. package/package.json +1 -1
  19. package/src/deno.js +1 -1
  20. package/src/deps/esm.sh/@types/react-dom@19.2.3/client.d.ts +1 -1
  21. package/src/deps/esm.sh/@types/{react@19.2.3 → react@19.2.14}/global.d.ts +1 -0
  22. package/src/deps/esm.sh/@types/{react@19.2.3 → react@19.2.14}/index.d.ts +93 -24
  23. package/src/deps/esm.sh/react-dom@19.2.4/client.d.ts +1 -1
  24. package/src/src/provider/index.ts +20 -0
  25. package/src/src/provider/runtime-loader.ts +1 -1008
  26. package/src/src/provider/shared/index.ts +3 -1
  27. package/src/src/provider/veryfront-cloud/model-catalog.ts +220 -0
  28. package/src/src/provider/veryfront-cloud/provider.ts +3 -7
  29. package/src/src/utils/version-constant.ts +1 -1
  30. package/esm/src/provider/runtime-loader/provider-finish-reasons.d.ts +0 -9
  31. package/esm/src/provider/runtime-loader/provider-finish-reasons.d.ts.map +0 -1
  32. package/esm/src/provider/runtime-loader/provider-finish-reasons.js +0 -60
  33. package/src/src/provider/runtime-loader/provider-finish-reasons.ts +0 -69
@@ -1,15 +1,11 @@
1
- import { getAnthropicMessagesUrl } from "./runtime-loader/provider-endpoints.js";
2
1
  import { isNumberArray } from "./runtime-loader/provider-embedding-responses.js";
3
- import { normalizeAnthropicFinishReason } from "./runtime-loader/provider-finish-reasons.js";
4
- import { createAnthropicRequestInit } from "./runtime-loader/provider-request-init.js";
5
- import { parseSseChunk } from "./runtime-loader/provider-sse.js";
6
- import { extractAnthropicUsage, mergeUsage, } from "./runtime-loader/provider-usage.js";
2
+ import { mergeUsage } from "./runtime-loader/provider-usage.js";
7
3
  import { buildProviderError, parseRetryAfterMs, requestJson, requestStream, } from "./runtime-loader/provider-http.js";
8
4
  import { readRecord } from "./runtime-loader/provider-records.js";
9
5
  import { TOOL_INPUT_PENDING_THRESHOLD_MS, withToolInputStatusTransitions, } from "./runtime-loader/tool-input-status.js";
10
6
  export { ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, } from "./runtime-loader/provider-http.js";
11
7
  export { TOOL_INPUT_PENDING_THRESHOLD_MS, withToolInputStatusTransitions };
12
- export { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, parseSseChunk, readRecord, requestJson, requestStream, };
8
+ export { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, readRecord, requestJson, requestStream, };
13
9
  export function createWarningCollector() {
14
10
  const list = [];
15
11
  return {
@@ -118,715 +114,6 @@ export function readProviderOptions(providerOptions, ...providerNames) {
118
114
  }
119
115
  return merged;
120
116
  }
121
- function normalizeAnthropicToolChoice(toolChoice) {
122
- if (typeof toolChoice === "string") {
123
- return { type: toolChoice };
124
- }
125
- return toolChoice;
126
- }
127
- function toSnakeCaseRecord(record) {
128
- return Object.fromEntries(Object.entries(record).map(([key, value]) => [
129
- key.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`),
130
- value,
131
- ]));
132
- }
133
- /**
134
- * Recursive snake_case key converter for nested config objects (used for
135
- * Anthropic mcp_servers, where authorizationToken / toolConfiguration /
136
- * allowedTools all need conversion).
137
- */
138
- function deepSnakeCase(value) {
139
- if (Array.isArray(value)) {
140
- return value.map(deepSnakeCase);
141
- }
142
- if (value !== null && typeof value === "object") {
143
- return Object.fromEntries(Object.entries(value).map(([key, v]) => [
144
- key.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`),
145
- deepSnakeCase(v),
146
- ]));
147
- }
148
- return value;
149
- }
150
- function pushAnthropicUserContent(messages, content) {
151
- if (content.length === 0) {
152
- return;
153
- }
154
- const lastMessage = messages.at(-1);
155
- if (lastMessage?.role === "user") {
156
- lastMessage.content.push(...content);
157
- return;
158
- }
159
- messages.push({
160
- role: "user",
161
- content,
162
- });
163
- }
164
- /**
165
- * Resolves a {@link ProviderCacheTtl} into Anthropic's `cache_control` shape.
166
- *
167
- * Returns `undefined` when caching is not requested (`false` / `undefined`),
168
- * `{ type: "ephemeral" }` for the 5-minute default (`true` / `"5m"`), or
169
- * `{ type: "ephemeral", ttl: "1h" }` for the extended 1-hour cache.
170
- */
171
- function resolveAnthropicCacheControlBlock(ttl) {
172
- if (ttl === undefined || ttl === false) {
173
- return undefined;
174
- }
175
- if (ttl === "1h") {
176
- return { type: "ephemeral", ttl: "1h" };
177
- }
178
- return { type: "ephemeral" };
179
- }
180
- function toAnthropicMessages(prompt, systemCacheControl) {
181
- const systemParts = [];
182
- const messages = [];
183
- for (const message of prompt) {
184
- switch (message.role) {
185
- case "system":
186
- if (message.content.length > 0) {
187
- systemParts.push(message.content);
188
- }
189
- break;
190
- case "user":
191
- pushAnthropicUserContent(messages, [{
192
- type: "text",
193
- text: readTextParts(message.content),
194
- }]);
195
- break;
196
- case "assistant":
197
- messages.push({
198
- role: "assistant",
199
- content: message.content.map((part) => {
200
- if (part.type === "text") {
201
- return { type: "text", text: part.text };
202
- }
203
- if (part.type === "reasoning") {
204
- // Redacted thinking blocks roundtrip as the encrypted blob
205
- // form Anthropic gave us. Plain thinking blocks need the
206
- // signature to verify on the server.
207
- if (typeof part.redactedData === "string") {
208
- return {
209
- type: "redacted_thinking",
210
- data: part.redactedData,
211
- };
212
- }
213
- return {
214
- type: "thinking",
215
- thinking: part.text ?? "",
216
- ...(typeof part.signature === "string" ? { signature: part.signature } : {}),
217
- };
218
- }
219
- return {
220
- type: "tool_use",
221
- id: part.toolCallId,
222
- name: part.toolName,
223
- input: part.input,
224
- };
225
- }),
226
- });
227
- break;
228
- case "tool":
229
- pushAnthropicUserContent(messages, message.content.map((part) => ({
230
- type: "tool_result",
231
- tool_use_id: part.toolCallId,
232
- content: stringifyJsonValue(part.output.value),
233
- })));
234
- break;
235
- }
236
- }
237
- if (systemParts.length === 0) {
238
- return { messages };
239
- }
240
- const joined = systemParts.join("\n\n");
241
- // Cache-controlled system prompts must use the array-of-blocks form so the
242
- // breakpoint lands on an individual content block. Callers that don't opt
243
- // in keep the legacy raw-string form for backward compatibility.
244
- if (systemCacheControl) {
245
- return {
246
- system: [{
247
- type: "text",
248
- text: joined,
249
- cache_control: systemCacheControl,
250
- }],
251
- messages,
252
- };
253
- }
254
- return { system: joined, messages };
255
- }
256
- /**
257
- * Short-name → latest-versioned-type alias map for Anthropic provider tools.
258
- *
259
- * Anthropic tool types are date-stamped (e.g. `code_execution_20260120`) so
260
- * callers either pin a version or get the latest. We accept both: a caller
261
- * can pass `anthropic.code_execution` and we map to the latest known version,
262
- * or pass `anthropic.code_execution_20250522` and we forward verbatim.
263
- *
264
- * Versions chosen here are the latest documented releases as of 2026-04-15
265
- * — see https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview.
266
- * When Anthropic ships newer versions, update this map.
267
- */
268
- const ANTHROPIC_TOOL_VERSION_ALIASES = {
269
- code_execution: "code_execution_20260120",
270
- computer_use: "computer_20250124",
271
- computer: "computer_20250124",
272
- text_editor: "text_editor_20250728",
273
- bash: "bash_20250124",
274
- memory: "memory_20250818",
275
- web_search: "web_search_20250305",
276
- web_fetch: "web_fetch_20250910",
277
- };
278
- function resolveAnthropicProviderType(rawType) {
279
- // Already-versioned types (contain a date stamp suffix) pass through verbatim.
280
- if (/_\d{8}$/.test(rawType)) {
281
- return rawType;
282
- }
283
- return ANTHROPIC_TOOL_VERSION_ALIASES[rawType] ?? rawType;
284
- }
285
- function toAnthropicTools(tools, toolsCacheControl) {
286
- if (!tools) {
287
- return undefined;
288
- }
289
- const normalized = [];
290
- for (const tool of tools) {
291
- if (tool.type === "function") {
292
- normalized.push({
293
- name: tool.name,
294
- ...(typeof tool.description === "string" ? { description: tool.description } : {}),
295
- input_schema: unwrapToolInputSchema(tool.inputSchema),
296
- });
297
- continue;
298
- }
299
- if (!tool.id.startsWith("anthropic.")) {
300
- continue;
301
- }
302
- const rawType = tool.id.slice("anthropic.".length);
303
- if (rawType.length === 0) {
304
- continue;
305
- }
306
- normalized.push({
307
- type: resolveAnthropicProviderType(rawType),
308
- name: tool.name,
309
- ...toSnakeCaseRecord(tool.args),
310
- });
311
- }
312
- if (normalized.length === 0) {
313
- return undefined;
314
- }
315
- // Attach the cache breakpoint to the final tool entry so Anthropic caches
316
- // the entire tools block up to and including that definition. Earlier tool
317
- // entries are implicitly covered by the same breakpoint per Anthropic's
318
- // walk-backward cache lookup behaviour.
319
- if (toolsCacheControl) {
320
- const lastIndex = normalized.length - 1;
321
- normalized[lastIndex] = {
322
- ...normalized[lastIndex],
323
- cache_control: toolsCacheControl,
324
- };
325
- }
326
- return normalized;
327
- }
328
- /**
329
- * Anthropic's Messages API requires `max_tokens` on every call, so the
330
- * outbound request builder must always supply a number. Picking the right
331
- * one means knowing the model: different Claude families have wildly
332
- * different maximum output budgets, and a flat default either truncates
333
- * modern models mid-response or gets rejected with "too many tokens" on
334
- * older ones. Return the model's advertised maximum as the default, and
335
- * clamp caller-provided values at that same ceiling for known models so a
336
- * bad input becomes a clipped response rather than an API error. Unknown
337
- * model ids get a conservative 4096 fallback and pass caller values
338
- * through unchanged, since we have no intel to clamp against.
339
- */
340
- function getAnthropicModelCapabilities(modelId) {
341
- if (modelId.includes("claude-sonnet-4-6") || modelId.includes("claude-opus-4-6")) {
342
- return { maxOutputTokens: 128_000, isKnownModel: true };
343
- }
344
- if (modelId.includes("claude-sonnet-4-5") ||
345
- modelId.includes("claude-opus-4-5") ||
346
- modelId.includes("claude-haiku-4-5")) {
347
- return { maxOutputTokens: 64_000, isKnownModel: true };
348
- }
349
- if (modelId.includes("claude-opus-4-1")) {
350
- return { maxOutputTokens: 32_000, isKnownModel: true };
351
- }
352
- if (modelId.includes("claude-sonnet-4-")) {
353
- return { maxOutputTokens: 64_000, isKnownModel: true };
354
- }
355
- if (modelId.includes("claude-opus-4-")) {
356
- return { maxOutputTokens: 32_000, isKnownModel: true };
357
- }
358
- if (modelId.includes("claude-3-haiku")) {
359
- return { maxOutputTokens: 4096, isKnownModel: true };
360
- }
361
- return { maxOutputTokens: 4096, isKnownModel: false };
362
- }
363
- function resolveAnthropicMaxTokens(modelId, callerMaxOutputTokens) {
364
- const { maxOutputTokens: modelMax, isKnownModel } = getAnthropicModelCapabilities(modelId);
365
- const requested = callerMaxOutputTokens ?? modelMax;
366
- if (isKnownModel && requested > modelMax) {
367
- return modelMax;
368
- }
369
- return requested;
370
- }
371
- /**
372
- * Map a unified reasoning effort level to an Anthropic `thinking.budget_tokens`
373
- * value. Anthropic's minimum accepted budget is 1024; higher tiers give Claude
374
- * more headroom to explore. `max` maps to the upper bound documented for
375
- * Claude 4.x family (32k tokens of thinking — caller can override via
376
- * `budgetTokens` if they need more).
377
- */
378
- function resolveAnthropicThinkingBudget(option) {
379
- if (!option || option.enabled !== true) {
380
- return undefined;
381
- }
382
- if (typeof option.budgetTokens === "number" && option.budgetTokens >= 1024) {
383
- return option.budgetTokens;
384
- }
385
- switch (option.effort) {
386
- case "low":
387
- return 1024;
388
- case "high":
389
- return 16_384;
390
- case "max":
391
- return 32_768;
392
- case "medium":
393
- default:
394
- return 4096;
395
- }
396
- }
397
- function buildAnthropicMessagesRequest(modelId, providerName, options, stream, warnings) {
398
- const systemCacheControl = resolveAnthropicCacheControlBlock(options.cacheControl?.system);
399
- const toolsCacheControl = resolveAnthropicCacheControlBlock(options.cacheControl?.tools);
400
- const { system, messages } = toAnthropicMessages(options.prompt, systemCacheControl);
401
- const anthropicTools = toAnthropicTools(options.tools, toolsCacheControl);
402
- const thinkingBudget = resolveAnthropicThinkingBudget(options.reasoning);
403
- const thinkingEnabled = thinkingBudget !== undefined;
404
- // Anthropic doesn't support these unified options at all — emit warnings
405
- // so callers don't quietly pass values that have zero effect.
406
- if (options.presencePenalty !== undefined) {
407
- warnings.push({
408
- type: "unsupported-setting",
409
- provider: "anthropic",
410
- setting: "presencePenalty",
411
- details: "Anthropic Messages API has no equivalent and the value was dropped.",
412
- });
413
- }
414
- if (options.frequencyPenalty !== undefined) {
415
- warnings.push({
416
- type: "unsupported-setting",
417
- provider: "anthropic",
418
- setting: "frequencyPenalty",
419
- details: "Anthropic Messages API has no equivalent and the value was dropped.",
420
- });
421
- }
422
- if (options.seed !== undefined) {
423
- warnings.push({
424
- type: "unsupported-setting",
425
- provider: "anthropic",
426
- setting: "seed",
427
- details: "Anthropic Messages API does not support deterministic seeding.",
428
- });
429
- }
430
- if (options.topK !== undefined) {
431
- warnings.push({
432
- type: "unsupported-setting",
433
- provider: "anthropic",
434
- setting: "topK",
435
- details: "Anthropic Messages API does not expose top_k on this surface.",
436
- });
437
- }
438
- if (options.stopSequences && options.stopSequences.length > 4) {
439
- warnings.push({
440
- type: "unsupported-setting",
441
- provider: "anthropic",
442
- setting: "stopSequences",
443
- details: `Anthropic accepts at most 4 stop sequences; ${options.stopSequences.length} were provided and the extras were truncated.`,
444
- });
445
- }
446
- if (thinkingEnabled && options.temperature !== undefined) {
447
- warnings.push({
448
- type: "unsupported-setting",
449
- provider: "anthropic",
450
- setting: "temperature",
451
- details: "Dropped because Anthropic rejects sampling params when extended thinking is enabled.",
452
- });
453
- }
454
- if (thinkingEnabled && options.topP !== undefined) {
455
- warnings.push({
456
- type: "unsupported-setting",
457
- provider: "anthropic",
458
- setting: "topP",
459
- details: "Dropped because Anthropic rejects sampling params when extended thinking is enabled.",
460
- });
461
- }
462
- if (options.responseFormat && options.responseFormat.type !== "text") {
463
- warnings.push({
464
- type: "unsupported-setting",
465
- provider: "anthropic",
466
- setting: "responseFormat",
467
- details: "Anthropic Messages API does not have a structured-output response_format equivalent. Use a tool with the schema as input_schema instead.",
468
- });
469
- }
470
- // Anthropic requires max_tokens > budget_tokens when thinking is enabled.
471
- // Growing max_tokens by the thinking budget preserves the caller's intended
472
- // output budget, and we clamp the sum at the model's advertised maximum so
473
- // the request never exceeds the API's hard cap.
474
- const baseMaxTokens = resolveAnthropicMaxTokens(modelId, options.maxOutputTokens);
475
- const maxTokens = thinkingEnabled
476
- ? Math.min(baseMaxTokens + (thinkingBudget ?? 0), getAnthropicModelCapabilities(modelId).maxOutputTokens)
477
- : baseMaxTokens;
478
- const body = {
479
- model: modelId,
480
- messages,
481
- max_tokens: maxTokens,
482
- ...(stream ? { stream: true } : {}),
483
- ...(system ? { system } : {}),
484
- // Sampling params are mutually exclusive with thinking on Anthropic — the
485
- // API rejects the combo outright. Drop them silently when thinking is on
486
- // (callers see thinking's output instead of what they'd have gotten from
487
- // custom sampling, which is the documented tradeoff).
488
- ...(!thinkingEnabled && options.temperature !== undefined
489
- ? { temperature: options.temperature }
490
- : {}),
491
- ...(!thinkingEnabled && options.topP !== undefined ? { top_p: options.topP } : {}),
492
- ...(options.stopSequences && options.stopSequences.length > 0
493
- ? { stop_sequences: options.stopSequences.slice(0, 4) }
494
- : {}),
495
- ...(anthropicTools ? { tools: anthropicTools } : {}),
496
- ...(options.toolChoice !== undefined
497
- ? { tool_choice: normalizeAnthropicToolChoice(options.toolChoice) }
498
- : {}),
499
- ...(thinkingEnabled ? { thinking: { type: "enabled", budget_tokens: thinkingBudget } } : {}),
500
- ...(typeof options.userId === "string" && options.userId.length > 0
501
- ? { metadata: { user_id: options.userId } }
502
- : {}),
503
- ...(options.mcpServers && options.mcpServers.length > 0
504
- ? { mcp_servers: deepSnakeCase(options.mcpServers) }
505
- : {}),
506
- ...(options.anthropicContainer !== undefined ? { container: options.anthropicContainer } : {}),
507
- };
508
- Object.assign(body, readProviderOptions(options.providerOptions, "anthropic", providerName));
509
- return body;
510
- }
511
- /**
512
- * Best-effort camelCase normalization of a single Anthropic citation
513
- * record. Handles the union of fields across web_search_result_location,
514
- * web_fetch_result_location, char_location, page_location, and
515
- * content_block_location citation kinds — see
516
- * https://docs.claude.com/en/docs/build-with-claude/citations
517
- */
518
- function normalizeAnthropicCitation(raw) {
519
- const r = readRecord(raw);
520
- if (!r)
521
- return undefined;
522
- const typeStr = typeof r.type === "string" ? r.type : undefined;
523
- if (!typeStr)
524
- return undefined;
525
- const out = { type: typeStr };
526
- if (typeof r.cited_text === "string")
527
- out.citedText = r.cited_text;
528
- if (typeof r.url === "string")
529
- out.url = r.url;
530
- if (typeof r.title === "string")
531
- out.title = r.title;
532
- if (typeof r.start_char_index === "number")
533
- out.startCharIndex = r.start_char_index;
534
- if (typeof r.end_char_index === "number")
535
- out.endCharIndex = r.end_char_index;
536
- if (typeof r.start_block_index === "number")
537
- out.startBlockIndex = r.start_block_index;
538
- if (typeof r.end_block_index === "number")
539
- out.endBlockIndex = r.end_block_index;
540
- if (typeof r.start_page_number === "number")
541
- out.startPageNumber = r.start_page_number;
542
- if (typeof r.end_page_number === "number")
543
- out.endPageNumber = r.end_page_number;
544
- if (typeof r.document_index === "number")
545
- out.documentIndex = r.document_index;
546
- if (typeof r.document_title === "string")
547
- out.documentTitle = r.document_title;
548
- return out;
549
- }
550
- function buildAnthropicGenerateResult(payload) {
551
- const record = readRecord(payload);
552
- const content = Array.isArray(record?.content) ? record.content : [];
553
- const normalized = [];
554
- for (const blockValue of content) {
555
- const block = readRecord(blockValue);
556
- const blockType = typeof block?.type === "string" ? block.type : undefined;
557
- if (blockType === "text" && typeof block?.text === "string" && block.text.length > 0) {
558
- const citationsRaw = Array.isArray(block.citations) ? block.citations : undefined;
559
- const citations = citationsRaw
560
- ?.flatMap((c) => {
561
- const normalizedCitation = normalizeAnthropicCitation(c);
562
- return normalizedCitation ? [normalizedCitation] : [];
563
- });
564
- normalized.push({
565
- type: "text",
566
- text: block.text,
567
- ...(citations && citations.length > 0 ? { citations } : {}),
568
- });
569
- continue;
570
- }
571
- // Thinking blocks carry the cleartext trace plus a signature that
572
- // Anthropic uses to verify on subsequent turns. Surfacing both lets
573
- // callers persist them as `reasoning` content parts and replay on
574
- // the next turn so Claude can continue from the same thinking.
575
- if (blockType === "thinking") {
576
- normalized.push({
577
- type: "reasoning",
578
- ...(typeof block?.thinking === "string" ? { text: block.thinking } : {}),
579
- ...(typeof block?.signature === "string" ? { signature: block.signature } : {}),
580
- });
581
- continue;
582
- }
583
- // Redacted thinking blocks arrive when Claude's safety classifier
584
- // hides the trace. Pass the encrypted blob through opaquely so the
585
- // caller can replay it on the next turn (Anthropic still needs the
586
- // blob to verify continuity even though it can't read it).
587
- if (blockType === "redacted_thinking" && typeof block?.data === "string") {
588
- normalized.push({
589
- type: "reasoning",
590
- redactedData: block.data,
591
- });
592
- continue;
593
- }
594
- if ((blockType === "tool_use" || blockType === "server_tool_use") &&
595
- typeof block?.id === "string" &&
596
- typeof block?.name === "string") {
597
- normalized.push({
598
- type: "tool-call",
599
- toolCallId: block.id,
600
- toolName: block.name,
601
- input: stringifyJsonValue(block.input ?? {}),
602
- });
603
- continue;
604
- }
605
- if (blockType === "web_search_tool_result" &&
606
- typeof block?.tool_use_id === "string" &&
607
- Array.isArray(block?.content)) {
608
- normalized.push({
609
- type: "tool-result",
610
- toolCallId: block.tool_use_id,
611
- toolName: "web_search",
612
- result: block.content,
613
- });
614
- }
615
- if (blockType === "web_fetch_tool_result" &&
616
- typeof block?.tool_use_id === "string" &&
617
- readRecord(block?.content)) {
618
- normalized.push({
619
- type: "tool-result",
620
- toolCallId: block.tool_use_id,
621
- toolName: "web_fetch",
622
- result: block.content,
623
- });
624
- }
625
- }
626
- return {
627
- content: normalized,
628
- finishReason: normalizeAnthropicFinishReason(record?.stop_reason),
629
- usage: extractAnthropicUsage(payload),
630
- };
631
- }
632
- async function* streamAnthropicCompatibleParts(stream) {
633
- const decoder = new TextDecoder();
634
- let buffer = "";
635
- const toolCalls = new Map();
636
- const reasoningBlocks = new Map();
637
- let finishReason = null;
638
- let usage;
639
- for await (const chunk of stream) {
640
- buffer += decoder.decode(chunk, { stream: true });
641
- const parsed = parseSseChunk(buffer);
642
- buffer = parsed.remainder;
643
- for (const event of parsed.events) {
644
- if (event === "[DONE]") {
645
- continue;
646
- }
647
- const record = readRecord(event);
648
- const eventType = typeof record?.type === "string" ? record.type : undefined;
649
- usage = mergeUsage(usage, extractAnthropicUsage(record));
650
- if (eventType === "message_start") {
651
- usage = mergeUsage(usage, extractAnthropicUsage(record?.message));
652
- continue;
653
- }
654
- if (eventType === "content_block_start") {
655
- const index = typeof record?.index === "number" ? record.index : 0;
656
- const contentBlock = readRecord(record?.content_block);
657
- const blockType = typeof contentBlock?.type === "string" ? contentBlock.type : undefined;
658
- if (blockType === "text" && typeof contentBlock?.text === "string" &&
659
- contentBlock.text.length > 0) {
660
- yield { type: "text-delta", delta: contentBlock.text };
661
- continue;
662
- }
663
- if (blockType === "thinking") {
664
- const reasoningId = `thinking-${index}`;
665
- reasoningBlocks.set(index, { id: reasoningId });
666
- yield {
667
- type: "reasoning-start",
668
- id: reasoningId,
669
- };
670
- if (typeof contentBlock?.thinking === "string" && contentBlock.thinking.length > 0) {
671
- yield {
672
- type: "reasoning-delta",
673
- id: reasoningId,
674
- delta: contentBlock.thinking,
675
- };
676
- }
677
- continue;
678
- }
679
- // Redacted thinking blocks arrive as opaque encrypted payloads when
680
- // Claude's safety classifier flags the reasoning trace. Surface them
681
- // as a zero-length reasoning block so callers know thinking happened
682
- // without leaking the (legitimately hidden) contents.
683
- if (blockType === "redacted_thinking") {
684
- const reasoningId = `thinking-${index}`;
685
- reasoningBlocks.set(index, { id: reasoningId });
686
- yield {
687
- type: "reasoning-start",
688
- id: reasoningId,
689
- };
690
- continue;
691
- }
692
- if ((blockType === "tool_use" || blockType === "server_tool_use") &&
693
- typeof contentBlock?.id === "string" &&
694
- typeof contentBlock?.name === "string") {
695
- const providerExecuted = blockType === "server_tool_use" ? true : undefined;
696
- const current = {
697
- id: contentBlock.id,
698
- name: contentBlock.name,
699
- input: "",
700
- ...(providerExecuted ? { providerExecuted } : {}),
701
- };
702
- toolCalls.set(index, current);
703
- yield {
704
- type: "tool-input-start",
705
- id: current.id,
706
- toolName: current.name,
707
- ...(providerExecuted ? { providerExecuted } : {}),
708
- };
709
- const initialInput = contentBlock.input;
710
- if (initialInput !== undefined) {
711
- const serializedInput = stringifyJsonValue(initialInput);
712
- current.input += serializedInput;
713
- yield {
714
- type: "tool-input-delta",
715
- id: current.id,
716
- delta: serializedInput,
717
- };
718
- }
719
- continue;
720
- }
721
- if (blockType === "web_search_tool_result" &&
722
- typeof contentBlock?.tool_use_id === "string" &&
723
- Array.isArray(contentBlock?.content)) {
724
- yield {
725
- type: "tool-result",
726
- toolCallId: contentBlock.tool_use_id,
727
- toolName: "web_search",
728
- result: contentBlock.content,
729
- providerExecuted: true,
730
- };
731
- }
732
- if (blockType === "web_fetch_tool_result" &&
733
- typeof contentBlock?.tool_use_id === "string" &&
734
- readRecord(contentBlock?.content)) {
735
- yield {
736
- type: "tool-result",
737
- toolCallId: contentBlock.tool_use_id,
738
- toolName: "web_fetch",
739
- result: contentBlock.content,
740
- providerExecuted: true,
741
- };
742
- }
743
- continue;
744
- }
745
- if (eventType === "content_block_delta") {
746
- const index = typeof record?.index === "number" ? record.index : 0;
747
- const delta = readRecord(record?.delta);
748
- const deltaType = typeof delta?.type === "string" ? delta.type : undefined;
749
- if (deltaType === "text_delta" && typeof delta?.text === "string" && delta.text.length > 0) {
750
- yield { type: "text-delta", delta: delta.text };
751
- continue;
752
- }
753
- if (deltaType === "thinking_delta" && typeof delta?.thinking === "string" &&
754
- delta.thinking.length > 0) {
755
- const current = reasoningBlocks.get(index);
756
- if (!current) {
757
- continue;
758
- }
759
- yield {
760
- type: "reasoning-delta",
761
- id: current.id,
762
- delta: delta.thinking,
763
- };
764
- continue;
765
- }
766
- if (deltaType === "input_json_delta" && typeof delta?.partial_json === "string") {
767
- const current = toolCalls.get(index);
768
- if (!current) {
769
- continue;
770
- }
771
- current.input += delta.partial_json;
772
- yield {
773
- type: "tool-input-delta",
774
- id: current.id,
775
- delta: delta.partial_json,
776
- };
777
- }
778
- continue;
779
- }
780
- if (eventType === "content_block_stop") {
781
- const index = typeof record?.index === "number" ? record.index : 0;
782
- const reasoning = reasoningBlocks.get(index);
783
- if (reasoning) {
784
- yield {
785
- type: "reasoning-end",
786
- id: reasoning.id,
787
- };
788
- reasoningBlocks.delete(index);
789
- continue;
790
- }
791
- const current = toolCalls.get(index);
792
- if (!current) {
793
- continue;
794
- }
795
- yield {
796
- type: "tool-call",
797
- toolCallId: current.id,
798
- toolName: current.name,
799
- input: current.input.length > 0 ? current.input : "{}",
800
- ...(current.providerExecuted ? { providerExecuted: true } : {}),
801
- };
802
- toolCalls.delete(index);
803
- continue;
804
- }
805
- if (eventType === "message_delta") {
806
- const delta = readRecord(record?.delta);
807
- const normalizedFinishReason = normalizeAnthropicFinishReason(delta?.stop_reason);
808
- if (normalizedFinishReason) {
809
- finishReason = normalizedFinishReason;
810
- }
811
- }
812
- }
813
- }
814
- if (buffer.trim().length > 0) {
815
- const parsed = parseSseChunk(`${buffer}\n\n`);
816
- for (const event of parsed.events) {
817
- if (event === "[DONE]") {
818
- continue;
819
- }
820
- const record = readRecord(event);
821
- usage = mergeUsage(usage, extractAnthropicUsage(record));
822
- }
823
- }
824
- yield {
825
- type: "finish",
826
- finishReason,
827
- ...(usage ? { usage } : {}),
828
- };
829
- }
830
117
  export function unwrapToolInputSchema(inputSchema) {
831
118
  if (typeof inputSchema !== "object" || inputSchema === null || Array.isArray(inputSchema)) {
832
119
  return inputSchema;
@@ -834,63 +121,3 @@ export function unwrapToolInputSchema(inputSchema) {
834
121
  const candidate = Reflect.get(inputSchema, "jsonSchema");
835
122
  return candidate ?? inputSchema;
836
123
  }
837
- export function createAnthropicModelRuntime(config, modelId) {
838
- const fetchImpl = config.fetch ?? globalThis.fetch;
839
- return {
840
- provider: config.name ?? "anthropic",
841
- modelId,
842
- specificationVersion: "v3",
843
- supportedUrls: {},
844
- doGenerate(optionsForRuntime) {
845
- const options = optionsForRuntime;
846
- const url = getAnthropicMessagesUrl(config.baseURL);
847
- const warnings = createWarningCollector();
848
- const body = buildAnthropicMessagesRequest(modelId, config.name ?? "anthropic", options, false, warnings);
849
- return requestJson({
850
- url,
851
- fetchImpl,
852
- providerLabel: config.name ?? "anthropic",
853
- providerKind: "anthropic",
854
- init: createAnthropicRequestInit({
855
- apiKey: config.apiKey,
856
- authToken: config.authToken,
857
- extraHeaders: options.headers,
858
- body: JSON.stringify(body),
859
- signal: options.abortSignal,
860
- }),
861
- }).then((payload) => {
862
- const drained = warnings.drain();
863
- return {
864
- ...buildAnthropicGenerateResult(payload),
865
- ...(drained.length > 0 ? { warnings: drained } : {}),
866
- };
867
- });
868
- },
869
- doStream(optionsForRuntime) {
870
- const options = optionsForRuntime;
871
- const url = getAnthropicMessagesUrl(config.baseURL);
872
- const warnings = createWarningCollector();
873
- const body = buildAnthropicMessagesRequest(modelId, config.name ?? "anthropic", options, true, warnings);
874
- return requestStream({
875
- url,
876
- fetchImpl,
877
- providerLabel: config.name ?? "anthropic",
878
- providerKind: "anthropic",
879
- init: createAnthropicRequestInit({
880
- apiKey: config.apiKey,
881
- authToken: config.authToken,
882
- extraHeaders: options.headers,
883
- enableFineGrainedToolStreaming: true,
884
- body: JSON.stringify(body),
885
- signal: options.abortSignal,
886
- }),
887
- }).then((responseStream) => {
888
- const drained = warnings.drain();
889
- return {
890
- stream: ReadableStream.from(withToolInputStatusTransitions(streamAnthropicCompatibleParts(responseStream))),
891
- ...(drained.length > 0 ? { warnings: drained } : {}),
892
- };
893
- });
894
- },
895
- };
896
- }