@happyvertical/ai 0.96.1 → 0.98.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -3
- package/dist/chunks/{anthropic-CD9Ud7a5.js → anthropic-6ZDF32jo.js} +181 -27
- package/dist/chunks/anthropic-6ZDF32jo.js.map +1 -0
- package/dist/chunks/{bedrock-Bv1qCquH.js → bedrock-BQi3Z9jR.js} +71 -21
- package/dist/chunks/bedrock-BQi3Z9jR.js.map +1 -0
- package/dist/chunks/{bifrost-DzT2J0Lq.js → bifrost-DsI2I4ka.js} +9 -6
- package/dist/chunks/bifrost-DsI2I4ka.js.map +1 -0
- package/dist/chunks/{byteplus-modelark-BIEXaTWw.js → byteplus-modelark-Dq8AgCQD.js} +7 -5
- package/dist/chunks/byteplus-modelark-Dq8AgCQD.js.map +1 -0
- package/dist/chunks/{claude-cli-Chfnckyj.js → claude-cli-DIFxvG3c.js} +7 -5
- package/dist/chunks/claude-cli-DIFxvG3c.js.map +1 -0
- package/dist/chunks/continuation-D0NisZA1.js +209 -0
- package/dist/chunks/continuation-D0NisZA1.js.map +1 -0
- package/dist/chunks/{gateway-admin-04mvbDHC.js → gateway-audio-models-Dn7Wrc6P.js} +53 -3
- package/dist/chunks/gateway-audio-models-Dn7Wrc6P.js.map +1 -0
- package/dist/chunks/{gemini-BGwrHeGc.js → gemini-BLFwWN90.js} +131 -13
- package/dist/chunks/gemini-BLFwWN90.js.map +1 -0
- package/dist/chunks/{huggingface-mthYVbLR.js → huggingface-BuRwlABZ.js} +8 -6
- package/dist/chunks/huggingface-BuRwlABZ.js.map +1 -0
- package/dist/chunks/{litellm-xCO8QlGI.js → litellm-CpfCT6jN.js} +9 -6
- package/dist/chunks/litellm-CpfCT6jN.js.map +1 -0
- package/dist/chunks/{ollama-Bedt8Agy.js → ollama-CnrskTUg.js} +38 -7
- package/dist/chunks/ollama-CnrskTUg.js.map +1 -0
- package/dist/chunks/{openai-compat-video-B-xVe9bW.js → openai-compat-video-CJIDdkUO.js} +7 -5
- package/dist/chunks/openai-compat-video-CJIDdkUO.js.map +1 -0
- package/dist/chunks/{qwen-tts-D-UmiMMy.js → qwen-tts-PzEkWcEb.js} +2 -2
- package/dist/chunks/{qwen-tts-D-UmiMMy.js.map → qwen-tts-PzEkWcEb.js.map} +1 -1
- package/dist/chunks/rate-limit-BzWfHA0e.js +713 -0
- package/dist/chunks/rate-limit-BzWfHA0e.js.map +1 -0
- package/dist/chunks/{seevio-BJRiguLc.js → seevio-D2y5Io7C.js} +4 -5
- package/dist/chunks/seevio-D2y5Io7C.js.map +1 -0
- package/dist/chunks/{src-BUQUZK2M.js → src-CLT6r1W0.js} +36 -23
- package/dist/chunks/src-CLT6r1W0.js.map +1 -0
- package/dist/chunks/tool-messages-0ND35mYX.js +57 -0
- package/dist/chunks/tool-messages-0ND35mYX.js.map +1 -0
- package/dist/chunks/{types-BpnNcTU1.js → types-DB5W6_F-.js} +37 -5
- package/dist/chunks/types-DB5W6_F-.js.map +1 -0
- package/dist/chunks/{typesafe-B0gPZQJg.js → typesafe-CVUqQ8N1.js} +7 -5
- package/dist/chunks/typesafe-CVUqQ8N1.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/node.js +5 -5
- package/dist/shared/continuation.d.ts +51 -0
- package/dist/shared/continuation.d.ts.map +1 -0
- package/dist/shared/factory.d.ts.map +1 -1
- package/dist/shared/providers/anthropic.d.ts +53 -0
- package/dist/shared/providers/anthropic.d.ts.map +1 -1
- package/dist/shared/providers/bedrock.d.ts +13 -0
- package/dist/shared/providers/bedrock.d.ts.map +1 -1
- package/dist/shared/providers/bifrost.d.ts.map +1 -1
- package/dist/shared/providers/claude-cli.d.ts.map +1 -1
- package/dist/shared/providers/gateway-audio-models.d.ts +24 -0
- package/dist/shared/providers/gateway-audio-models.d.ts.map +1 -0
- package/dist/shared/providers/gemini.d.ts +28 -0
- package/dist/shared/providers/gemini.d.ts.map +1 -1
- package/dist/shared/providers/huggingface.d.ts.map +1 -1
- package/dist/shared/providers/litellm.d.ts.map +1 -1
- package/dist/shared/providers/ollama.d.ts.map +1 -1
- package/dist/shared/providers/openai.d.ts.map +1 -1
- package/dist/shared/providers/seevio.d.ts.map +1 -1
- package/dist/shared/providers/typesafe.d.ts.map +1 -1
- package/dist/shared/rate-limit.d.ts +19 -1
- package/dist/shared/rate-limit.d.ts.map +1 -1
- package/dist/shared/safety.d.ts.map +1 -1
- package/dist/shared/tool-messages.d.ts +27 -0
- package/dist/shared/tool-messages.d.ts.map +1 -0
- package/dist/shared/types.d.ts +191 -22
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/chunks/anthropic-CD9Ud7a5.js.map +0 -1
- package/dist/chunks/bedrock-Bv1qCquH.js.map +0 -1
- package/dist/chunks/bifrost-DzT2J0Lq.js.map +0 -1
- package/dist/chunks/byteplus-modelark-BIEXaTWw.js.map +0 -1
- package/dist/chunks/claude-cli-Chfnckyj.js.map +0 -1
- package/dist/chunks/gateway-admin-04mvbDHC.js.map +0 -1
- package/dist/chunks/gemini-BGwrHeGc.js.map +0 -1
- package/dist/chunks/huggingface-mthYVbLR.js.map +0 -1
- package/dist/chunks/litellm-xCO8QlGI.js.map +0 -1
- package/dist/chunks/ollama-Bedt8Agy.js.map +0 -1
- package/dist/chunks/openai-compat-video-B-xVe9bW.js.map +0 -1
- package/dist/chunks/rate-limit-BVW_1Tey.js +0 -202
- package/dist/chunks/rate-limit-BVW_1Tey.js.map +0 -1
- package/dist/chunks/safety-BKswBoqy.js +0 -279
- package/dist/chunks/safety-BKswBoqy.js.map +0 -1
- package/dist/chunks/seevio-BJRiguLc.js.map +0 -1
- package/dist/chunks/src-BUQUZK2M.js.map +0 -1
- package/dist/chunks/types-BpnNcTU1.js.map +0 -1
- package/dist/chunks/typesafe-B0gPZQJg.js.map +0 -1
package/README.md
CHANGED
|
@@ -64,6 +64,16 @@ const bifrost = await getAI({
|
|
|
64
64
|
defaultModel: process.env.BIFROST_MODEL,
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
+
// LiteLLM and Bifrost getModels() list gateway audio models with their own
|
|
68
|
+
// capabilities: transcription models (whisper, transcribe, speech-to-text
|
|
69
|
+
// tokens) report ['transcription'] and TTS models (tts, speech tokens) report
|
|
70
|
+
// ['speech']. Markers match whole model-ID tokens, not substrings. They are
|
|
71
|
+
// never auto-selected for chat, vision, embeddings, or image generation.
|
|
72
|
+
// Moderation and rerank models stay filtered out of the listing.
|
|
73
|
+
const transcribers = (await bifrost.getModels()).filter((model) =>
|
|
74
|
+
model.capabilities.includes('transcription'),
|
|
75
|
+
);
|
|
76
|
+
|
|
67
77
|
// Ollama (local by default)
|
|
68
78
|
const ollama = await getAI({
|
|
69
79
|
type: 'ollama',
|
|
@@ -329,7 +339,8 @@ LiteLLM uses the same SDK surface, mapping projects to LiteLLM teams and virtual
|
|
|
329
339
|
Every generative provider constructor applies the same safe defaults, including
|
|
330
340
|
instances created directly instead of through `getAI()`:
|
|
331
341
|
|
|
332
|
-
- 4,096 output tokens
|
|
342
|
+
- 4,096 output tokens when the caller passes no `maxTokens` (`defaultOutputTokens`)
|
|
343
|
+
- a 131,072-token output ceiling (`maxOutputTokens`), so an explicit `maxTokens` is bounded by the model, not this package
|
|
333
344
|
- 1,024 reasoning tokens per request
|
|
334
345
|
- one generated image per request
|
|
335
346
|
- a 120-second provider timeout
|
|
@@ -382,6 +393,48 @@ effective token ceilings plus sanitized `usageTags`; prompts, responses, and
|
|
|
382
393
|
credentials are never included. Legacy `thinkingLevel` options remain available
|
|
383
394
|
as deprecated aliases and are normalized through the reasoning ceiling.
|
|
384
395
|
|
|
396
|
+
## Continuing Truncated Replies
|
|
397
|
+
|
|
398
|
+
When a reply stops because it hit its output limit (`finishReason: 'length'`:
|
|
399
|
+
OpenAI-compatible `length`, Anthropic/Bedrock `max_tokens`, Gemini
|
|
400
|
+
`MAX_TOKENS`, Ollama `length`), `getAI()` clients can ask the model to carry on
|
|
401
|
+
and return one stitched reply. It is **off by default**: each continuation is a
|
|
402
|
+
full extra request (up to `1 + maxContinuations` times the spend), which should
|
|
403
|
+
be a deliberate choice, and the generation guardrails above apply to every part.
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
const result = await ai.chat(messages, { continueOnLength: true });
|
|
407
|
+
// or { continueOnLength: { maxContinuations: 2 } } (default 3)
|
|
408
|
+
// or set it as the client default: getAI({ ..., continueOnLength: true })
|
|
409
|
+
|
|
410
|
+
result.content; // stitched text
|
|
411
|
+
result.parts; // requests used (set when continuation ran)
|
|
412
|
+
result.truncated; // true if the reply still ended on the limit
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
- Each continuation re-sends the conversation with the partial reply as an
|
|
416
|
+
assistant turn plus a "continue exactly where you stopped" user turn, and any
|
|
417
|
+
text the model repeats at the seam (12+ characters) is trimmed. When a part
|
|
418
|
+
ends a sentence and the next starts a new one without a space, the space is
|
|
419
|
+
restored.
|
|
420
|
+
- `chat`, `complete`, `message`, and `stream` all support it (Ollama's
|
|
421
|
+
`complete` switches from `/generate` to a one-message chat when it is on); a stream stays one
|
|
422
|
+
continuous stream (the first characters of each continuation are held briefly
|
|
423
|
+
so a repeated seam can be trimmed). `onProgress` receives only the stitched text.
|
|
424
|
+
- `usage` on the result is the sum across parts. Provider `onUsage` hooks still
|
|
425
|
+
fire once per upstream request, so their sum equals the total.
|
|
426
|
+
- Tool calls and `responseFormat: { type: 'json_object' }` are never continued
|
|
427
|
+
(stitching them is unsafe); the reply is returned as-is with `truncated: true`.
|
|
428
|
+
- With `rateLimit` pacing, every part is its own paced request: a rate-limit
|
|
429
|
+
retry repeats only the part that was rejected, never the parts already paid for.
|
|
430
|
+
- A reply that hits the limit before producing any text (reasoning used the
|
|
431
|
+
whole budget) is not continued; it is returned with `truncated: true`.
|
|
432
|
+
- Aborting through `signal` stops before the next part is requested. A part
|
|
433
|
+
that fails throws the error; the partial text is not returned.
|
|
434
|
+
- `truncated: true` is also set when continuation is off and the reply hit the limit.
|
|
435
|
+
- Streaming adapters report the finish reason through `ChatOptions.onFinishReason`;
|
|
436
|
+
a continued stream reports it once, with the last part's reason.
|
|
437
|
+
|
|
385
438
|
## Opt-In Rate-Limit Pacing
|
|
386
439
|
|
|
387
440
|
Use `rateLimit` when multiple calls share the same provider budget and you want
|
|
@@ -424,12 +477,24 @@ in-process retry wrapper is applied. Provider retries still default to zero.
|
|
|
424
477
|
| `cooldownMs` | `number` | `0` | Minimum delay after a successful call before the next call with the same key |
|
|
425
478
|
| `initialDelayMs` | `number` | `5000` | Fallback retry delay when the provider does not return `Retry-After` |
|
|
426
479
|
| `maxAttempts` | `number` | `1` | Total attempts, including the initial call; increase explicitly only for an approved workload |
|
|
480
|
+
| `maxRetryDelayMs` | `number` | `60000` | Longest reset wait still retried; a limit that resets further away is not retried and its error has `retryable: false` |
|
|
427
481
|
| `requestsPerMinute` | `number` | provider-specific | Used by `qwen3-tts` local token-bucket limiting |
|
|
428
482
|
| `maxConcurrent` | `number` | provider-specific | Used by `qwen3-tts` local concurrency limiting |
|
|
429
483
|
|
|
430
484
|
- If `key` is omitted, `@happyvertical/ai` derives a provider-scoped key from the configured credentials
|
|
431
485
|
- Setting any of `key`, `cooldownMs`, `initialDelayMs`, or `maxAttempts` also opts in when `enabled` is omitted
|
|
432
486
|
- Only normalized rate-limit failures are retried
|
|
487
|
+
- The request `timeout` covers the whole call from the caller's side: time
|
|
488
|
+
queued behind other calls on the same key, cooldown and retry waits, and the
|
|
489
|
+
request itself, which gets only what is left. A call still queued at its
|
|
490
|
+
deadline fails with `AIError` code `AI_TIMEOUT` and never reaches the provider.
|
|
491
|
+
When a rate limit has closed the key past the deadline, the call fails at once
|
|
492
|
+
with a `RateLimitError` carrying the original `reason` and the remaining
|
|
493
|
+
`retryAfterMs`. A retry that cannot finish before the deadline is not started.
|
|
494
|
+
Aborting `signal` releases a queued call (`AI_ABORTED`). Methods without
|
|
495
|
+
request controls (`embed`, `getModels`, speech) use the client `timeout` for
|
|
496
|
+
their queue wait when one is set. With `continueOnLength`, each part is its
|
|
497
|
+
own request with its own timeout.
|
|
433
498
|
- `stream()` is left unchanged; pacing is applied to the promise-returning request methods
|
|
434
499
|
|
|
435
500
|
Example quota-sensitive batch workload:
|
|
@@ -524,13 +589,28 @@ All extend `AIError`: `AuthenticationError`, `RateLimitError`, `ModelNotFoundErr
|
|
|
524
589
|
|
|
525
590
|
- `AIError.retryable` distinguishes retryable failures from terminal ones
|
|
526
591
|
- `RateLimitError.retryAfter` exposes provider retry hints in seconds when available
|
|
592
|
+
- `RateLimitError.reason` keeps the provider's own text (for example
|
|
593
|
+
`token limit exceeded (277867/250000, resets every 1h)`), and the message
|
|
594
|
+
includes it, so callers can say why a call was refused
|
|
595
|
+
- `RateLimitError.retryAfterMs` is the time until the limit resets when the
|
|
596
|
+
provider said so: `Retry-After`, a reset header (`x-ratelimit-reset-*`,
|
|
597
|
+
`anthropic-ratelimit-*-reset`), or text such as "try again in 20s"
|
|
598
|
+
- `RateLimitError.limitWindowMs` is set when the provider names a limit window
|
|
599
|
+
but no reset time ("resets every 1h"); the limit lifts within that time
|
|
600
|
+
- `error.cause` is the provider error it was mapped from
|
|
601
|
+
- `RateLimitError.retryable` is `false` when the limit resets more than a
|
|
602
|
+
minute away (`retryAfterMs`, else `limitWindowMs`; `rateLimit.maxRetryDelayMs`
|
|
603
|
+
changes the threshold). Job runners should not re-run such a job soon; they
|
|
604
|
+
can reschedule it after `retryAfterMs ?? limitWindowMs`
|
|
527
605
|
|
|
528
606
|
```typescript
|
|
529
607
|
try {
|
|
530
608
|
await ai.chat(messages);
|
|
531
609
|
} catch (error) {
|
|
532
|
-
if (error instanceof RateLimitError
|
|
533
|
-
console.log('
|
|
610
|
+
if (error instanceof RateLimitError) {
|
|
611
|
+
console.log('why:', error.reason);
|
|
612
|
+
if (error.retryable) console.log('retry after seconds:', error.retryAfter);
|
|
613
|
+
else console.log('lifts in ms:', error.retryAfterMs ?? error.limitWindowMs);
|
|
534
614
|
}
|
|
535
615
|
}
|
|
536
616
|
```
|
|
@@ -564,6 +644,46 @@ if (response.toolCalls) {
|
|
|
564
644
|
}
|
|
565
645
|
```
|
|
566
646
|
|
|
647
|
+
To continue the loop, replay the assistant turn with `tool_calls` unchanged
|
|
648
|
+
and answer each call with a `role: 'tool'` message carrying its
|
|
649
|
+
`tool_call_id`. Every chat provider maps this to its native pairing
|
|
650
|
+
(`tool_call_id` for OpenAI-compatible APIs including Bifrost and LiteLLM,
|
|
651
|
+
`tool_use_id` for Anthropic, `toolUseId` for Bedrock, `functionResponse` for
|
|
652
|
+
Gemini, `tool_name` for Ollama). `name` on the tool message is optional; it is
|
|
653
|
+
resolved from the matching call when a provider needs it.
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
const call = response.toolCalls[0];
|
|
657
|
+
const followUp = await ai.chat([
|
|
658
|
+
{ role: 'user', content: 'What is the weather in Tokyo?' },
|
|
659
|
+
{ role: 'assistant', content: response.content, tool_calls: response.toolCalls },
|
|
660
|
+
{ role: 'tool', tool_call_id: call.id, content: JSON.stringify({ tempC: 21 }) },
|
|
661
|
+
], { tools });
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Replay `response.toolCalls` as returned, and keep every field if you persist
|
|
665
|
+
the conversation: provider-specific replay data rides on the tool calls.
|
|
666
|
+
Gemini 3 needs `thoughtSignature`, and Anthropic with extended thinking
|
|
667
|
+
(`reasoning.maxTokens`) needs `thinkingBlocks` (the signed thinking that opened
|
|
668
|
+
the tool-calling turn). If an Anthropic tool loop's latest `tool_use` turn has
|
|
669
|
+
no `thinkingBlocks`, that request is sent without extended thinking instead of
|
|
670
|
+
failing.
|
|
671
|
+
Gemini replays only calls that have a result, and when a Gemini 3 step has no
|
|
672
|
+
`thoughtSignature` it sends Google's documented placeholder signature, which
|
|
673
|
+
avoids the 400 but lowers reasoning quality, so persist the real one.
|
|
674
|
+
|
|
675
|
+
A loop's final round usually asks for a plain answer (`toolChoice: 'none'`,
|
|
676
|
+
often with no `tools`) while replaying the tool history. Anthropic and Bedrock
|
|
677
|
+
reject tool history without declared tools, so both declare a minimal
|
|
678
|
+
definition for each tool the history references. Anthropic sends
|
|
679
|
+
`tool_choice: { type: 'none' }`; Bedrock Converse has no "none" choice, so it
|
|
680
|
+
keeps the tools, omits `toolChoice`, and adds a system instruction not to call
|
|
681
|
+
tools (an instruction, not a guarantee; check `finishReason`).
|
|
682
|
+
|
|
683
|
+
`stream()` yields text only and never returns tool calls, so run tool-calling
|
|
684
|
+
rounds with `chat()`. Anthropic streams ignore `tools`/`toolChoice` and declare
|
|
685
|
+
tools only when the replayed history needs them, with `tool_choice` none.
|
|
686
|
+
|
|
567
687
|
## Usage Tracking
|
|
568
688
|
|
|
569
689
|
Track token usage, costs, and performance across all providers with the `onUsage` callback:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as ContextLengthError,
|
|
2
|
-
import { n as
|
|
3
|
-
import { a as normalizeBaseAIOptions, c as prepareRequestControls, o as normalizeChatOptions } from "./safety-BKswBoqy.js";
|
|
1
|
+
import { a as ContextLengthError, l as extractTextContent, r as AuthenticationError, s as ModelNotFoundError, t as AIError } from "./types-DB5W6_F-.js";
|
|
2
|
+
import { c as normalizeChatOptions, n as rateLimitErrorFrom, s as normalizeBaseAIOptions, u as prepareRequestControls } from "./rate-limit-BzWfHA0e.js";
|
|
4
3
|
import { t as emitUsage } from "./usage-C1Y1Nlg4.js";
|
|
4
|
+
import { r as parseToolArguments } from "./tool-messages-0ND35mYX.js";
|
|
5
5
|
//#region src/shared/providers/anthropic.ts
|
|
6
6
|
/**
|
|
7
7
|
* Anthropic Claude provider implementation
|
|
@@ -103,18 +103,11 @@ var AnthropicProvider = class {
|
|
|
103
103
|
top_p: options.topP,
|
|
104
104
|
stop_sequences: Array.isArray(options.stop) ? options.stop : options.stop ? [options.stop] : void 0,
|
|
105
105
|
system: system || void 0,
|
|
106
|
-
|
|
107
|
-
name: tool.function.name,
|
|
108
|
-
description: tool.function.description || "",
|
|
109
|
-
input_schema: tool.function.parameters || { type: "object" }
|
|
110
|
-
})) : void 0,
|
|
111
|
-
tool_choice: this.mapToolChoice(options.toolChoice),
|
|
106
|
+
...this.mapToolParams(options, anthropicMessages),
|
|
112
107
|
stream: false
|
|
113
108
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
budget_tokens: options.reasoning?.maxTokens
|
|
117
|
-
};
|
|
109
|
+
const thinking = this.mapThinking(options, anthropicMessages);
|
|
110
|
+
if (thinking) requestParams.thinking = thinking;
|
|
118
111
|
if (options.responseFormat?.type === "json_object") {
|
|
119
112
|
const jsonInstruction = "\n\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.";
|
|
120
113
|
requestParams.system = requestParams.system ? requestParams.system + jsonInstruction : jsonInstruction.trim();
|
|
@@ -132,6 +125,8 @@ var AnthropicProvider = class {
|
|
|
132
125
|
arguments: JSON.stringify(block.input)
|
|
133
126
|
}
|
|
134
127
|
}));
|
|
128
|
+
const thinkingBlocks = extractThinkingBlocks(response.content);
|
|
129
|
+
if (toolCalls.length > 0 && thinkingBlocks.length > 0) toolCalls[0].thinkingBlocks = thinkingBlocks;
|
|
135
130
|
const usage = {
|
|
136
131
|
promptTokens: response.usage.input_tokens,
|
|
137
132
|
completionTokens: response.usage.output_tokens,
|
|
@@ -169,7 +164,8 @@ var AnthropicProvider = class {
|
|
|
169
164
|
signal: options.signal,
|
|
170
165
|
timeout: options.timeout,
|
|
171
166
|
reasoning: options.reasoning,
|
|
172
|
-
usageTags: options.usageTags
|
|
167
|
+
usageTags: options.usageTags,
|
|
168
|
+
continueOnLength: options.continueOnLength
|
|
173
169
|
});
|
|
174
170
|
}
|
|
175
171
|
/**
|
|
@@ -215,7 +211,8 @@ var AnthropicProvider = class {
|
|
|
215
211
|
signal: options.signal,
|
|
216
212
|
timeout: options.timeout,
|
|
217
213
|
reasoning: options.reasoning,
|
|
218
|
-
usageTags: options.usageTags
|
|
214
|
+
usageTags: options.usageTags,
|
|
215
|
+
continueOnLength: options.continueOnLength
|
|
219
216
|
})).content;
|
|
220
217
|
}
|
|
221
218
|
async embed(_text, _options = {}) {
|
|
@@ -230,6 +227,15 @@ var AnthropicProvider = class {
|
|
|
230
227
|
async generateImage(_prompt, _options = {}) {
|
|
231
228
|
throw new AIError("Anthropic Claude does not support image generation. Use OpenAI or Gemini.", "NOT_SUPPORTED", "anthropic");
|
|
232
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Streams a chat completion as text chunks.
|
|
232
|
+
*
|
|
233
|
+
* Streams yield text only and never surface tool calls, so `options.tools`
|
|
234
|
+
* and `options.toolChoice` are ignored: tools are declared only when the
|
|
235
|
+
* replayed history contains `tool_use` blocks (Anthropic requires it), with
|
|
236
|
+
* `tool_choice: { type: 'none' }`. Use `chat()` for rounds that may call
|
|
237
|
+
* tools.
|
|
238
|
+
*/
|
|
233
239
|
async *stream(messages, options = {}) {
|
|
234
240
|
const startTime = Date.now();
|
|
235
241
|
let controls;
|
|
@@ -247,20 +253,24 @@ var AnthropicProvider = class {
|
|
|
247
253
|
top_p: options.topP,
|
|
248
254
|
stop_sequences: Array.isArray(options.stop) ? options.stop : options.stop ? [options.stop] : void 0,
|
|
249
255
|
system: system || void 0,
|
|
256
|
+
...this.mapHistoryToolParams(anthropicMessages),
|
|
250
257
|
stream: true
|
|
251
258
|
};
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
budget_tokens: options.reasoning?.maxTokens
|
|
255
|
-
};
|
|
259
|
+
const thinking = this.mapThinking(options, anthropicMessages);
|
|
260
|
+
if (thinking) requestParams.thinking = thinking;
|
|
256
261
|
const stream = await this.client.messages.create(requestParams, {
|
|
257
262
|
signal: controls.signal,
|
|
258
263
|
timeout: controls.timeout
|
|
259
264
|
});
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
265
|
+
let streamStopReason = null;
|
|
266
|
+
for await (const chunk of stream) {
|
|
267
|
+
if (chunk.type === "message_delta" && chunk.delta?.stop_reason) streamStopReason = chunk.delta.stop_reason;
|
|
268
|
+
if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
|
|
269
|
+
if (options.onProgress) options.onProgress(chunk.delta.text);
|
|
270
|
+
yield chunk.delta.text;
|
|
271
|
+
}
|
|
263
272
|
}
|
|
273
|
+
options.onFinishReason?.(this.mapFinishReason(streamStopReason) ?? "stop");
|
|
264
274
|
emitUsage(this.options, "anthropic", "stream", model, void 0, startTime, options.usageTags);
|
|
265
275
|
} catch (error) {
|
|
266
276
|
if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", "anthropic", options.model);
|
|
@@ -400,13 +410,68 @@ var AnthropicProvider = class {
|
|
|
400
410
|
async getVoices(_options) {
|
|
401
411
|
throw new AIError("Voice listing is not supported by Anthropic provider. Use Qwen3-TTS provider.", "NOT_IMPLEMENTED", "anthropic");
|
|
402
412
|
}
|
|
413
|
+
/**
|
|
414
|
+
* Maps internal messages to Anthropic Messages API turns.
|
|
415
|
+
*
|
|
416
|
+
* Assistant `tool_calls` become `tool_use` blocks and `role: 'tool'`
|
|
417
|
+
* results become `tool_result` blocks paired by `tool_call_id`
|
|
418
|
+
* (`tool_use_id`). Consecutive tool results are grouped into one user turn,
|
|
419
|
+
* as Anthropic requires every result for a turn to follow it directly.
|
|
420
|
+
* Only answered calls are replayed as `tool_use`, and only results whose
|
|
421
|
+
* `tool_call_id` matches a replayed call become `tool_result`. A result
|
|
422
|
+
* without `tool_call_id`, or one answering no replayed call, falls back to
|
|
423
|
+
* plain text (and a call left unanswered keeps only its text).
|
|
424
|
+
*/
|
|
403
425
|
mapMessagesToAnthropic(messages) {
|
|
404
426
|
let system;
|
|
405
427
|
const anthropicMessages = [];
|
|
428
|
+
const answeredToolCallIds = new Set(messages.filter((message) => message.role === "tool" && message.tool_call_id).map((message) => message.tool_call_id));
|
|
429
|
+
const replayedToolUseIds = /* @__PURE__ */ new Set();
|
|
430
|
+
for (const message of messages) {
|
|
431
|
+
if (message.role !== "assistant") continue;
|
|
432
|
+
for (const toolCall of message.tool_calls || []) if (answeredToolCallIds.has(toolCall.id)) replayedToolUseIds.add(toolCall.id);
|
|
433
|
+
}
|
|
406
434
|
for (const message of messages) {
|
|
407
435
|
const textContent = extractTextContent(message.content);
|
|
408
|
-
if (message.role === "system")
|
|
409
|
-
|
|
436
|
+
if (message.role === "system") {
|
|
437
|
+
system = system ? `${system}\n\n${textContent}` : textContent;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (message.role === "tool" && message.tool_call_id && replayedToolUseIds.has(message.tool_call_id)) {
|
|
441
|
+
const block = {
|
|
442
|
+
type: "tool_result",
|
|
443
|
+
tool_use_id: message.tool_call_id,
|
|
444
|
+
content: textContent
|
|
445
|
+
};
|
|
446
|
+
const previous = anthropicMessages[anthropicMessages.length - 1];
|
|
447
|
+
if (previous?.role === "user" && Array.isArray(previous.content) && previous.content.every((part) => part.type === "tool_result")) previous.content.push(block);
|
|
448
|
+
else anthropicMessages.push({
|
|
449
|
+
role: "user",
|
|
450
|
+
content: [block]
|
|
451
|
+
});
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
const toolCalls = message.role === "assistant" ? (message.tool_calls || []).filter((toolCall) => answeredToolCallIds.has(toolCall.id)) : [];
|
|
455
|
+
if (toolCalls.length > 0) {
|
|
456
|
+
const content = [];
|
|
457
|
+
for (const toolCall of toolCalls) for (const block of toolCall.thinkingBlocks || []) content.push({ ...block });
|
|
458
|
+
if (textContent) content.push({
|
|
459
|
+
type: "text",
|
|
460
|
+
text: textContent
|
|
461
|
+
});
|
|
462
|
+
for (const toolCall of toolCalls) content.push({
|
|
463
|
+
type: "tool_use",
|
|
464
|
+
id: toolCall.id,
|
|
465
|
+
name: toolCall.function.name,
|
|
466
|
+
input: parseToolArguments(toolCall.function.arguments)
|
|
467
|
+
});
|
|
468
|
+
anthropicMessages.push({
|
|
469
|
+
role: "assistant",
|
|
470
|
+
content
|
|
471
|
+
});
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
anthropicMessages.push({
|
|
410
475
|
role: message.role === "assistant" ? "assistant" : "user",
|
|
411
476
|
content: textContent
|
|
412
477
|
});
|
|
@@ -416,9 +481,81 @@ var AnthropicProvider = class {
|
|
|
416
481
|
anthropicMessages
|
|
417
482
|
};
|
|
418
483
|
}
|
|
484
|
+
/**
|
|
485
|
+
* Builds the `tools` / `tool_choice` request fields.
|
|
486
|
+
*
|
|
487
|
+
* Anthropic rejects a request whose history contains `tool_use` or
|
|
488
|
+
* `tool_result` blocks unless `tools` is declared. A tool loop's final,
|
|
489
|
+
* tool-less round (no `tools`, typically `toolChoice: 'none'`) still
|
|
490
|
+
* replays that history, so when the caller declares no tools but the
|
|
491
|
+
* history uses some, this declares a minimal definition for each tool the
|
|
492
|
+
* history references and sets `tool_choice: { type: 'none' }` so the model
|
|
493
|
+
* answers in text instead of calling them.
|
|
494
|
+
*/
|
|
495
|
+
mapToolParams(options, anthropicMessages) {
|
|
496
|
+
const tools = this.mapTools(options);
|
|
497
|
+
if (tools) return {
|
|
498
|
+
tools,
|
|
499
|
+
tool_choice: this.mapToolChoice(options.toolChoice)
|
|
500
|
+
};
|
|
501
|
+
return this.mapHistoryToolParams(anthropicMessages);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Declares a minimal definition for each tool the replayed history
|
|
505
|
+
* references, with `tool_choice: { type: 'none' }`, or nothing when the
|
|
506
|
+
* history has no `tool_use` blocks. Used for tool-less rounds and for
|
|
507
|
+
* every stream (streams never yield tool calls).
|
|
508
|
+
*/
|
|
509
|
+
mapHistoryToolParams(anthropicMessages) {
|
|
510
|
+
const historyToolNames = /* @__PURE__ */ new Set();
|
|
511
|
+
for (const message of anthropicMessages) {
|
|
512
|
+
if (!Array.isArray(message.content)) continue;
|
|
513
|
+
for (const block of message.content) if (block.type === "tool_use") historyToolNames.add(block.name);
|
|
514
|
+
}
|
|
515
|
+
if (historyToolNames.size === 0) return {};
|
|
516
|
+
return {
|
|
517
|
+
tools: [...historyToolNames].map((name) => ({
|
|
518
|
+
name,
|
|
519
|
+
description: "Used earlier in this conversation; not available for this turn.",
|
|
520
|
+
input_schema: { type: "object" }
|
|
521
|
+
})),
|
|
522
|
+
tool_choice: { type: "none" }
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Builds the `thinking` request field for `options.reasoning.maxTokens`.
|
|
527
|
+
*
|
|
528
|
+
* While thinking is enabled, Anthropic requires the assistant turn of an
|
|
529
|
+
* in-progress tool loop (the last assistant turn, when it holds `tool_use`)
|
|
530
|
+
* to start with its thinking blocks. They are replayed from
|
|
531
|
+
* `AIToolCall.thinkingBlocks`; when that turn has none (the history came
|
|
532
|
+
* from another provider, a store that dropped the field, or a call made
|
|
533
|
+
* without thinking), thinking is left off for this request rather than
|
|
534
|
+
* sending a request Anthropic would reject.
|
|
535
|
+
*/
|
|
536
|
+
mapThinking(options, anthropicMessages) {
|
|
537
|
+
const budget = options.reasoning?.maxTokens || 0;
|
|
538
|
+
if (budget <= 0) return void 0;
|
|
539
|
+
const lastAssistant = [...anthropicMessages].reverse().find((message) => message.role === "assistant");
|
|
540
|
+
if (lastAssistant && Array.isArray(lastAssistant.content) && lastAssistant.content.some((block) => block.type === "tool_use")) {
|
|
541
|
+
const firstType = lastAssistant.content[0]?.type;
|
|
542
|
+
if (firstType !== "thinking" && firstType !== "redacted_thinking") return;
|
|
543
|
+
}
|
|
544
|
+
return {
|
|
545
|
+
type: "enabled",
|
|
546
|
+
budget_tokens: budget
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
mapTools(options) {
|
|
550
|
+
return options.tools && options.tools.length > 0 ? options.tools.map((tool) => ({
|
|
551
|
+
name: tool.function.name,
|
|
552
|
+
description: tool.function.description || "",
|
|
553
|
+
input_schema: tool.function.parameters || { type: "object" }
|
|
554
|
+
})) : void 0;
|
|
555
|
+
}
|
|
419
556
|
mapToolChoice(toolChoice) {
|
|
420
557
|
if (!toolChoice || toolChoice === "auto") return { type: "auto" };
|
|
421
|
-
if (toolChoice === "none") return;
|
|
558
|
+
if (toolChoice === "none") return { type: "none" };
|
|
422
559
|
if (typeof toolChoice === "object" && toolChoice.type === "function") return {
|
|
423
560
|
type: "tool",
|
|
424
561
|
name: toolChoice.function.name
|
|
@@ -440,7 +577,7 @@ var AnthropicProvider = class {
|
|
|
440
577
|
const apiError = error;
|
|
441
578
|
switch (apiError.status) {
|
|
442
579
|
case 401: return new AuthenticationError("anthropic");
|
|
443
|
-
case 429: return
|
|
580
|
+
case 429: return rateLimitErrorFrom("anthropic", error);
|
|
444
581
|
case 404: return new ModelNotFoundError(apiError.message || "Model not found", "anthropic");
|
|
445
582
|
case 413: return new ContextLengthError("anthropic");
|
|
446
583
|
}
|
|
@@ -448,7 +585,24 @@ var AnthropicProvider = class {
|
|
|
448
585
|
return new AIError(error instanceof Error ? error.message : "Unknown Anthropic error occurred", "UNKNOWN_ERROR", "anthropic");
|
|
449
586
|
}
|
|
450
587
|
};
|
|
588
|
+
/**
|
|
589
|
+
* Thinking and redacted-thinking blocks from an Anthropic response, copied
|
|
590
|
+
* with only the fields the API accepts back.
|
|
591
|
+
*/
|
|
592
|
+
function extractThinkingBlocks(content) {
|
|
593
|
+
const blocks = [];
|
|
594
|
+
for (const block of content || []) if (block?.type === "thinking") blocks.push({
|
|
595
|
+
type: "thinking",
|
|
596
|
+
thinking: block.thinking,
|
|
597
|
+
signature: block.signature
|
|
598
|
+
});
|
|
599
|
+
else if (block?.type === "redacted_thinking") blocks.push({
|
|
600
|
+
type: "redacted_thinking",
|
|
601
|
+
data: block.data
|
|
602
|
+
});
|
|
603
|
+
return blocks;
|
|
604
|
+
}
|
|
451
605
|
//#endregion
|
|
452
606
|
export { AnthropicProvider };
|
|
453
607
|
|
|
454
|
-
//# sourceMappingURL=anthropic-
|
|
608
|
+
//# sourceMappingURL=anthropic-6ZDF32jo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-6ZDF32jo.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 { 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 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 { emitUsage } from './usage';\n\ntype AnthropicContentBlock =\n | AIThinkingBlock\n | { type: 'text'; text: string }\n | {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n }\n | { type: 'tool_result'; tool_use_id: string; content: string };\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);\n if (thinking) {\n requestParams.thinking = thinking;\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 requestParams.system = requestParams.system\n ? requestParams.system + jsonInstruction\n : jsonInstruction.trim();\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: TokenUsage = {\n promptTokens: response.usage.input_tokens,\n completionTokens: response.usage.output_tokens,\n totalTokens: response.usage.input_tokens + response.usage.output_tokens,\n };\n emitUsage(\n this.options,\n 'anthropic',\n 'chat',\n response.model || model,\n usage,\n startTime,\n options.usageTags,\n );\n\n return {\n content: textContent,\n model: response.model,\n finishReason: this.mapFinishReason(response.stop_reason),\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);\n if (thinking) {\n requestParams.thinking = thinking;\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 for await (const chunk of stream) {\n if (chunk.type === 'message_delta' && chunk.delta?.stop_reason) {\n streamStopReason = chunk.delta.stop_reason;\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 options.onFinishReason?.(\n this.mapFinishReason(streamStopReason) ?? 'stop',\n );\n\n emitUsage(\n this.options,\n 'anthropic',\n 'stream',\n model!,\n 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;\n anthropicMessages: AnthropicMessage[];\n } {\n // Anthropic handles system messages separately\n let system: string | undefined;\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 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 };\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 anthropicMessages.push({ role: 'assistant', content });\n continue;\n }\n\n anthropicMessages.push({\n role: message.role === 'assistant' ? 'assistant' : 'user',\n content: textContent,\n });\n }\n\n return { system, anthropicMessages };\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 ): { type: 'enabled'; budget_tokens: number } | undefined {\n const budget = options.reasoning?.maxTokens || 0;\n if (budget <= 0) 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 return { type: 'enabled', budget_tokens: budget };\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 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/**\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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"}
|