jangada-ai 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Neri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,43 @@
1
- # Temporary Holding Version
1
+ # jangada (JS/TS)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Camada fina e adaptável sobre os SDKs de LLM, em TypeScript. Troque provider,
4
+ modelo e chave sem mudar o resto do código, com retry e fallback por tipo de
5
+ erro, structured output com Zod, tools, streaming e custo em cada resposta.
6
+
7
+ Port da lib Python [`jangada-ai`](https://pypi.org/project/jangada-ai/).
8
+ Documentação: [docs.jangada.dev.br](https://docs.jangada.dev.br).
9
+
10
+ > Versão 0.1.0: núcleo e 8 providers. MCP, agentes, RAG e o resto chegam nas próximas versões (veja o [roadmap de paridade](https://docs.jangada.dev.br/javascript)).
11
+
12
+ ```bash
13
+ npm install jangada-ai
14
+ npm install openai # e/ou @anthropic-ai/sdk, @google/genai, zod
15
+ ```
16
+
17
+ Roda em Node 22+, Bun, Deno e runtimes edge. O núcleo não usa `fs`; leitura de
18
+ arquivo e `.env` ficam em `jangada-ai/node`.
19
+
20
+ ```ts
21
+ import { LLM } from "jangada-ai";
22
+ import "jangada-ai/node"; // carrega o .env
23
+
24
+ const llm = new LLM("openai", "gpt-4o-mini", {
25
+ fallbacks: [new LLM("anthropic", "claude-haiku-4-5")],
26
+ });
27
+ const comp = await llm.complete("Olá, {{ nome }}!", { variables: { nome: "Ana" } });
28
+ console.log(comp.text, comp.cost);
29
+ ```
30
+
31
+ Providers: `openai`, `azure`, `groq`, `openrouter`, `deepseek`, `anthropic`,
32
+ `gemini`, `vertex`. Exemplos em [`examples/`](examples/).
33
+
34
+ ## Desenvolvimento
35
+
36
+ ```bash
37
+ npm run check # lint + tipos + testes + build
38
+ npm run test:live # APIs reais (precisa das chaves no ambiente)
39
+ ```
40
+
41
+ ## Licença
42
+
43
+ MIT
@@ -0,0 +1,517 @@
1
+ import { F as ProviderError, H as classify, N as OutputValidationError, R as ServerError, V as UnsupportedError, d as NativeTool, j as LLMError, n as applyProfile, s as splitTools, t as Provider } from "./base-C-SPiVRW.js";
2
+ import { c as imageBase64, i as Completion, s as ToolCall } from "./env-c3s1IX0t.js";
3
+ // src/providers/anthropic.ts
4
+ /**
5
+ * Provider Anthropic (Claude): texto, vision, tools, tools nativas (server tools),
6
+ * streaming e structured output por tool-forcing. SDK: `@anthropic-ai/sdk`
7
+ * (peer dependency opcional).
8
+ *
9
+ * Structured output força uma ferramenta `extract` cujo `input_schema` é o JSON
10
+ * Schema. Nos modelos que REJEITAM forced tool use (Opus 5.5, Sonnet 5.5, Fable 5.1,
11
+ * Mythos 5.1) cai para `tool_choice: auto` + strict tool use, a migração oficial.
12
+ *
13
+ * Fica para a Fase 3: MCP remoto (Messages API beta).
14
+ *
15
+ * Espelha `jangada_ai/providers/anthropic.py`.
16
+ */
17
+ const THINKING_KEY = "anthropic_thinking_blocks";
18
+ const CONTENT_KEY = "anthropic_content";
19
+ const CONTAINER_KEY = "anthropic_container";
20
+ const MAX_PAUSE = 5;
21
+ const NATIVE_VERSIONS = {
22
+ web_search: ["web_search_20250305", "web_search"],
23
+ web_fetch: ["web_fetch_20250910", "web_fetch"],
24
+ code_execution: ["code_execution_20250825", "code_execution"]
25
+ };
26
+ const NATIVE_OPTS = {
27
+ maxUses: "max_uses",
28
+ allowedDomains: "allowed_domains",
29
+ blockedDomains: "blocked_domains",
30
+ userLocation: "user_location"
31
+ };
32
+ const PLAIN_BLOCKS = /* @__PURE__ */ new Set([
33
+ "text",
34
+ "tool_use",
35
+ "thinking",
36
+ "redacted_thinking"
37
+ ]);
38
+ const NO_FORCED_TOOLS = /(?:opus-5[.-]5|sonnet-5[.-]5|fable-5[.-]1|mythos-5[.-]1)/;
39
+ function get(obj, key) {
40
+ return obj !== null && typeof obj === "object" ? obj[key] : void 0;
41
+ }
42
+ function n(v) {
43
+ return typeof v === "number" && Number.isInteger(v) ? v : 0;
44
+ }
45
+ /** strict tool use exige `additionalProperties: false` em todo objeto (não mexe em `required`). */
46
+ function strictSchema(node) {
47
+ if (Array.isArray(node)) return node.map(strictSchema);
48
+ if (node && typeof node === "object") {
49
+ const out = {};
50
+ for (const [k, v] of Object.entries(node)) out[k] = strictSchema(v);
51
+ if (out.type === "object" && !("additionalProperties" in out)) out.additionalProperties = false;
52
+ return out;
53
+ }
54
+ return node;
55
+ }
56
+ /** Bloco da resposta como objeto simples, reenviável exatamente como veio. */
57
+ function dumpBlock(b) {
58
+ return JSON.parse(JSON.stringify(b));
59
+ }
60
+ function systemText(content) {
61
+ if (typeof content === "string") return content;
62
+ return content.map((p) => p.type === "text" ? p.text : "").filter(Boolean).join("\n");
63
+ }
64
+ var AnthropicProvider = class AnthropicProvider extends Provider {
65
+ name = "anthropic";
66
+ envKey = "ANTHROPIC_API_KEY";
67
+ nativeToolKinds = /* @__PURE__ */ new Set([
68
+ "web_search",
69
+ "web_fetch",
70
+ "code_execution"
71
+ ]);
72
+ nativeToolPassthrough = true;
73
+ nativeToolAliases = ["claude"];
74
+ async buildClient() {
75
+ let mod;
76
+ try {
77
+ mod = await import("@anthropic-ai/sdk");
78
+ } catch (e) {
79
+ throw new Error("Instale o SDK da Anthropic: npm install @anthropic-ai/sdk", { cause: e });
80
+ }
81
+ return new mod.default({
82
+ apiKey: this.resolveApiKey(),
83
+ ...this.clientOptions
84
+ });
85
+ }
86
+ content(content) {
87
+ if (typeof content === "string") return content;
88
+ const blocks = [];
89
+ let thinking = [];
90
+ for (const p of content) if (p.type === "text") {
91
+ if (p.text) blocks.push({
92
+ type: "text",
93
+ text: p.text
94
+ });
95
+ } else if (p.type === "image") blocks.push({
96
+ type: "image",
97
+ source: {
98
+ type: "base64",
99
+ media_type: p.mimeType,
100
+ data: imageBase64(p)
101
+ }
102
+ });
103
+ else if (p.type === "tool_call") {
104
+ if (!thinking.length) thinking = [...p.metadata?.[THINKING_KEY] ?? []];
105
+ blocks.push({
106
+ type: "tool_use",
107
+ id: p.id,
108
+ name: p.name,
109
+ input: p.args
110
+ });
111
+ } else if (p.type === "tool_result") blocks.push({
112
+ type: "tool_result",
113
+ tool_use_id: p.toolCallId,
114
+ content: p.content,
115
+ ...p.isError ? { is_error: true } : {}
116
+ });
117
+ return [...thinking, ...blocks];
118
+ }
119
+ split(messages) {
120
+ const system = messages.filter((m) => m.role === "system").map((m) => systemText(m.content)).filter(Boolean).join("\n\n") || void 0;
121
+ const convo = [];
122
+ for (const m of messages) {
123
+ if (m.role === "system") continue;
124
+ const brutos = m.metadata?.[CONTENT_KEY];
125
+ if (m.role === "assistant" && brutos?.length) {
126
+ convo.push({
127
+ role: "assistant",
128
+ content: [...brutos]
129
+ });
130
+ continue;
131
+ }
132
+ convo.push({
133
+ role: m.role === "tool" ? "user" : m.role,
134
+ content: this.content(m.content)
135
+ });
136
+ }
137
+ return {
138
+ system,
139
+ convo
140
+ };
141
+ }
142
+ /** Canônico → nativo: stop → stop_sequences; seed não existe na Anthropic. */
143
+ translate(params) {
144
+ const map = {
145
+ maxTokens: "max_tokens",
146
+ topP: "top_p",
147
+ topK: "top_k",
148
+ stop: "stop_sequences"
149
+ };
150
+ const out = {};
151
+ for (const [k, v] of Object.entries(params)) {
152
+ if (v === void 0 || v === null || k === "seed") continue;
153
+ const key = map[k] ?? k;
154
+ out[key] = key === "stop_sequences" && typeof v === "string" ? [v] : v;
155
+ }
156
+ return out;
157
+ }
158
+ formatTools(tools) {
159
+ const { functions, natives } = splitTools(tools);
160
+ const out = functions.map((t) => ({
161
+ name: t.name,
162
+ description: t.description,
163
+ input_schema: t.parameters
164
+ }));
165
+ for (const nt of natives) out.push(this.nativeTool(nt));
166
+ return out;
167
+ }
168
+ /** NativeTool canônica → server tool da Messages API. */
169
+ nativeTool(nt) {
170
+ if (nt.kind === "native") return nt.spec;
171
+ const raw = nt.optsFor("anthropic");
172
+ const o = {};
173
+ for (const [k, v] of Object.entries(raw)) o[NATIVE_OPTS[k] ?? k] = v;
174
+ const [tipoPadrao, nome] = NATIVE_VERSIONS[nt.kind];
175
+ const tipo = o.version ?? tipoPadrao;
176
+ delete o.version;
177
+ delete o.container;
178
+ if (o.files !== void 0) throw new UnsupportedError("codeExecution({ files }) não existe na Anthropic: envie os arquivos como blocos container_upload (Files API) na mensagem.", { provider: this.name });
179
+ const loc = o.user_location;
180
+ if (loc && typeof loc === "object" && !("type" in loc)) o.user_location = {
181
+ type: "approximate",
182
+ ...loc
183
+ };
184
+ if (o.allowed_domains && o.blocked_domains) throw new UnsupportedError(`${nt.kind}: a Anthropic não aceita allowedDomains e blockedDomains juntos (400). Use só um.`, { provider: this.name });
185
+ return {
186
+ type: tipo,
187
+ name: nome,
188
+ ...o
189
+ };
190
+ }
191
+ /** Container do code_execution: o explícito ou o último do histórico. */
192
+ static containerFrom(messages, tools) {
193
+ for (const t of tools) if (t instanceof NativeTool && t.kind === "code_execution") {
194
+ const explicito = t.options.container;
195
+ if (typeof explicito === "string" && explicito) return explicito;
196
+ for (const m of [...messages].reverse()) {
197
+ const cid = m.metadata?.[CONTAINER_KEY];
198
+ if (typeof cid === "string" && cid) return cid;
199
+ }
200
+ }
201
+ }
202
+ static toolChoice(tc) {
203
+ if (tc === "required" || tc === "any") return { type: "any" };
204
+ if (tc === "auto" || tc === "none") return { type: tc };
205
+ if (typeof tc === "string") return {
206
+ type: "tool",
207
+ name: tc
208
+ };
209
+ return tc;
210
+ }
211
+ forbidsForcedTools() {
212
+ return NO_FORCED_TOOLS.test(this.model ?? "");
213
+ }
214
+ kwargs(messages, options) {
215
+ const { system, convo } = this.split(messages);
216
+ const native = applyProfile(this.name, this.model, this.translate(options.params), this.profileModel ? { profileModel: this.profileModel } : void 0).params;
217
+ native.max_tokens ??= 8192;
218
+ const kw = {
219
+ model: this.model,
220
+ messages: convo,
221
+ ...native
222
+ };
223
+ if (system) kw.system = system;
224
+ const tools = options.tools;
225
+ if (tools?.length) {
226
+ kw.tools = this.formatTools(tools);
227
+ if (options.toolChoice !== void 0 && options.toolChoice !== null) {
228
+ const tc = AnthropicProvider.toolChoice(options.toolChoice);
229
+ if (this.forbidsForcedTools() && (tc.type === "any" || tc.type === "tool")) throw new UnsupportedError(`${this.model} não suporta forced tool use (toolChoice=${JSON.stringify(options.toolChoice)}): a API retorna 400. Use toolChoice 'auto' (com strict tool use, se quiser garantir o schema) ou structured output via parse().`, { provider: this.name });
230
+ kw.tool_choice = tc;
231
+ }
232
+ const container = AnthropicProvider.containerFrom(messages, tools);
233
+ if (container) kw.container = container;
234
+ }
235
+ return kw;
236
+ }
237
+ static thinkingBlocks(content) {
238
+ const out = [];
239
+ for (const b of content) {
240
+ const tipo = get(b, "type");
241
+ if (tipo === "thinking") out.push({
242
+ type: "thinking",
243
+ thinking: get(b, "thinking") ?? "",
244
+ signature: get(b, "signature") ?? ""
245
+ });
246
+ else if (tipo === "redacted_thinking") out.push({
247
+ type: "redacted_thinking",
248
+ data: get(b, "data") ?? ""
249
+ });
250
+ }
251
+ return out;
252
+ }
253
+ completion(resp) {
254
+ try {
255
+ return this.buildCompletion(resp);
256
+ } catch (e) {
257
+ if (e instanceof LLMError) throw e;
258
+ throw new ProviderError(`[${this.name}] resposta em formato inesperado: ${e instanceof Error ? `${e.name}: ${e.message}` : e}`, {
259
+ provider: this.name,
260
+ original: e
261
+ });
262
+ }
263
+ }
264
+ buildCompletion(resp) {
265
+ const content = get(resp, "content");
266
+ if (!Array.isArray(content)) throw new ServerError(`[${this.name}] resposta sem conteúdo (content=${content}).`, { provider: this.name });
267
+ const text = content.map((b) => get(b, "type") === "text" ? get(b, "text") ?? "" : "").join("");
268
+ const toolCalls = content.filter((b) => get(b, "type") === "tool_use" && get(b, "name")).map((b) => new ToolCall(String(get(b, "id") ?? ""), String(get(b, "name")), { ...get(b, "input") ?? {} }));
269
+ const thinking = AnthropicProvider.thinkingBlocks(content);
270
+ if (thinking.length && toolCalls[0]) toolCalls[0].metadata[THINKING_KEY] = thinking;
271
+ const { citations, serverCalls, preservar } = AnthropicProvider.nativeOutputs(content);
272
+ const meta = {};
273
+ if (preservar) meta[CONTENT_KEY] = content.map(dumpBlock);
274
+ const cid = get(get(resp, "container"), "id");
275
+ if (typeof cid === "string" && cid) meta[CONTAINER_KEY] = cid;
276
+ let usage = null;
277
+ const u = get(resp, "usage");
278
+ if (u) {
279
+ const cacheWrite = n(get(u, "cache_creation_input_tokens"));
280
+ const cacheRead = n(get(u, "cache_read_input_tokens"));
281
+ usage = {
282
+ inputTokens: n(get(u, "input_tokens")) + cacheWrite + cacheRead,
283
+ outputTokens: n(get(u, "output_tokens"))
284
+ };
285
+ if (cacheRead) usage.cacheReadTokens = cacheRead;
286
+ if (cacheWrite) usage.cacheWriteTokens = cacheWrite;
287
+ const stu = get(u, "server_tool_use");
288
+ if (stu) {
289
+ const reqs = {};
290
+ const ws = n(get(stu, "web_search_requests"));
291
+ const wf = n(get(stu, "web_fetch_requests"));
292
+ if (ws) reqs.web_search = ws;
293
+ if (wf) reqs.web_fetch = wf;
294
+ if (Object.keys(reqs).length) usage.serverToolRequests = reqs;
295
+ }
296
+ }
297
+ const stop = get(resp, "stop_reason") ?? null;
298
+ return new Completion({
299
+ text,
300
+ model: this.model,
301
+ provider: this.name,
302
+ raw: resp,
303
+ usage,
304
+ toolCalls,
305
+ finishReason: stop === "max_tokens" ? "length" : stop,
306
+ citations,
307
+ serverToolCalls: serverCalls,
308
+ metadata: meta
309
+ });
310
+ }
311
+ /**
312
+ * Citações dos blocos de texto + execuções de server tools (`server_tool_use`
313
+ * casado com o `*_tool_result` pelo `tool_use_id`). `preservar` diz se o turno
314
+ * tem blocos que precisam voltar intactos no histórico.
315
+ */
316
+ static nativeOutputs(content) {
317
+ const citations = [];
318
+ const calls = /* @__PURE__ */ new Map();
319
+ const ordem = [];
320
+ let preservar = false;
321
+ let pos = 0;
322
+ for (const b of content) {
323
+ const tipo = get(b, "type");
324
+ if (tipo === "text") {
325
+ const txt = get(b, "text") ?? "";
326
+ const cits = get(b, "citations") ?? [];
327
+ if (cits.length) preservar = true;
328
+ for (const c of cits) {
329
+ const ct = String(get(c, "type") ?? "");
330
+ citations.push({
331
+ url: get(c, "url") ?? null,
332
+ title: get(c, "title") ?? null,
333
+ citedText: get(c, "cited_text") ?? null,
334
+ start: pos,
335
+ end: pos + txt.length,
336
+ source: ct.includes("web") ? "web" : ct || null
337
+ });
338
+ }
339
+ pos += txt.length;
340
+ continue;
341
+ }
342
+ if (typeof tipo === "string" && PLAIN_BLOCKS.has(tipo)) continue;
343
+ preservar = true;
344
+ if (tipo === "server_tool_use") {
345
+ const id = get(b, "id") ?? null;
346
+ const stc = {
347
+ type: get(b, "name") || "server_tool",
348
+ input: get(b, "input"),
349
+ id
350
+ };
351
+ calls.set(id ?? String(calls.size), stc);
352
+ ordem.push(stc);
353
+ } else if (typeof tipo === "string" && tipo.endsWith("_tool_result")) {
354
+ const alvoId = get(b, "tool_use_id");
355
+ const saida = dumpBlock(b).content;
356
+ const alvo = alvoId ? calls.get(alvoId) : void 0;
357
+ if (alvo) alvo.output = saida;
358
+ else ordem.push({
359
+ type: tipo.slice(0, -12),
360
+ output: saida,
361
+ id: alvoId ?? null
362
+ });
363
+ }
364
+ }
365
+ return {
366
+ citations,
367
+ serverCalls: ordem,
368
+ preservar
369
+ };
370
+ }
371
+ static mergeUsage(total, resp) {
372
+ const u = get(resp, "usage");
373
+ if (!u) return;
374
+ for (const k of [
375
+ "input_tokens",
376
+ "output_tokens",
377
+ "cache_creation_input_tokens",
378
+ "cache_read_input_tokens"
379
+ ]) total[k] = (total[k] ?? 0) + n(get(u, k));
380
+ const stu = get(u, "server_tool_use");
381
+ for (const k of ["web_search_requests", "web_fetch_requests"]) total[k] = (total[k] ?? 0) + n(get(stu, k));
382
+ }
383
+ /** Resposta final com o conteúdo de TODAS as rodadas e o usage somado. */
384
+ static finalAfterPause(resp, acum, total) {
385
+ if (!acum.length) return resp;
386
+ AnthropicProvider.mergeUsage(total, resp);
387
+ return {
388
+ content: [...acum, ...get(resp, "content") ?? []],
389
+ usage: {
390
+ input_tokens: total.input_tokens ?? 0,
391
+ output_tokens: total.output_tokens ?? 0,
392
+ cache_creation_input_tokens: total.cache_creation_input_tokens ?? 0,
393
+ cache_read_input_tokens: total.cache_read_input_tokens ?? 0,
394
+ server_tool_use: {
395
+ web_search_requests: total.web_search_requests ?? 0,
396
+ web_fetch_requests: total.web_fetch_requests ?? 0
397
+ }
398
+ },
399
+ stop_reason: get(resp, "stop_reason"),
400
+ container: get(resp, "container"),
401
+ raw_last: resp
402
+ };
403
+ }
404
+ static continueAfterPause(kw, resp) {
405
+ const content = (get(resp, "content") ?? []).map(dumpBlock);
406
+ return {
407
+ ...kw,
408
+ messages: [...kw.messages, {
409
+ role: "assistant",
410
+ content
411
+ }]
412
+ };
413
+ }
414
+ async complete(messages, options) {
415
+ const client = await this.client();
416
+ let kw = this.kwargs(messages, options);
417
+ const acum = [];
418
+ const total = {};
419
+ let resp;
420
+ for (let i = 0; i <= MAX_PAUSE; i++) {
421
+ try {
422
+ resp = await client.messages.create(kw, { signal: options.signal });
423
+ } catch (e) {
424
+ throw this.err(e, options);
425
+ }
426
+ if (get(resp, "stop_reason") !== "pause_turn") break;
427
+ acum.push(...get(resp, "content") ?? []);
428
+ AnthropicProvider.mergeUsage(total, resp);
429
+ kw = AnthropicProvider.continueAfterPause(kw, resp);
430
+ }
431
+ return this.completion(AnthropicProvider.finalAfterPause(resp, acum, total));
432
+ }
433
+ toolKwargs(messages, schema, options) {
434
+ const kw = this.kwargs(messages, {
435
+ ...options,
436
+ tools: void 0,
437
+ toolChoice: void 0
438
+ });
439
+ const extract = {
440
+ name: "extract",
441
+ description: `Retorne um objeto ${schema.name} estruturado.`,
442
+ input_schema: schema.jsonSchema
443
+ };
444
+ if (this.forbidsForcedTools()) {
445
+ extract.strict = true;
446
+ extract.input_schema = strictSchema(extract.input_schema);
447
+ kw.tool_choice = { type: "auto" };
448
+ } else kw.tool_choice = {
449
+ type: "tool",
450
+ name: "extract"
451
+ };
452
+ kw.tools = [extract];
453
+ delete kw.thinking;
454
+ return kw;
455
+ }
456
+ async parse(messages, schema, options) {
457
+ const client = await this.client();
458
+ const kw = this.toolKwargs(messages, schema, options);
459
+ let resp;
460
+ try {
461
+ resp = await client.messages.create(kw, { signal: options.signal });
462
+ } catch (e) {
463
+ throw this.err(e, options);
464
+ }
465
+ const comp = this.completion(resp);
466
+ Provider.checkTruncation(comp, this.name);
467
+ const toolUse = (get(resp, "content") ?? []).find((b) => get(b, "type") === "tool_use");
468
+ if (!toolUse) throw new OutputValidationError(`[${this.name}] o modelo não devolveu o objeto estruturado (stop_reason=${JSON.stringify(comp.finishReason ?? "sem tool_use")}): ${JSON.stringify(comp.text.slice(0, 200))}`, { provider: this.name });
469
+ const input = get(toolUse, "input");
470
+ comp.text = JSON.stringify(input);
471
+ comp.parsed = await schema.validate(input);
472
+ comp.toolCalls = [];
473
+ return comp;
474
+ }
475
+ async *stream(messages, options) {
476
+ for await (const ev of this.streamEvents(messages, options)) if (ev.type === "text") yield ev.text;
477
+ }
478
+ async *streamEvents(messages, options) {
479
+ const client = await this.client();
480
+ let kw = this.kwargs(messages, options);
481
+ const acum = [];
482
+ const total = {};
483
+ let resp;
484
+ for (let i = 0; i <= MAX_PAUSE; i++) {
485
+ try {
486
+ const s = client.messages.stream(kw, { signal: options.signal });
487
+ for await (const ev of s) {
488
+ if (get(ev, "type") !== "content_block_delta") continue;
489
+ const delta = get(ev, "delta");
490
+ if (get(delta, "type") === "text_delta") {
491
+ const t = get(delta, "text");
492
+ if (typeof t === "string" && t) yield {
493
+ type: "text",
494
+ text: t
495
+ };
496
+ }
497
+ }
498
+ resp = await s.finalMessage();
499
+ } catch (e) {
500
+ throw this.err(e, options);
501
+ }
502
+ if (get(resp, "stop_reason") !== "pause_turn") break;
503
+ acum.push(...get(resp, "content") ?? []);
504
+ AnthropicProvider.mergeUsage(total, resp);
505
+ kw = AnthropicProvider.continueAfterPause(kw, resp);
506
+ }
507
+ yield {
508
+ type: "done",
509
+ text: "",
510
+ completion: this.completion(AnthropicProvider.finalAfterPause(resp, acum, total))
511
+ };
512
+ }
513
+ err(e, options) {
514
+ return options.signal?.aborted ? e : classify(e, this.name);
515
+ }
516
+ };
517
+ export { AnthropicProvider };