@happyvertical/ai 0.111.2 → 0.111.4
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 +60 -0
- package/dist/chunks/{anthropic-DQjBd3wu.js → anthropic-BXiE495e.js} +94 -20
- package/dist/chunks/anthropic-BXiE495e.js.map +1 -0
- package/dist/chunks/{bedrock-gtE6OU5Z.js → bedrock-fO9ix2hd.js} +37 -16
- package/dist/chunks/bedrock-fO9ix2hd.js.map +1 -0
- package/dist/chunks/{bifrost-B0191CPM.js → bifrost-CrLHqZQJ.js} +4 -4
- package/dist/chunks/{bifrost-B0191CPM.js.map → bifrost-CrLHqZQJ.js.map} +1 -1
- package/dist/chunks/{byteplus-modelark-C0rRxYwh.js → byteplus-modelark-CM0-yuF3.js} +3 -3
- package/dist/chunks/{byteplus-modelark-C0rRxYwh.js.map → byteplus-modelark-CM0-yuF3.js.map} +1 -1
- package/dist/chunks/cache-hint-BdVY0hrJ.js +39 -0
- package/dist/chunks/cache-hint-BdVY0hrJ.js.map +1 -0
- package/dist/chunks/{claude-cli-DysSNpKY.js → claude-cli-CD1quA61.js} +3 -3
- package/dist/chunks/{claude-cli-DysSNpKY.js.map → claude-cli-CD1quA61.js.map} +1 -1
- package/dist/chunks/{continuation-B-b0Iqgj.js → continuation-Dgw5Zujl.js} +10 -3
- package/dist/chunks/continuation-Dgw5Zujl.js.map +1 -0
- package/dist/chunks/{gateway-audio-models-57sR3hHE.js → gateway-audio-models-Cs7vu956.js} +2 -2
- package/dist/chunks/{gateway-audio-models-57sR3hHE.js.map → gateway-audio-models-Cs7vu956.js.map} +1 -1
- package/dist/chunks/{gemini-BHdv492T.js → gemini-DjUpgpM1.js} +3 -3
- package/dist/chunks/{gemini-BHdv492T.js.map → gemini-DjUpgpM1.js.map} +1 -1
- package/dist/chunks/{huggingface-vooWYknE.js → huggingface-C8P7DPFD.js} +3 -3
- package/dist/chunks/{huggingface-vooWYknE.js.map → huggingface-C8P7DPFD.js.map} +1 -1
- package/dist/chunks/{laya-B7aCsYqu.js → laya-DuRRHJHo.js} +3 -3
- package/dist/chunks/{laya-B7aCsYqu.js.map → laya-DuRRHJHo.js.map} +1 -1
- package/dist/chunks/{litellm-Bhns6CSh.js → litellm-Cc6xC6sj.js} +4 -4
- package/dist/chunks/{litellm-Bhns6CSh.js.map → litellm-Cc6xC6sj.js.map} +1 -1
- package/dist/chunks/{ollama-pL2WCpkS.js → ollama-D5dILl5b.js} +8 -5
- package/dist/chunks/ollama-D5dILl5b.js.map +1 -0
- package/dist/chunks/{openai-compat-video-BoLwWXJM.js → openai-compat-video-DMu4hsI5.js} +3 -3
- package/dist/chunks/{openai-compat-video-BoLwWXJM.js.map → openai-compat-video-DMu4hsI5.js.map} +1 -1
- package/dist/chunks/{qwen-tts-BbGkn1oW.js → qwen-tts-dvbQnMSK.js} +2 -2
- package/dist/chunks/{qwen-tts-BbGkn1oW.js.map → qwen-tts-dvbQnMSK.js.map} +1 -1
- package/dist/chunks/{rate-limit-B3Kz6v4j.js → rate-limit-B9BFC8ld.js} +2 -2
- package/dist/chunks/{rate-limit-B3Kz6v4j.js.map → rate-limit-B9BFC8ld.js.map} +1 -1
- package/dist/chunks/{seevio-Dt8kWgpg.js → seevio-BnmiByK2.js} +3 -3
- package/dist/chunks/{seevio-Dt8kWgpg.js.map → seevio-BnmiByK2.js.map} +1 -1
- package/dist/chunks/{src-CWFnwztw.js → src-EMvAiwjJ.js} +115 -46
- package/dist/chunks/src-EMvAiwjJ.js.map +1 -0
- package/dist/chunks/{typesafe-DDVSVUR_.js → typesafe-d5Jwafs7.js} +3 -3
- package/dist/chunks/{typesafe-DDVSVUR_.js.map → typesafe-d5Jwafs7.js.map} +1 -1
- package/dist/chunks/{usage-yIT5lpQm.js → usage-B40bJZ49.js} +86 -3
- package/dist/chunks/usage-B40bJZ49.js.map +1 -0
- package/dist/chunks/{webllm-DrwR5V-y.js → webllm-Cp4aHmyG.js} +2 -2
- package/dist/chunks/{webllm-DrwR5V-y.js.map → webllm-Cp4aHmyG.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/local.js +2 -2
- package/dist/node.js +3 -3
- package/dist/shared/cache-hint.d.ts +26 -0
- package/dist/shared/cache-hint.d.ts.map +1 -0
- package/dist/shared/claude-models.d.ts +29 -0
- package/dist/shared/claude-models.d.ts.map +1 -0
- package/dist/shared/continuation.d.ts.map +1 -1
- package/dist/shared/providers/anthropic.d.ts +5 -0
- package/dist/shared/providers/anthropic.d.ts.map +1 -1
- package/dist/shared/providers/bedrock.d.ts.map +1 -1
- package/dist/shared/providers/ollama.d.ts.map +1 -1
- package/dist/shared/providers/openai.d.ts +14 -0
- package/dist/shared/providers/openai.d.ts.map +1 -1
- package/dist/shared/providers/usage.d.ts +5 -0
- package/dist/shared/providers/usage.d.ts.map +1 -1
- package/dist/shared/safety.d.ts.map +1 -1
- package/dist/shared/types.d.ts +87 -3
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/chunks/anthropic-DQjBd3wu.js.map +0 -1
- package/dist/chunks/bedrock-gtE6OU5Z.js.map +0 -1
- package/dist/chunks/continuation-B-b0Iqgj.js.map +0 -1
- package/dist/chunks/ollama-pL2WCpkS.js.map +0 -1
- package/dist/chunks/src-CWFnwztw.js.map +0 -1
- package/dist/chunks/usage-yIT5lpQm.js.map +0 -1
package/README.md
CHANGED
|
@@ -673,6 +673,37 @@ alias like `openai/terra`, gets the same shape unless it names a legacy family
|
|
|
673
673
|
of one), because an alias name does not reveal the model behind it. Bare ids and other vendors are
|
|
674
674
|
matched by name only.
|
|
675
675
|
|
|
676
|
+
### Cache and cost usage
|
|
677
|
+
|
|
678
|
+
`TokenUsage` (on `AIResponse.usage` and `onUsage` events) reports prompt-cache
|
|
679
|
+
figures when the provider sends them: `cacheReadInputTokens` and
|
|
680
|
+
`cacheCreationInputTokens`. Both are included in `promptTokens` for every
|
|
681
|
+
provider (Anthropic and Bedrock report them separately from input tokens; the SDK
|
|
682
|
+
adds them), and absent when the provider reports none. `costUsd` is set only when
|
|
683
|
+
the server or gateway reports a cost (Bifrost's `usage.cost.total_cost`); the SDK
|
|
684
|
+
never estimates one. Anthropic and Bedrock streams report usage too.
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
### Claude effort
|
|
688
|
+
|
|
689
|
+
Claude 4.6+ models (Opus and Sonnet 4.6 and later, Haiku 5 and later, Fable,
|
|
690
|
+
Mythos) take `output_config.effort` instead of a thinking budget, and Opus 4.7+
|
|
691
|
+
rejects `budget_tokens` outright. For those models `reasoning.effort`
|
|
692
|
+
(`'low' | 'medium' | 'high' | 'xhigh' | 'max'`) is sent as `output_config.effort`
|
|
693
|
+
with adaptive thinking, and it is not converted into a token budget, so it is
|
|
694
|
+
not limited by `generationLimits.maxReasoningTokens`:
|
|
695
|
+
|
|
696
|
+
- `anthropic`: `output_config: { effort }` and `thinking: { type: 'adaptive' }`.
|
|
697
|
+
- `bifrost`: `extra_params: { output_config: { effort }, thinking: { type: 'adaptive' } }`
|
|
698
|
+
with the `x-bf-passthrough-extra-params: true` header (Bifrost's provider-parameter
|
|
699
|
+
passthrough). Its `reasoning` envelope is not sent for these models, because
|
|
700
|
+
Bifrost would turn `max_tokens` into a rejected `budget_tokens`.
|
|
701
|
+
- `bedrock`: the same fields in `additionalModelRequestFields`.
|
|
702
|
+
|
|
703
|
+
`'none'` and `'minimal'` are sent as `'low'` (Claude cannot be told to skip
|
|
704
|
+
thinking through effort); `'xhigh'` is sent as `'high'` on Opus/Sonnet 4.6, which
|
|
705
|
+
has no `xhigh`. Older Claude models keep the `reasoning.maxTokens` budget.
|
|
706
|
+
|
|
676
707
|
LiteLLM rejects caller-supplied `reasoning.maxTokens`,
|
|
677
708
|
`reasoning.includeThoughts`, and legacy `includeThoughts` before model discovery
|
|
678
709
|
or transport. Its Chat Completions protocol has no generic separate reasoning
|
|
@@ -821,6 +852,35 @@ for await (const text of ai.stream(messages, {
|
|
|
821
852
|
switch the caller asked for. LiteLLM and Bifrost forward it to their gateway.
|
|
822
853
|
- With `continueOnLength`, reasoning from every part is joined and reported once.
|
|
823
854
|
|
|
855
|
+
## Prompt Caching and Claude Refusals
|
|
856
|
+
|
|
857
|
+
Mark the end of a cacheable prompt prefix with `cacheHint` on a message or on a
|
|
858
|
+
text part (`true`, or `{ ttl: '5m' | '1h' }`). The Anthropic and Bifrost providers
|
|
859
|
+
forward it as `cache_control: { type: 'ephemeral' }` (plus `ttl`); every other
|
|
860
|
+
provider ignores it. On a `system` message it caches the system prompt. Cache
|
|
861
|
+
reads and writes come back in `usage.cacheReadInputTokens` and
|
|
862
|
+
`usage.cacheCreationInputTokens`.
|
|
863
|
+
|
|
864
|
+
```typescript
|
|
865
|
+
const response = await ai.chat(
|
|
866
|
+
[
|
|
867
|
+
{ role: 'system', content: longRubric, cacheHint: true },
|
|
868
|
+
{ role: 'user', content: question },
|
|
869
|
+
],
|
|
870
|
+
{ model: 'claude-opus-5-5', reasoning: { effort: 'medium' } },
|
|
871
|
+
);
|
|
872
|
+
console.log(response.usage?.cacheReadInputTokens);
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
Providers cap the number of cached prefixes (Anthropic allows 4) and need a
|
|
876
|
+
minimum prefix size; a shorter prefix is silently not cached.
|
|
877
|
+
|
|
878
|
+
When Claude declines a request (`stop_reason: 'refusal'`), `finishReason` is
|
|
879
|
+
`'refusal'` (distinct from `'content_filter'`) and `response.refusal` holds
|
|
880
|
+
`{ category, explanation }` from `stop_details`. It is not thrown. Streams call
|
|
881
|
+
`onRefusal(refusal)` and then `onFinishReason('refusal')`. Bifrost reports it only
|
|
882
|
+
if the gateway forwards the reason as a `refusal` finish reason.
|
|
883
|
+
|
|
824
884
|
## Continuing Truncated Replies
|
|
825
885
|
|
|
826
886
|
When a reply stops because it hit its output limit (`finishReason: 'length'`:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
1
|
+
import { n as toCacheControl, r as toCacheableTextBlocks, t as hasCacheHint } from "./cache-hint-BdVY0hrJ.js";
|
|
2
|
+
import { T as extractTextContent, _ as AuthenticationError, c as normalizeBaseAIOptions, d as prepareRequestControls, f as claudeRejectsThinkingBudget, h as AIError, l as normalizeChatOptions, m as toClaudeEffort, n as emitUsage, p as claudeSupportsEffort, t as cacheUsageFields, x as ModelNotFoundError, y as ContextLengthError } from "./usage-B40bJZ49.js";
|
|
3
|
+
import { n as rateLimitErrorFrom } from "./rate-limit-B9BFC8ld.js";
|
|
3
4
|
import { r as parseToolArguments } from "./tool-messages-0ND35mYX.js";
|
|
4
5
|
//#region src/shared/providers/anthropic.ts
|
|
5
6
|
/**
|
|
@@ -9,6 +10,14 @@ import { r as parseToolArguments } from "./tool-messages-0ND35mYX.js";
|
|
|
9
10
|
* including chat completions, streaming responses, and vision capabilities.
|
|
10
11
|
* Note: Claude models do not support embeddings - use OpenAI or another provider for that.
|
|
11
12
|
*/
|
|
13
|
+
/** Text blocks of a message, carrying `cache_control` where hinted. */
|
|
14
|
+
function toAnthropicTextBlocks(message) {
|
|
15
|
+
return toCacheableTextBlocks(message).map((block) => ({
|
|
16
|
+
type: "text",
|
|
17
|
+
text: block.text,
|
|
18
|
+
...block.cacheControl ? { cache_control: block.cacheControl } : {}
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
12
21
|
/**
|
|
13
22
|
* Anthropic Claude provider implementation that handles all interactions with Anthropic's API.
|
|
14
23
|
* Supports Claude models, streaming, vision capabilities, and function calling.
|
|
@@ -105,11 +114,17 @@ var AnthropicProvider = class {
|
|
|
105
114
|
...this.mapToolParams(options, anthropicMessages),
|
|
106
115
|
stream: false
|
|
107
116
|
};
|
|
108
|
-
const thinking = this.mapThinking(options, anthropicMessages);
|
|
117
|
+
const thinking = this.mapThinking(options, anthropicMessages, model);
|
|
109
118
|
if (thinking) requestParams.thinking = thinking;
|
|
119
|
+
const outputConfig = this.mapOutputConfig(options, model);
|
|
120
|
+
if (outputConfig) requestParams.output_config = outputConfig;
|
|
110
121
|
if (options.responseFormat?.type === "json_object") {
|
|
111
122
|
const jsonInstruction = "\n\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.";
|
|
112
|
-
requestParams.system
|
|
123
|
+
if (Array.isArray(requestParams.system)) requestParams.system = [...requestParams.system, {
|
|
124
|
+
type: "text",
|
|
125
|
+
text: jsonInstruction.trim()
|
|
126
|
+
}];
|
|
127
|
+
else requestParams.system = requestParams.system ? requestParams.system + jsonInstruction : jsonInstruction.trim();
|
|
113
128
|
}
|
|
114
129
|
const response = await this.client.messages.create(requestParams, {
|
|
115
130
|
signal: controls.signal,
|
|
@@ -126,16 +141,14 @@ var AnthropicProvider = class {
|
|
|
126
141
|
}));
|
|
127
142
|
const thinkingBlocks = extractThinkingBlocks(response.content);
|
|
128
143
|
if (toolCalls.length > 0 && thinkingBlocks.length > 0) toolCalls[0].thinkingBlocks = thinkingBlocks;
|
|
129
|
-
const usage =
|
|
130
|
-
promptTokens: response.usage.input_tokens,
|
|
131
|
-
completionTokens: response.usage.output_tokens,
|
|
132
|
-
totalTokens: response.usage.input_tokens + response.usage.output_tokens
|
|
133
|
-
};
|
|
144
|
+
const usage = mapAnthropicUsage(response.usage);
|
|
134
145
|
emitUsage(this.options, "anthropic", "chat", response.model || model, usage, startTime, options.usageTags);
|
|
146
|
+
const finishReason = this.mapFinishReason(response.stop_reason);
|
|
135
147
|
return {
|
|
136
148
|
content: textContent,
|
|
137
149
|
model: response.model,
|
|
138
|
-
finishReason
|
|
150
|
+
finishReason,
|
|
151
|
+
...finishReason === "refusal" ? { refusal: mapRefusal(response.stop_details) } : {},
|
|
139
152
|
usage,
|
|
140
153
|
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
141
154
|
};
|
|
@@ -255,22 +268,38 @@ var AnthropicProvider = class {
|
|
|
255
268
|
...this.mapHistoryToolParams(anthropicMessages),
|
|
256
269
|
stream: true
|
|
257
270
|
};
|
|
258
|
-
const thinking = this.mapThinking(options, anthropicMessages);
|
|
271
|
+
const thinking = this.mapThinking(options, anthropicMessages, model);
|
|
259
272
|
if (thinking) requestParams.thinking = thinking;
|
|
273
|
+
const outputConfig = this.mapOutputConfig(options, model);
|
|
274
|
+
if (outputConfig) requestParams.output_config = outputConfig;
|
|
260
275
|
const stream = await this.client.messages.create(requestParams, {
|
|
261
276
|
signal: controls.signal,
|
|
262
277
|
timeout: controls.timeout
|
|
263
278
|
});
|
|
264
279
|
let streamStopReason = null;
|
|
280
|
+
let streamStopDetails;
|
|
281
|
+
let streamUsage;
|
|
265
282
|
for await (const chunk of stream) {
|
|
266
|
-
if (chunk.type === "
|
|
283
|
+
if (chunk.type === "message_start" && chunk.message?.usage) streamUsage = { ...chunk.message.usage };
|
|
284
|
+
if (chunk.type === "message_delta") {
|
|
285
|
+
if (chunk.usage) streamUsage = {
|
|
286
|
+
...streamUsage,
|
|
287
|
+
...chunk.usage
|
|
288
|
+
};
|
|
289
|
+
if (chunk.delta?.stop_reason) {
|
|
290
|
+
streamStopReason = chunk.delta.stop_reason;
|
|
291
|
+
streamStopDetails = chunk.delta.stop_details;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
267
294
|
if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
|
|
268
295
|
if (options.onProgress) options.onProgress(chunk.delta.text);
|
|
269
296
|
yield chunk.delta.text;
|
|
270
297
|
}
|
|
271
298
|
}
|
|
272
|
-
|
|
273
|
-
|
|
299
|
+
const streamFinish = this.mapFinishReason(streamStopReason) ?? "stop";
|
|
300
|
+
if (streamFinish === "refusal") options.onRefusal?.(mapRefusal(streamStopDetails));
|
|
301
|
+
options.onFinishReason?.(streamFinish);
|
|
302
|
+
emitUsage(this.options, "anthropic", "stream", model, streamUsage ? mapAnthropicUsage(streamUsage) : void 0, startTime, options.usageTags);
|
|
274
303
|
} catch (error) {
|
|
275
304
|
if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", "anthropic", options.model);
|
|
276
305
|
if (options.signal?.aborted) throw new AIError("AI request aborted by caller", "AI_ABORTED", "anthropic", options.model);
|
|
@@ -423,6 +452,8 @@ var AnthropicProvider = class {
|
|
|
423
452
|
*/
|
|
424
453
|
mapMessagesToAnthropic(messages) {
|
|
425
454
|
let system;
|
|
455
|
+
const systemBlocks = [];
|
|
456
|
+
let systemHinted = false;
|
|
426
457
|
const anthropicMessages = [];
|
|
427
458
|
const answeredToolCallIds = new Set(messages.filter((message) => message.role === "tool" && message.tool_call_id).map((message) => message.tool_call_id));
|
|
428
459
|
const replayedToolUseIds = /* @__PURE__ */ new Set();
|
|
@@ -434,13 +465,16 @@ var AnthropicProvider = class {
|
|
|
434
465
|
const textContent = extractTextContent(message.content);
|
|
435
466
|
if (message.role === "system") {
|
|
436
467
|
system = system ? `${system}\n\n${textContent}` : textContent;
|
|
468
|
+
systemHinted ||= hasCacheHint(message);
|
|
469
|
+
systemBlocks.push(...toAnthropicTextBlocks(message));
|
|
437
470
|
continue;
|
|
438
471
|
}
|
|
439
472
|
if (message.role === "tool" && message.tool_call_id && replayedToolUseIds.has(message.tool_call_id)) {
|
|
440
473
|
const block = {
|
|
441
474
|
type: "tool_result",
|
|
442
475
|
tool_use_id: message.tool_call_id,
|
|
443
|
-
content: textContent
|
|
476
|
+
content: textContent,
|
|
477
|
+
...message.cacheHint ? { cache_control: toCacheControl(message.cacheHint) } : {}
|
|
444
478
|
};
|
|
445
479
|
const previous = anthropicMessages[anthropicMessages.length - 1];
|
|
446
480
|
if (previous?.role === "user" && Array.isArray(previous.content) && previous.content.every((part) => part.type === "tool_result")) previous.content.push(block);
|
|
@@ -464,19 +498,24 @@ var AnthropicProvider = class {
|
|
|
464
498
|
name: toolCall.function.name,
|
|
465
499
|
input: parseToolArguments(toolCall.function.arguments)
|
|
466
500
|
});
|
|
501
|
+
const cacheControl = toCacheControl(message.cacheHint);
|
|
502
|
+
const last = content[content.length - 1];
|
|
503
|
+
if (cacheControl && (last.type === "text" || last.type === "tool_use")) last.cache_control = cacheControl;
|
|
467
504
|
anthropicMessages.push({
|
|
468
505
|
role: "assistant",
|
|
469
506
|
content
|
|
470
507
|
});
|
|
471
508
|
continue;
|
|
472
509
|
}
|
|
510
|
+
const hintedBlocks = hasCacheHint(message) ? toAnthropicTextBlocks(message).filter((block) => block.text) : [];
|
|
473
511
|
anthropicMessages.push({
|
|
474
512
|
role: message.role === "assistant" ? "assistant" : "user",
|
|
475
|
-
content: textContent
|
|
513
|
+
content: hintedBlocks.length > 0 ? hintedBlocks : textContent
|
|
476
514
|
});
|
|
477
515
|
}
|
|
516
|
+
const hintedSystem = systemBlocks.filter((block) => block.text);
|
|
478
517
|
return {
|
|
479
|
-
system,
|
|
518
|
+
system: systemHinted ? hintedSystem.length > 0 ? hintedSystem : void 0 : system,
|
|
480
519
|
anthropicMessages
|
|
481
520
|
};
|
|
482
521
|
}
|
|
@@ -532,19 +571,31 @@ var AnthropicProvider = class {
|
|
|
532
571
|
* without thinking), thinking is left off for this request rather than
|
|
533
572
|
* sending a request Anthropic would reject.
|
|
534
573
|
*/
|
|
535
|
-
mapThinking(options, anthropicMessages) {
|
|
574
|
+
mapThinking(options, anthropicMessages, model) {
|
|
575
|
+
const effort = options.reasoning?.effort;
|
|
576
|
+
const effortDriven = effort !== void 0 && effort !== "none" && claudeSupportsEffort(model);
|
|
536
577
|
const budget = options.reasoning?.maxTokens || 0;
|
|
537
|
-
if (budget <= 0) return void 0;
|
|
578
|
+
if (budget <= 0 && !effortDriven) return void 0;
|
|
538
579
|
const lastAssistant = [...anthropicMessages].reverse().find((message) => message.role === "assistant");
|
|
539
580
|
if (lastAssistant && Array.isArray(lastAssistant.content) && lastAssistant.content.some((block) => block.type === "tool_use")) {
|
|
540
581
|
const firstType = lastAssistant.content[0]?.type;
|
|
541
582
|
if (firstType !== "thinking" && firstType !== "redacted_thinking") return;
|
|
542
583
|
}
|
|
584
|
+
if (effortDriven || claudeRejectsThinkingBudget(model)) return { type: "adaptive" };
|
|
543
585
|
return {
|
|
544
586
|
type: "enabled",
|
|
545
587
|
budget_tokens: budget
|
|
546
588
|
};
|
|
547
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Builds `output_config` for the effort of Claude 4.6+ models. Other models
|
|
592
|
+
* have no effort control, so the option is not sent to them.
|
|
593
|
+
*/
|
|
594
|
+
mapOutputConfig(options, model) {
|
|
595
|
+
const effort = options.reasoning?.effort;
|
|
596
|
+
if (effort === void 0 || !claudeSupportsEffort(model)) return void 0;
|
|
597
|
+
return { effort: toClaudeEffort(model, effort) };
|
|
598
|
+
}
|
|
548
599
|
mapTools(options) {
|
|
549
600
|
return options.tools && options.tools.length > 0 ? options.tools.map((tool) => ({
|
|
550
601
|
name: tool.function.name,
|
|
@@ -567,6 +618,7 @@ var AnthropicProvider = class {
|
|
|
567
618
|
case "max_tokens": return "length";
|
|
568
619
|
case "stop_sequence": return "stop";
|
|
569
620
|
case "tool_use": return "tool_calls";
|
|
621
|
+
case "refusal": return "refusal";
|
|
570
622
|
default: return "stop";
|
|
571
623
|
}
|
|
572
624
|
}
|
|
@@ -584,6 +636,28 @@ var AnthropicProvider = class {
|
|
|
584
636
|
return new AIError(error instanceof Error ? error.message : "Unknown Anthropic error occurred", "UNKNOWN_ERROR", "anthropic");
|
|
585
637
|
}
|
|
586
638
|
};
|
|
639
|
+
/** Refusal details from Anthropic's `stop_details`. */
|
|
640
|
+
function mapRefusal(details) {
|
|
641
|
+
return {
|
|
642
|
+
category: typeof details?.category === "string" ? details.category : null,
|
|
643
|
+
explanation: typeof details?.explanation === "string" ? details.explanation : null
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Token usage from an Anthropic usage object. `input_tokens` excludes cached
|
|
648
|
+
* tokens, so cache reads and writes are added to `promptTokens`.
|
|
649
|
+
*/
|
|
650
|
+
function mapAnthropicUsage(usage) {
|
|
651
|
+
const cache = cacheUsageFields(usage?.cache_read_input_tokens, usage?.cache_creation_input_tokens);
|
|
652
|
+
const promptTokens = (usage?.input_tokens || 0) + (cache.cacheReadInputTokens || 0) + (cache.cacheCreationInputTokens || 0);
|
|
653
|
+
const completionTokens = usage?.output_tokens || 0;
|
|
654
|
+
return {
|
|
655
|
+
promptTokens,
|
|
656
|
+
completionTokens,
|
|
657
|
+
totalTokens: promptTokens + completionTokens,
|
|
658
|
+
...cache
|
|
659
|
+
};
|
|
660
|
+
}
|
|
587
661
|
/**
|
|
588
662
|
* Thinking and redacted-thinking blocks from an Anthropic response, copied
|
|
589
663
|
* with only the fields the API accepts back.
|
|
@@ -604,4 +678,4 @@ function extractThinkingBlocks(content) {
|
|
|
604
678
|
//#endregion
|
|
605
679
|
export { AnthropicProvider };
|
|
606
680
|
|
|
607
|
-
//# sourceMappingURL=anthropic-
|
|
681
|
+
//# sourceMappingURL=anthropic-BXiE495e.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-BXiE495e.js","names":[],"sources":["../../src/shared/providers/anthropic.ts"],"sourcesContent":["/**\n * Anthropic Claude provider implementation\n *\n * Provides a standardized interface for interacting with Anthropic's Claude models,\n * including chat completions, streaming responses, and vision capabilities.\n * Note: Claude models do not support embeddings - use OpenAI or another provider for that.\n */\n\nimport {\n type CacheControlWire,\n hasCacheHint,\n toCacheableTextBlocks,\n toCacheControl,\n} from '../cache-hint';\nimport {\n type ClaudeEffort,\n claudeRejectsThinkingBudget,\n claudeSupportsEffort,\n toClaudeEffort,\n} from '../claude-models';\nimport { rateLimitErrorFrom } from '../rate-limit';\nimport {\n normalizeBaseAIOptions,\n normalizeChatOptions,\n type PreparedRequestControls,\n prepareRequestControls,\n} from '../safety';\nimport { parseToolArguments } from '../tool-messages';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIRefusal,\n AIResponse,\n AIThinkingBlock,\n AIToolCall,\n AnthropicOptions,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n TokenUsage,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport {\n AIError,\n AuthenticationError,\n ContextLengthError,\n extractTextContent,\n ModelNotFoundError,\n} from '../types';\nimport { cacheUsageFields, emitUsage } from './usage';\n\ntype AnthropicCacheControl = { cache_control?: CacheControlWire };\n\ntype AnthropicTextBlock = {\n type: 'text';\n text: string;\n} & AnthropicCacheControl;\n\ntype AnthropicContentBlock =\n | AIThinkingBlock\n | AnthropicTextBlock\n | ({\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n } & AnthropicCacheControl)\n | ({\n type: 'tool_result';\n tool_use_id: string;\n content: string;\n } & AnthropicCacheControl);\n\n/** Text blocks of a message, carrying `cache_control` where hinted. */\nfunction toAnthropicTextBlocks(message: AIMessage): AnthropicTextBlock[] {\n return toCacheableTextBlocks(message).map((block) => ({\n type: 'text' as const,\n text: block.text,\n ...(block.cacheControl ? { cache_control: block.cacheControl } : {}),\n }));\n}\n\ninterface AnthropicMessage {\n role: 'user' | 'assistant';\n content: string | AnthropicContentBlock[];\n}\n\n// Note: This implementation will require @anthropic-ai/sdk package\n// For now, this is a placeholder that defines the interface\n\n/**\n * Anthropic Claude provider implementation that handles all interactions with Anthropic's API.\n * Supports Claude models, streaming, vision capabilities, and function calling.\n * Does not support embeddings (use OpenAI or another provider for embeddings).\n */\nexport class AnthropicProvider implements AIInterface {\n private options: AnthropicOptions;\n private client: any; // Will be Anthropic instance from @anthropic-ai/sdk\n\n /**\n * Creates a new Anthropic provider instance\n * @param options - Configuration options for the Anthropic provider\n */\n constructor(options: AnthropicOptions) {\n this.options = normalizeBaseAIOptions({\n defaultModel: 'claude-3-5-sonnet-20241022',\n anthropicVersion: '2023-06-01',\n ...options,\n });\n\n // Initialize Anthropic client\n this.initializeClientSync();\n }\n\n private initializeClientSync() {\n try {\n // Dynamic import in constructor - this will work if the package is installed\n import('@anthropic-ai/sdk')\n .then(({ Anthropic }) => {\n this.client = new Anthropic({\n apiKey: this.options.apiKey,\n baseURL: this.options.baseUrl,\n timeout: this.options.timeout,\n maxRetries: this.options.maxRetries,\n defaultHeaders: {\n 'anthropic-version': this.options.anthropicVersion,\n ...this.options.headers,\n },\n });\n })\n .catch(() => {\n // Client will be null and we'll handle it in methods\n });\n } catch (_error) {\n // Client will be null and we'll handle it in methods\n }\n }\n\n /**\n * Ensures the Anthropic client is initialized by dynamically importing the SDK\n * @throws {AIError} When the Anthropic SDK cannot be loaded\n * @private\n */\n private async ensureClient() {\n if (!this.client) {\n try {\n const { Anthropic } = await import('@anthropic-ai/sdk');\n this.client = new Anthropic({\n apiKey: this.options.apiKey,\n baseURL: this.options.baseUrl,\n timeout: this.options.timeout,\n maxRetries: this.options.maxRetries,\n defaultHeaders: {\n 'anthropic-version': this.options.anthropicVersion,\n ...this.options.headers,\n },\n });\n } catch (_error) {\n throw new AIError(\n 'Failed to initialize Anthropic client. Make sure @anthropic-ai/sdk is installed.',\n 'INITIALIZATION_ERROR',\n 'anthropic',\n );\n }\n }\n }\n\n /**\n * Generate a chat completion using Claude models\n * @param messages - Array of conversation messages\n * @param options - Optional configuration for the chat completion\n * @returns Promise resolving to the AI response with content and metadata\n * @throws {AIError} When the API request fails or SDK is not available\n *\n * @example\n * ```typescript\n * const response = await provider.chat([\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'Explain quantum computing' }\n * ], {\n * model: 'claude-3-5-sonnet-20241022',\n * maxTokens: 1000\n * });\n * ```\n */\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n try {\n await this.ensureClient();\n\n const { system, anthropicMessages } =\n this.mapMessagesToAnthropic(messages);\n\n const model = options.model || this.options.defaultModel;\n options = normalizeChatOptions(this.options, options, 'anthropic', model);\n controls = prepareRequestControls(this.options, options);\n\n // Build request parameters\n const requestParams: Record<string, any> = {\n model,\n messages: anthropicMessages,\n max_tokens: options.maxTokens,\n temperature: options.temperature,\n top_p: options.topP,\n stop_sequences: Array.isArray(options.stop)\n ? options.stop\n : options.stop\n ? [options.stop]\n : undefined,\n system: system || undefined,\n ...this.mapToolParams(options, anthropicMessages),\n stream: false,\n };\n const thinking = this.mapThinking(options, anthropicMessages, model);\n if (thinking) {\n requestParams.thinking = thinking;\n }\n const outputConfig = this.mapOutputConfig(options, model);\n if (outputConfig) {\n requestParams.output_config = outputConfig;\n }\n\n // Add response format if specified\n if (options.responseFormat?.type === 'json_object') {\n const jsonInstruction =\n '\\n\\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.';\n if (Array.isArray(requestParams.system)) {\n // Appended after the cached blocks so the instruction never\n // changes the cached prefix.\n requestParams.system = [\n ...requestParams.system,\n { type: 'text', text: jsonInstruction.trim() },\n ];\n } else {\n requestParams.system = requestParams.system\n ? requestParams.system + jsonInstruction\n : jsonInstruction.trim();\n }\n }\n\n const response = await this.client.messages.create(requestParams, {\n signal: controls.signal,\n timeout: controls.timeout,\n });\n\n // Extract text content and tool calls from response\n const textContent = response.content\n .filter((block: any) => block.type === 'text')\n .map((block: any) => block.text)\n .join('');\n\n const toolCalls: AIToolCall[] = response.content\n .filter((block: any) => block.type === 'tool_use')\n .map((block: any) => ({\n id: block.id,\n type: 'function' as const,\n function: {\n name: block.name,\n arguments: JSON.stringify(block.input),\n },\n }));\n\n // Extended thinking: the replayed tool_use turn must start with these\n // blocks (signatures intact), so they ride on the first tool call.\n const thinkingBlocks = extractThinkingBlocks(response.content);\n if (toolCalls.length > 0 && thinkingBlocks.length > 0) {\n toolCalls[0].thinkingBlocks = thinkingBlocks;\n }\n\n const usage = mapAnthropicUsage(response.usage);\n emitUsage(\n this.options,\n 'anthropic',\n 'chat',\n response.model || model,\n usage,\n startTime,\n options.usageTags,\n );\n\n const finishReason = this.mapFinishReason(response.stop_reason);\n return {\n content: textContent,\n model: response.model,\n finishReason,\n ...(finishReason === 'refusal'\n ? { refusal: mapRefusal(response.stop_details) }\n : {}),\n usage,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n };\n } catch (error) {\n if (controls?.didTimeout()) {\n throw new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n 'anthropic',\n options.model,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n 'anthropic',\n options.model,\n );\n }\n throw this.mapError(error);\n } finally {\n controls?.cleanup();\n }\n }\n\n async complete(\n prompt: string,\n options: CompletionOptions = {},\n ): Promise<AIResponse> {\n return this.chat([{ role: 'user', content: prompt }], {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n n: options.n,\n stop: options.stop,\n stream: options.stream,\n onProgress: options.onProgress,\n signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\n usageTags: options.usageTags,\n continueOnLength: options.continueOnLength,\n });\n }\n\n /**\n * Simple message interface for single-turn interactions with optional history\n *\n * @param text - The message text to send\n * @param options - Configuration options including history, model, etc.\n * @returns Promise resolving to the response content string\n *\n * @example\n * ```typescript\n * // Simple usage\n * const response = await provider.message('Hello!');\n *\n * // With history\n * const response = await provider.message('What was my question?', {\n * history: [\n * { role: 'user', content: 'What is 2+2?' },\n * { role: 'assistant', content: '4' }\n * ]\n * });\n * ```\n */\n async message(text: string, options: MessageOptions = {}): Promise<string> {\n // Build messages array from history + current message\n const messages: AIMessage[] = [\n ...(options.history || []),\n { role: options.role || 'user', content: text },\n ];\n\n const response = await this.chat(messages, {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n stop: options.stop,\n stream: options.stream,\n frequencyPenalty: options.frequencyPenalty,\n presencePenalty: options.presencePenalty,\n responseFormat: options.responseFormat,\n seed: options.seed,\n tools: options.tools,\n toolChoice: options.toolChoice,\n onProgress: options.onProgress,\n signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\n usageTags: options.usageTags,\n continueOnLength: options.continueOnLength,\n });\n\n return response.content;\n }\n\n async embed(\n _text: string | string[],\n _options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n // Anthropic Claude doesn't provide embeddings API\n throw new AIError(\n 'Anthropic Claude does not support embeddings. Use OpenAI or another provider for embeddings.',\n 'NOT_SUPPORTED',\n 'anthropic',\n );\n }\n\n async embedImage(\n _image: string | Buffer,\n _options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Anthropic Claude does not support image embeddings. Use OpenAI or Gemini.',\n 'NOT_SUPPORTED',\n 'anthropic',\n );\n }\n\n async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options: ImageDescriptionOptions = {},\n ): Promise<string> {\n // Note: Claude does support vision, but we're keeping this as a stub for now\n // A full implementation could use the chat API with image content\n throw new AIError(\n 'Image description is not yet implemented for Anthropic. Use OpenAI or Gemini.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n async generateImage(\n _prompt: string,\n _options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n throw new AIError(\n 'Anthropic Claude does not support image generation. Use OpenAI or Gemini.',\n 'NOT_SUPPORTED',\n 'anthropic',\n );\n }\n\n /**\n * Streams a chat completion as text chunks.\n *\n * Streams yield text only and never surface tool calls, so `options.tools`\n * and `options.toolChoice` are ignored: tools are declared only when the\n * replayed history contains `tool_use` blocks (Anthropic requires it), with\n * `tool_choice: { type: 'none' }`. Use `chat()` for rounds that may call\n * tools.\n */\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n try {\n await this.ensureClient();\n\n const { system, anthropicMessages } =\n this.mapMessagesToAnthropic(messages);\n\n const model = options.model || this.options.defaultModel;\n options = normalizeChatOptions(this.options, options, 'anthropic', model);\n controls = prepareRequestControls(this.options, options);\n\n const requestParams: Record<string, any> = {\n model,\n messages: anthropicMessages,\n max_tokens: options.maxTokens,\n temperature: options.temperature,\n top_p: options.topP,\n stop_sequences: Array.isArray(options.stop)\n ? options.stop\n : options.stop\n ? [options.stop]\n : undefined,\n system: system || undefined,\n // stream() yields text only, so a tool call would be lost: declare\n // tools only when the replayed history requires it, with\n // tool_choice none.\n ...this.mapHistoryToolParams(anthropicMessages),\n stream: true,\n };\n const thinking = this.mapThinking(options, anthropicMessages, model);\n if (thinking) {\n requestParams.thinking = thinking;\n }\n const outputConfig = this.mapOutputConfig(options, model);\n if (outputConfig) {\n requestParams.output_config = outputConfig;\n }\n const stream = await this.client.messages.create(requestParams, {\n signal: controls.signal,\n timeout: controls.timeout,\n });\n\n let streamStopReason: string | null = null;\n let streamStopDetails: unknown;\n let streamUsage: Record<string, unknown> | undefined;\n for await (const chunk of stream) {\n if (chunk.type === 'message_start' && chunk.message?.usage) {\n streamUsage = { ...chunk.message.usage };\n }\n if (chunk.type === 'message_delta') {\n // message_delta usage is cumulative for the fields it carries.\n if (chunk.usage) streamUsage = { ...streamUsage, ...chunk.usage };\n if (chunk.delta?.stop_reason) {\n streamStopReason = chunk.delta.stop_reason;\n streamStopDetails = chunk.delta.stop_details;\n }\n }\n if (\n chunk.type === 'content_block_delta' &&\n chunk.delta.type === 'text_delta'\n ) {\n if (options.onProgress) {\n options.onProgress(chunk.delta.text);\n }\n yield chunk.delta.text;\n }\n }\n const streamFinish = this.mapFinishReason(streamStopReason) ?? 'stop';\n if (streamFinish === 'refusal') {\n options.onRefusal?.(mapRefusal(streamStopDetails));\n }\n options.onFinishReason?.(streamFinish);\n\n emitUsage(\n this.options,\n 'anthropic',\n 'stream',\n model!,\n streamUsage ? mapAnthropicUsage(streamUsage) : undefined,\n startTime,\n options.usageTags,\n );\n } catch (error) {\n if (controls?.didTimeout()) {\n throw new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n 'anthropic',\n options.model,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n 'anthropic',\n options.model,\n );\n }\n throw this.mapError(error);\n } finally {\n controls?.cleanup();\n }\n }\n\n async countTokens(text: string): Promise<number> {\n // Anthropic doesn't provide a direct token counting API\n // This is an approximation - Claude uses a different tokenizer than OpenAI\n return Math.ceil(text.length / 3.5); // Slightly different ratio for Claude\n }\n\n async getModels(): Promise<AIModel[]> {\n // Return static list of known Claude models\n return [\n {\n id: 'claude-3-5-sonnet-20241022',\n name: 'Claude 3.5 Sonnet',\n description: 'Most intelligent Claude model with balanced performance',\n contextLength: 200000,\n capabilities: ['text', 'chat', 'vision', 'functions'],\n supportsFunctions: true,\n supportsVision: true,\n },\n {\n id: 'claude-3-5-haiku-20241022',\n name: 'Claude 3.5 Haiku',\n description: 'Fastest Claude model for simple tasks',\n contextLength: 200000,\n capabilities: ['text', 'chat', 'vision'],\n supportsFunctions: true,\n supportsVision: true,\n },\n {\n id: 'claude-3-opus-20240229',\n name: 'Claude 3 Opus',\n description: 'Most powerful Claude model for complex tasks',\n contextLength: 200000,\n capabilities: ['text', 'chat', 'vision', 'functions'],\n supportsFunctions: true,\n supportsVision: true,\n },\n {\n id: 'claude-3-sonnet-20240229',\n name: 'Claude 3 Sonnet',\n description: 'Balanced Claude model for most tasks',\n contextLength: 200000,\n capabilities: ['text', 'chat', 'vision'],\n supportsFunctions: true,\n supportsVision: true,\n },\n {\n id: 'claude-3-haiku-20240307',\n name: 'Claude 3 Haiku',\n description: 'Fast Claude model for simple tasks',\n contextLength: 200000,\n capabilities: ['text', 'chat', 'vision'],\n supportsFunctions: false,\n supportsVision: true,\n },\n ];\n }\n\n async getCapabilities(): Promise<AICapabilities> {\n return {\n chat: true,\n completion: true,\n embeddings: false, // Claude doesn't support embeddings\n streaming: true,\n functions: true,\n vision: true,\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 200000,\n supportedOperations: [\n 'chat',\n 'completion',\n 'streaming',\n 'functions',\n 'vision',\n ],\n };\n }\n\n // ============================================================================\n // Video Generation Methods (Not supported - use gemini, byteplus-modelark,\n // or openai-compat-video provider)\n // ============================================================================\n\n async submitVideoGenerationJob(\n _options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n throw new AIError(\n 'Video generation is not supported by Anthropic provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n async getVideoGenerationJob(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n throw new AIError(\n 'Video generation is not supported by Anthropic provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n async fetchVideoGenerationResult(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n throw new AIError(\n 'Video generation is not supported by Anthropic provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n throw new AIError(\n 'Video generation is not supported by Anthropic provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n async validateVideoGenerationAccess(): Promise<boolean> {\n throw new AIError(\n 'Video generation is not supported by Anthropic provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n // ============================================================================\n // TTS Methods (Not supported - use Qwen3-TTS provider)\n // ============================================================================\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n throw new AIError(\n 'TTS is not supported by Anthropic provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by Anthropic provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async cloneVoice(_options: VoiceCloneOptions): Promise<Voice> {\n throw new AIError(\n 'Voice cloning is not supported by Anthropic provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by Anthropic provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by Anthropic provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'anthropic',\n );\n }\n\n /**\n * Maps internal messages to Anthropic Messages API turns.\n *\n * Assistant `tool_calls` become `tool_use` blocks and `role: 'tool'`\n * results become `tool_result` blocks paired by `tool_call_id`\n * (`tool_use_id`). Consecutive tool results are grouped into one user turn,\n * as Anthropic requires every result for a turn to follow it directly.\n * Only answered calls are replayed as `tool_use`, and only results whose\n * `tool_call_id` matches a replayed call become `tool_result`. A result\n * without `tool_call_id`, or one answering no replayed call, falls back to\n * plain text (and a call left unanswered keeps only its text).\n */\n private mapMessagesToAnthropic(messages: AIMessage[]): {\n system?: string | AnthropicTextBlock[];\n anthropicMessages: AnthropicMessage[];\n } {\n // Anthropic handles system messages separately\n let system: string | undefined;\n // Only built when a system message carries a cache hint: breakpoints\n // need block form, so the system prompt is sent as text blocks then.\n const systemBlocks: AnthropicTextBlock[] = [];\n let systemHinted = false;\n const anthropicMessages: AnthropicMessage[] = [];\n const answeredToolCallIds = new Set(\n messages\n .filter((message) => message.role === 'tool' && message.tool_call_id)\n .map((message) => message.tool_call_id as string),\n );\n // Calls replayed as tool_use: those with an answer. Only results for\n // these ids become tool_result blocks; any other result is an orphan\n // Anthropic would reject, so it falls back to plain text.\n const replayedToolUseIds = new Set<string>();\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const toolCall of message.tool_calls || []) {\n if (answeredToolCallIds.has(toolCall.id)) {\n replayedToolUseIds.add(toolCall.id);\n }\n }\n }\n\n for (const message of messages) {\n const textContent = extractTextContent(message.content);\n if (message.role === 'system') {\n // Combine multiple system messages\n system = system ? `${system}\\n\\n${textContent}` : textContent;\n systemHinted ||= hasCacheHint(message);\n systemBlocks.push(...toAnthropicTextBlocks(message));\n continue;\n }\n\n if (\n message.role === 'tool' &&\n message.tool_call_id &&\n replayedToolUseIds.has(message.tool_call_id)\n ) {\n const block: AnthropicContentBlock = {\n type: 'tool_result',\n tool_use_id: message.tool_call_id,\n content: textContent,\n ...(message.cacheHint\n ? { cache_control: toCacheControl(message.cacheHint) }\n : {}),\n };\n const previous = anthropicMessages[anthropicMessages.length - 1];\n if (\n previous?.role === 'user' &&\n Array.isArray(previous.content) &&\n previous.content.every((part) => part.type === 'tool_result')\n ) {\n previous.content.push(block);\n } else {\n anthropicMessages.push({ role: 'user', content: [block] });\n }\n continue;\n }\n\n const toolCalls =\n message.role === 'assistant'\n ? (message.tool_calls || []).filter((toolCall) =>\n answeredToolCallIds.has(toolCall.id),\n )\n : [];\n if (toolCalls.length > 0) {\n const content: AnthropicContentBlock[] = [];\n for (const toolCall of toolCalls) {\n for (const block of toolCall.thinkingBlocks || []) {\n content.push({ ...block });\n }\n }\n if (textContent) {\n content.push({ type: 'text', text: textContent });\n }\n for (const toolCall of toolCalls) {\n content.push({\n type: 'tool_use',\n id: toolCall.id,\n name: toolCall.function.name,\n input: parseToolArguments(toolCall.function.arguments),\n });\n }\n const cacheControl = toCacheControl(message.cacheHint);\n const last = content[content.length - 1];\n if (\n cacheControl &&\n (last.type === 'text' || last.type === 'tool_use')\n ) {\n last.cache_control = cacheControl;\n }\n anthropicMessages.push({ role: 'assistant', content });\n continue;\n }\n\n const hintedBlocks = hasCacheHint(message)\n ? toAnthropicTextBlocks(message).filter((block) => block.text)\n : [];\n anthropicMessages.push({\n role: message.role === 'assistant' ? 'assistant' : 'user',\n content: hintedBlocks.length > 0 ? hintedBlocks : textContent,\n });\n }\n\n const hintedSystem = systemBlocks.filter((block) => block.text);\n return {\n system: systemHinted\n ? hintedSystem.length > 0\n ? hintedSystem\n : undefined\n : system,\n anthropicMessages,\n };\n }\n\n /**\n * Builds the `tools` / `tool_choice` request fields.\n *\n * Anthropic rejects a request whose history contains `tool_use` or\n * `tool_result` blocks unless `tools` is declared. A tool loop's final,\n * tool-less round (no `tools`, typically `toolChoice: 'none'`) still\n * replays that history, so when the caller declares no tools but the\n * history uses some, this declares a minimal definition for each tool the\n * history references and sets `tool_choice: { type: 'none' }` so the model\n * answers in text instead of calling them.\n */\n private mapToolParams(\n options: ChatOptions,\n anthropicMessages: AnthropicMessage[],\n ): { tools?: Record<string, any>[]; tool_choice?: Record<string, any> } {\n const tools = this.mapTools(options);\n if (tools) {\n return { tools, tool_choice: this.mapToolChoice(options.toolChoice) };\n }\n return this.mapHistoryToolParams(anthropicMessages);\n }\n\n /**\n * Declares a minimal definition for each tool the replayed history\n * references, with `tool_choice: { type: 'none' }`, or nothing when the\n * history has no `tool_use` blocks. Used for tool-less rounds and for\n * every stream (streams never yield tool calls).\n */\n private mapHistoryToolParams(anthropicMessages: AnthropicMessage[]): {\n tools?: Record<string, any>[];\n tool_choice?: Record<string, any>;\n } {\n // mapMessagesToAnthropic emits tool_result only for ids it replayed as\n // tool_use (orphan results become text), so tool_use names cover every\n // tool the history references.\n const historyToolNames = new Set<string>();\n for (const message of anthropicMessages) {\n if (!Array.isArray(message.content)) continue;\n for (const block of message.content) {\n if (block.type === 'tool_use') historyToolNames.add(block.name);\n }\n }\n if (historyToolNames.size === 0) {\n return {};\n }\n\n return {\n tools: [...historyToolNames].map((name) => ({\n name,\n description:\n 'Used earlier in this conversation; not available for this turn.',\n input_schema: { type: 'object' },\n })),\n tool_choice: { type: 'none' },\n };\n }\n\n /**\n * Builds the `thinking` request field for `options.reasoning.maxTokens`.\n *\n * While thinking is enabled, Anthropic requires the assistant turn of an\n * in-progress tool loop (the last assistant turn, when it holds `tool_use`)\n * to start with its thinking blocks. They are replayed from\n * `AIToolCall.thinkingBlocks`; when that turn has none (the history came\n * from another provider, a store that dropped the field, or a call made\n * without thinking), thinking is left off for this request rather than\n * sending a request Anthropic would reject.\n */\n private mapThinking(\n options: ChatOptions,\n anthropicMessages: AnthropicMessage[],\n model: string | undefined,\n ):\n | { type: 'enabled'; budget_tokens: number }\n | { type: 'adaptive' }\n | undefined {\n const effort = options.reasoning?.effort;\n // Claude 4.6+ models are steered by `output_config.effort`, with thinking\n // adaptive; effort 'none' asks for no thinking (where it can be omitted).\n const effortDriven =\n effort !== undefined && effort !== 'none' && claudeSupportsEffort(model);\n const budget = options.reasoning?.maxTokens || 0;\n if (budget <= 0 && !effortDriven) return undefined;\n\n const lastAssistant = [...anthropicMessages]\n .reverse()\n .find((message) => message.role === 'assistant');\n if (\n lastAssistant &&\n Array.isArray(lastAssistant.content) &&\n lastAssistant.content.some((block) => block.type === 'tool_use')\n ) {\n const firstType = lastAssistant.content[0]?.type;\n if (firstType !== 'thinking' && firstType !== 'redacted_thinking') {\n return undefined;\n }\n }\n\n // Models that reject `budget_tokens` (Opus/Sonnet 4.7+, Fable, ...) can\n // only think adaptively; the budget is not sent.\n if (effortDriven || claudeRejectsThinkingBudget(model)) {\n return { type: 'adaptive' };\n }\n return { type: 'enabled', budget_tokens: budget };\n }\n\n /**\n * Builds `output_config` for the effort of Claude 4.6+ models. Other models\n * have no effort control, so the option is not sent to them.\n */\n private mapOutputConfig(\n options: ChatOptions,\n model: string | undefined,\n ): { effort: ClaudeEffort } | undefined {\n const effort = options.reasoning?.effort;\n if (effort === undefined || !claudeSupportsEffort(model)) return undefined;\n return { effort: toClaudeEffort(model, effort) };\n }\n\n private mapTools(options: ChatOptions): Record<string, any>[] | undefined {\n return options.tools && options.tools.length > 0\n ? options.tools.map((tool) => ({\n name: tool.function.name,\n description: tool.function.description || '',\n input_schema: tool.function.parameters || { type: 'object' },\n }))\n : undefined;\n }\n\n private mapToolChoice(\n toolChoice?:\n | 'auto'\n | 'none'\n | { type: 'function'; function: { name: string } },\n ): any {\n if (!toolChoice || toolChoice === 'auto') {\n return { type: 'auto' };\n }\n\n if (toolChoice === 'none') {\n return { type: 'none' };\n }\n\n if (typeof toolChoice === 'object' && toolChoice.type === 'function') {\n return {\n type: 'tool',\n name: toolChoice.function.name,\n };\n }\n\n return { type: 'auto' };\n }\n\n private mapFinishReason(reason: string | null): AIResponse['finishReason'] {\n switch (reason) {\n case 'end_turn':\n return 'stop';\n case 'max_tokens':\n return 'length';\n case 'stop_sequence':\n return 'stop';\n case 'tool_use':\n return 'tool_calls';\n case 'refusal':\n return 'refusal';\n default:\n return 'stop';\n }\n }\n\n private mapError(error: unknown): AIError {\n if (error instanceof AIError) {\n return error;\n }\n\n // Map common HTTP status codes from Anthropic API\n if (typeof error === 'object' && error !== null && 'status' in error) {\n const apiError = error as { status: number; message?: string };\n switch (apiError.status) {\n case 401:\n return new AuthenticationError('anthropic');\n case 429:\n return rateLimitErrorFrom('anthropic', error);\n case 404:\n return new ModelNotFoundError(\n apiError.message || 'Model not found',\n 'anthropic',\n );\n case 413:\n return new ContextLengthError('anthropic');\n }\n }\n\n const errorMessage =\n error instanceof Error\n ? error.message\n : 'Unknown Anthropic error occurred';\n return new AIError(errorMessage, 'UNKNOWN_ERROR', 'anthropic');\n }\n}\n\n/** Refusal details from Anthropic's `stop_details`. */\nfunction mapRefusal(details: any): AIRefusal {\n return {\n category: typeof details?.category === 'string' ? details.category : null,\n explanation:\n typeof details?.explanation === 'string' ? details.explanation : null,\n };\n}\n\n/**\n * Token usage from an Anthropic usage object. `input_tokens` excludes cached\n * tokens, so cache reads and writes are added to `promptTokens`.\n */\nfunction mapAnthropicUsage(usage: any): TokenUsage {\n const cache = cacheUsageFields(\n usage?.cache_read_input_tokens,\n usage?.cache_creation_input_tokens,\n );\n const promptTokens =\n (usage?.input_tokens || 0) +\n (cache.cacheReadInputTokens || 0) +\n (cache.cacheCreationInputTokens || 0);\n const completionTokens = usage?.output_tokens || 0;\n return {\n promptTokens,\n completionTokens,\n totalTokens: promptTokens + completionTokens,\n ...cache,\n };\n}\n\n/**\n * Thinking and redacted-thinking blocks from an Anthropic response, copied\n * with only the fields the API accepts back.\n */\nfunction extractThinkingBlocks(content: any[]): AIThinkingBlock[] {\n const blocks: AIThinkingBlock[] = [];\n for (const block of content || []) {\n if (block?.type === 'thinking') {\n blocks.push({\n type: 'thinking',\n thinking: block.thinking,\n signature: block.signature,\n });\n } else if (block?.type === 'redacted_thinking') {\n blocks.push({ type: 'redacted_thinking', data: block.data });\n }\n }\n return blocks;\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
import { n as rateLimitErrorFrom } from "./rate-limit-
|
|
1
|
+
import { T as extractTextContent, _ as AuthenticationError, c as normalizeBaseAIOptions, d as prepareRequestControls, f as claudeRejectsThinkingBudget, h as AIError, l as normalizeChatOptions, m as toClaudeEffort, n as emitUsage, p as claudeSupportsEffort, t as cacheUsageFields, u as normalizeImageGenerationOptions, x as ModelNotFoundError, y as ContextLengthError } from "./usage-B40bJZ49.js";
|
|
2
|
+
import { n as rateLimitErrorFrom } from "./rate-limit-B9BFC8ld.js";
|
|
3
3
|
//#region src/shared/providers/bedrock.ts
|
|
4
4
|
/**
|
|
5
5
|
* AWS Bedrock provider implementation
|
|
@@ -275,13 +275,14 @@ var BedrockProvider = class {
|
|
|
275
275
|
if (options.onProgress) options.onProgress(text);
|
|
276
276
|
yield text;
|
|
277
277
|
}
|
|
278
|
-
if (event.metadata?.usage) usage =
|
|
279
|
-
promptTokens: event.metadata.usage.inputTokens || 0,
|
|
280
|
-
completionTokens: event.metadata.usage.outputTokens || 0,
|
|
281
|
-
totalTokens: event.metadata.usage.totalTokens || 0
|
|
282
|
-
};
|
|
278
|
+
if (event.metadata?.usage) usage = mapBedrockUsage(event.metadata.usage);
|
|
283
279
|
}
|
|
284
|
-
|
|
280
|
+
const streamFinish = this.mapBedrockFinishReason(streamStopReason) ?? "stop";
|
|
281
|
+
if (streamFinish === "refusal") options.onRefusal?.({
|
|
282
|
+
category: null,
|
|
283
|
+
explanation: null
|
|
284
|
+
});
|
|
285
|
+
options.onFinishReason?.(streamFinish);
|
|
285
286
|
emitUsage(this.options, "bedrock", "stream", modelId || "unknown", usage, startTime, options.usageTags);
|
|
286
287
|
} catch (error) {
|
|
287
288
|
if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", "bedrock", options.model);
|
|
@@ -483,7 +484,12 @@ var BedrockProvider = class {
|
|
|
483
484
|
...Object.keys(inferenceConfig).length > 0 && { inferenceConfig },
|
|
484
485
|
...systemPrompt && { system: [{ text: systemPrompt }] }
|
|
485
486
|
};
|
|
486
|
-
|
|
487
|
+
const effort = options.reasoning?.effort;
|
|
488
|
+
if (effort !== void 0 && claudeSupportsEffort(modelId)) request.additionalModelRequestFields = {
|
|
489
|
+
output_config: { effort: toClaudeEffort(modelId, effort) },
|
|
490
|
+
...effort !== "none" ? { thinking: { type: "adaptive" } } : {}
|
|
491
|
+
};
|
|
492
|
+
else if ((options.reasoning?.maxTokens || 0) > 0) request.additionalModelRequestFields = { thinking: claudeRejectsThinkingBudget(modelId) ? { type: "adaptive" } : {
|
|
487
493
|
type: "enabled",
|
|
488
494
|
budget_tokens: options.reasoning?.maxTokens
|
|
489
495
|
} };
|
|
@@ -540,6 +546,7 @@ var BedrockProvider = class {
|
|
|
540
546
|
};
|
|
541
547
|
}
|
|
542
548
|
mapConverseResponse(response, modelId) {
|
|
549
|
+
const finishReason = this.mapBedrockFinishReason(response.stopReason);
|
|
543
550
|
const contentBlocks = response.output?.message?.content || [];
|
|
544
551
|
const textContent = contentBlocks.filter((block) => typeof block.text === "string").map((block) => block.text).join("");
|
|
545
552
|
const toolCalls = contentBlocks.filter((block) => block.toolUse).map((block) => ({
|
|
@@ -550,15 +557,15 @@ var BedrockProvider = class {
|
|
|
550
557
|
arguments: JSON.stringify(block.toolUse.input || {})
|
|
551
558
|
}
|
|
552
559
|
}));
|
|
553
|
-
const usage = response.usage &&
|
|
554
|
-
promptTokens: response.usage.inputTokens || 0,
|
|
555
|
-
completionTokens: response.usage.outputTokens || 0,
|
|
556
|
-
totalTokens: response.usage.totalTokens || 0
|
|
557
|
-
};
|
|
560
|
+
const usage = response.usage && mapBedrockUsage(response.usage);
|
|
558
561
|
return {
|
|
559
562
|
content: textContent,
|
|
560
563
|
model: modelId,
|
|
561
|
-
finishReason
|
|
564
|
+
finishReason,
|
|
565
|
+
...finishReason === "refusal" ? { refusal: {
|
|
566
|
+
category: null,
|
|
567
|
+
explanation: null
|
|
568
|
+
} } : {},
|
|
562
569
|
usage,
|
|
563
570
|
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
564
571
|
};
|
|
@@ -569,6 +576,7 @@ var BedrockProvider = class {
|
|
|
569
576
|
case "max_tokens": return "length";
|
|
570
577
|
case "stop_sequence": return "stop";
|
|
571
578
|
case "tool_use": return "tool_calls";
|
|
579
|
+
case "refusal": return "refusal";
|
|
572
580
|
default: return "stop";
|
|
573
581
|
}
|
|
574
582
|
}
|
|
@@ -758,7 +766,20 @@ var BedrockProvider = class {
|
|
|
758
766
|
return new AIError(error instanceof Error ? error.message : "Unknown Bedrock error occurred", "UNKNOWN_ERROR", "bedrock");
|
|
759
767
|
}
|
|
760
768
|
};
|
|
769
|
+
/**
|
|
770
|
+
* Token usage from a Converse usage object. `inputTokens` excludes cached
|
|
771
|
+
* tokens, so cache reads and writes are added to `promptTokens`.
|
|
772
|
+
*/
|
|
773
|
+
function mapBedrockUsage(usage) {
|
|
774
|
+
const cache = cacheUsageFields(usage.cacheReadInputTokens, usage.cacheWriteInputTokens);
|
|
775
|
+
return {
|
|
776
|
+
promptTokens: (usage.inputTokens || 0) + (cache.cacheReadInputTokens || 0) + (cache.cacheCreationInputTokens || 0),
|
|
777
|
+
completionTokens: usage.outputTokens || 0,
|
|
778
|
+
totalTokens: usage.totalTokens || 0,
|
|
779
|
+
...cache
|
|
780
|
+
};
|
|
781
|
+
}
|
|
761
782
|
//#endregion
|
|
762
783
|
export { BedrockProvider };
|
|
763
784
|
|
|
764
|
-
//# sourceMappingURL=bedrock-
|
|
785
|
+
//# sourceMappingURL=bedrock-fO9ix2hd.js.map
|