framework-agents 0.1.5 → 0.1.6
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/dist/agent-AXBAXCB5.js +7 -0
- package/dist/{agent-KV6TG47A.js → agent-Y4D6JSG5.js} +1 -1
- package/dist/{chunk-2XXGDP7B.js → chunk-AJI2QSJE.js} +94 -18
- package/dist/chunk-AJI2QSJE.js.map +1 -0
- package/dist/{chunk-G3CMR4MD.js → chunk-JUOOPH4Z.js} +102 -16
- package/dist/cli.js +103 -87
- package/dist/index.d.ts +2 -1
- package/dist/index.js +103 -87
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/agent-GDFFNEW5.js +0 -7
- package/dist/chunk-2XXGDP7B.js.map +0 -1
- /package/dist/{agent-GDFFNEW5.js.map → agent-AXBAXCB5.js.map} +0 -0
|
@@ -100,6 +100,16 @@ var ToolRejection = class extends Error {
|
|
|
100
100
|
this.feedback = feedback;
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
|
+
var RETRYABLE = /* @__PURE__ */ Symbol.for("framework.retryable");
|
|
104
|
+
function stampRetryable(err, retryable) {
|
|
105
|
+
err[RETRYABLE] = retryable;
|
|
106
|
+
return err;
|
|
107
|
+
}
|
|
108
|
+
function getRetryable(err) {
|
|
109
|
+
if (err instanceof AgentError) return err.retryable;
|
|
110
|
+
if (err instanceof Error) return err[RETRYABLE];
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
103
113
|
var HitlInterrupt = class extends Error {
|
|
104
114
|
interruptId;
|
|
105
115
|
tool;
|
|
@@ -461,6 +471,27 @@ async function runWithBudget(fn, budget = {}) {
|
|
|
461
471
|
}
|
|
462
472
|
|
|
463
473
|
// src/agent/index.ts
|
|
474
|
+
var EventQueue = class {
|
|
475
|
+
items = [];
|
|
476
|
+
wakes = [];
|
|
477
|
+
closed = false;
|
|
478
|
+
push(item) {
|
|
479
|
+
this.items.push(item);
|
|
480
|
+
this.wakes.shift()?.();
|
|
481
|
+
}
|
|
482
|
+
close() {
|
|
483
|
+
this.closed = true;
|
|
484
|
+
for (const w of this.wakes.splice(0)) w();
|
|
485
|
+
}
|
|
486
|
+
async next() {
|
|
487
|
+
for (; ; ) {
|
|
488
|
+
const item = this.items.shift();
|
|
489
|
+
if (item !== void 0) return { done: false, value: item };
|
|
490
|
+
if (this.closed) return { done: true };
|
|
491
|
+
await new Promise((resolve2) => this.wakes.push(resolve2));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
};
|
|
464
495
|
var DEFAULT_SYSTEM = "You are a helpful agent. Use tools when needed. Be concise.";
|
|
465
496
|
var DEFAULT_MAX_STEPS = 6;
|
|
466
497
|
var toolJsonSchemas = /* @__PURE__ */ new WeakMap();
|
|
@@ -798,27 +829,82 @@ var Agent = class _Agent {
|
|
|
798
829
|
...strategy === "provider" && format ? { responseFormat: { name: responseFormatName(format), schema: responseFormatJsonSchema(format) } } : {}
|
|
799
830
|
};
|
|
800
831
|
for (const mw of middleware) req = await mw.beforeModel?.(req, ctxFor(step)) ?? req;
|
|
801
|
-
const
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
832
|
+
const needsBatching = middleware.some((mw) => mw.wrapModel || mw.afterModel);
|
|
833
|
+
const buffered = [];
|
|
834
|
+
const channel = new EventQueue();
|
|
835
|
+
const emitDelta = (type, d) => {
|
|
836
|
+
const event = { type, delta: d };
|
|
837
|
+
if (needsBatching) buffered.push(event);
|
|
838
|
+
else channel.push(event);
|
|
839
|
+
};
|
|
840
|
+
const callModel = (r) => {
|
|
841
|
+
const start = buffered.length;
|
|
842
|
+
let failed = false;
|
|
843
|
+
try {
|
|
844
|
+
return model.complete({
|
|
845
|
+
...r,
|
|
846
|
+
// Propagate the run's abort into the in-flight provider request.
|
|
847
|
+
...signal ? { signal } : {},
|
|
848
|
+
onToken: (d) => {
|
|
849
|
+
events?.onToken?.(d);
|
|
850
|
+
if (failed) return;
|
|
851
|
+
emitDelta("token", d);
|
|
852
|
+
},
|
|
853
|
+
onThinking: (d) => {
|
|
854
|
+
events?.onThinking?.(d);
|
|
855
|
+
if (failed) return;
|
|
856
|
+
emitDelta("thinking", d);
|
|
857
|
+
}
|
|
858
|
+
}).catch((err) => {
|
|
859
|
+
failed = true;
|
|
860
|
+
buffered.splice(start);
|
|
861
|
+
throw err;
|
|
862
|
+
});
|
|
863
|
+
} catch (err) {
|
|
864
|
+
failed = true;
|
|
865
|
+
buffered.splice(start);
|
|
866
|
+
throw err;
|
|
813
867
|
}
|
|
814
|
-
}
|
|
868
|
+
};
|
|
815
869
|
const modelChain = middleware.filter((mw) => mw.wrapModel).reduceRight(
|
|
816
870
|
(next, mw) => (r) => mw.wrapModel(r, next, ctxFor(step)),
|
|
817
871
|
callModel
|
|
818
872
|
);
|
|
819
|
-
let
|
|
820
|
-
|
|
821
|
-
|
|
873
|
+
let deltasEmittedThisCall = false;
|
|
874
|
+
let completion;
|
|
875
|
+
const pendingCompletion = modelChain(req).then(
|
|
876
|
+
(c) => {
|
|
877
|
+
channel.close();
|
|
878
|
+
return c;
|
|
879
|
+
},
|
|
880
|
+
(e) => {
|
|
881
|
+
channel.close();
|
|
882
|
+
throw e;
|
|
883
|
+
}
|
|
884
|
+
);
|
|
885
|
+
pendingCompletion.catch(() => {
|
|
886
|
+
});
|
|
887
|
+
try {
|
|
888
|
+
if (needsBatching) {
|
|
889
|
+
completion = await pendingCompletion;
|
|
890
|
+
for (const e of buffered.splice(0)) {
|
|
891
|
+
if (e.delta !== "") yield e;
|
|
892
|
+
}
|
|
893
|
+
} else {
|
|
894
|
+
for (; ; ) {
|
|
895
|
+
const next = await channel.next();
|
|
896
|
+
if (next.done) break;
|
|
897
|
+
deltasEmittedThisCall = true;
|
|
898
|
+
if (next.value.delta !== "") yield next.value;
|
|
899
|
+
}
|
|
900
|
+
completion = await pendingCompletion;
|
|
901
|
+
}
|
|
902
|
+
} catch (err) {
|
|
903
|
+
buffered.splice(0);
|
|
904
|
+
if (deltasEmittedThisCall && getRetryable(err) === void 0) {
|
|
905
|
+
stampRetryable(err, false);
|
|
906
|
+
}
|
|
907
|
+
throw err;
|
|
822
908
|
}
|
|
823
909
|
for (const mw of [...middleware].reverse()) completion = await mw.afterModel?.(completion, req, ctxFor(step)) ?? completion;
|
|
824
910
|
usage.inputTokens += completion.usage.inputTokens;
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
imagesOf,
|
|
10
10
|
todoListMiddleware,
|
|
11
11
|
wireTextOf
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JUOOPH4Z.js";
|
|
13
13
|
|
|
14
14
|
// src/serve/cli.ts
|
|
15
15
|
import { resolve as resolve3 } from "path";
|
|
@@ -184,8 +184,8 @@ var OpenAICompatibleProvider = class {
|
|
|
184
184
|
}
|
|
185
185
|
} : {},
|
|
186
186
|
messages: options.messages.map(toWireMessage),
|
|
187
|
-
...options.tools
|
|
188
|
-
tools: options.tools.map((t) => ({
|
|
187
|
+
...options.tools?.length ? {
|
|
188
|
+
tools: (options.tools ?? []).map((t) => ({
|
|
189
189
|
type: "function",
|
|
190
190
|
function: {
|
|
191
191
|
name: t.name,
|
|
@@ -252,91 +252,107 @@ var OpenAICompatibleProvider = class {
|
|
|
252
252
|
/** SSE: `data: {...}` lines; delta.content -> onToken, delta.reasoning_content -> onThinking. */
|
|
253
253
|
async readStream(res, options) {
|
|
254
254
|
if (!res.body) throw new Error("Streaming response has no body");
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
let
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (
|
|
305
|
-
|
|
255
|
+
return this._readSse(res.body, options).catch((err) => {
|
|
256
|
+
if (err instanceof AgentError || err instanceof Error && err.name === "AbortError") throw err;
|
|
257
|
+
throw new AgentError("MODEL_ERROR", `Stream broke mid-response: ${err}`, false);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
async _readSse(body, options) {
|
|
261
|
+
let delivered = false;
|
|
262
|
+
try {
|
|
263
|
+
const reader = body.getReader();
|
|
264
|
+
const decoder = new TextDecoder();
|
|
265
|
+
let text = "";
|
|
266
|
+
let thinking = "";
|
|
267
|
+
let inputTokens = 0;
|
|
268
|
+
let outputTokens = 0;
|
|
269
|
+
let reasoningTokens;
|
|
270
|
+
let finishReason;
|
|
271
|
+
const toolArgBuffers = /* @__PURE__ */ new Map();
|
|
272
|
+
let buffer = "";
|
|
273
|
+
const flush = (chunk) => {
|
|
274
|
+
buffer += chunk;
|
|
275
|
+
const lines = buffer.split("\n");
|
|
276
|
+
buffer = lines.pop() ?? "";
|
|
277
|
+
for (const line of lines) {
|
|
278
|
+
const t = line.trim();
|
|
279
|
+
if (!t.startsWith("data:")) continue;
|
|
280
|
+
const payload = t.slice(5).trim();
|
|
281
|
+
if (payload === "[DONE]") continue;
|
|
282
|
+
let evt;
|
|
283
|
+
try {
|
|
284
|
+
evt = JSON.parse(payload);
|
|
285
|
+
} catch {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (evt.choices?.[0]?.finish_reason) finishReason = evt.choices[0].finish_reason ?? void 0;
|
|
289
|
+
if (evt.usage) {
|
|
290
|
+
if (evt.usage.prompt_tokens != null) inputTokens = evt.usage.prompt_tokens;
|
|
291
|
+
if (evt.usage.completion_tokens != null) outputTokens = evt.usage.completion_tokens;
|
|
292
|
+
const rt = reasoningOf(evt.usage);
|
|
293
|
+
if (rt != null) reasoningTokens = rt;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const delta = evt.choices?.[0]?.delta;
|
|
297
|
+
if (!delta) continue;
|
|
298
|
+
const think = thinkingOf(delta);
|
|
299
|
+
if (think) {
|
|
300
|
+
thinking += think;
|
|
301
|
+
delivered = true;
|
|
302
|
+
options.onThinking?.(think);
|
|
303
|
+
}
|
|
304
|
+
if (delta.content) {
|
|
305
|
+
text += delta.content;
|
|
306
|
+
delivered = true;
|
|
307
|
+
options.onToken?.(delta.content);
|
|
308
|
+
}
|
|
309
|
+
for (const [i, tc] of (delta.tool_calls ?? []).entries()) {
|
|
310
|
+
const key = tc.index ?? i;
|
|
311
|
+
delivered = true;
|
|
312
|
+
const slot = toolArgBuffers.get(key) ?? { id: "", name: "", args: "" };
|
|
313
|
+
if (tc.id) slot.id = tc.id;
|
|
314
|
+
if (tc.function?.name) slot.name = tc.function.name;
|
|
315
|
+
if (tc.function?.arguments) slot.args += tc.function.arguments;
|
|
316
|
+
toolArgBuffers.set(key, slot);
|
|
317
|
+
}
|
|
306
318
|
}
|
|
319
|
+
};
|
|
320
|
+
for (; ; ) {
|
|
321
|
+
const { done, value } = await reader.read();
|
|
322
|
+
if (done) break;
|
|
323
|
+
flush(decoder.decode(value, { stream: true }));
|
|
307
324
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (!text && !toolArgBuffers.size && thinking && finishReason === "stop") {
|
|
316
|
-
throw new AgentError(
|
|
317
|
-
"MODEL_ERROR",
|
|
318
|
-
"Stream ended at finish_reason=stop having put the entire answer in the reasoning channel (no content, no tool calls, thinking present). The reasoning/content split lost the reply \u2014 retry or raise maxTokens.",
|
|
319
|
-
true
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
if (!text && !toolArgBuffers.size && finishReason === "length") {
|
|
323
|
-
throw new AgentError(
|
|
324
|
-
"MODEL_ERROR",
|
|
325
|
-
`Stream ended at finish_reason=length with no content (${outputTokens} tokens burned). Raise maxTokens.`,
|
|
326
|
-
false
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
return {
|
|
330
|
-
text,
|
|
331
|
-
...thinking ? { thinking } : {},
|
|
332
|
-
...finishReason ? { finishReason } : {},
|
|
333
|
-
toolCalls: [...toolArgBuffers.values()].filter((t) => t.name).map((t) => ({ id: t.id || `call_${++callSeq}`, name: t.name, args: safeJson(t.args) })),
|
|
334
|
-
usage: {
|
|
335
|
-
inputTokens,
|
|
336
|
-
outputTokens,
|
|
337
|
-
...reasoningTokens != null ? { reasoningTokens } : {}
|
|
325
|
+
flush(decoder.decode());
|
|
326
|
+
if (!text && !toolArgBuffers.size && thinking && finishReason === "stop") {
|
|
327
|
+
throw new AgentError(
|
|
328
|
+
"MODEL_ERROR",
|
|
329
|
+
"Stream ended at finish_reason=stop having put the entire answer in the reasoning channel (no content, no tool calls, thinking present). The reasoning/content split lost the reply \u2014 retry or raise maxTokens.",
|
|
330
|
+
true
|
|
331
|
+
);
|
|
338
332
|
}
|
|
339
|
-
|
|
333
|
+
if (!text && !toolArgBuffers.size && finishReason === "length") {
|
|
334
|
+
throw new AgentError(
|
|
335
|
+
"MODEL_ERROR",
|
|
336
|
+
`Stream ended at finish_reason=length with no content (${outputTokens} tokens burned). Raise maxTokens.`,
|
|
337
|
+
false
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
text,
|
|
342
|
+
...thinking ? { thinking } : {},
|
|
343
|
+
...finishReason ? { finishReason } : {},
|
|
344
|
+
toolCalls: [...toolArgBuffers.values()].map((t) => ({ id: t.id || `call_${++callSeq}`, name: t.name, args: safeJson(t.args) })),
|
|
345
|
+
usage: {
|
|
346
|
+
inputTokens,
|
|
347
|
+
outputTokens,
|
|
348
|
+
...reasoningTokens != null ? { reasoningTokens } : {}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
} catch (err) {
|
|
352
|
+
if (err instanceof Error && err.name === "AbortError") throw err;
|
|
353
|
+
if (err instanceof AgentError) throw err;
|
|
354
|
+
throw new AgentError("MODEL_ERROR", `Stream broke mid-response${delivered ? " (deltas already delivered)" : ""}: ${err}`, !delivered);
|
|
355
|
+
}
|
|
340
356
|
}
|
|
341
357
|
};
|
|
342
358
|
var callSeq = 0;
|
|
@@ -431,8 +447,8 @@ var AnthropicProvider = class {
|
|
|
431
447
|
...thinkingBudget ? { thinking: { type: "enabled", budget_tokens: thinkingBudget } } : {},
|
|
432
448
|
...stream ? { stream: true } : {},
|
|
433
449
|
messages,
|
|
434
|
-
...options.tools
|
|
435
|
-
tools: options.tools.map((t) => ({
|
|
450
|
+
...options.tools?.length ? {
|
|
451
|
+
tools: (options.tools ?? []).map((t) => ({
|
|
436
452
|
name: t.name,
|
|
437
453
|
description: t.description,
|
|
438
454
|
input_schema: t.parametersSchema ?? { type: "object" }
|
package/dist/index.d.ts
CHANGED
|
@@ -625,6 +625,7 @@ declare class OpenAICompatibleProvider implements ModelProvider {
|
|
|
625
625
|
private readJson;
|
|
626
626
|
/** SSE: `data: {...}` lines; delta.content -> onToken, delta.reasoning_content -> onThinking. */
|
|
627
627
|
private readStream;
|
|
628
|
+
private _readSse;
|
|
628
629
|
}
|
|
629
630
|
/** Build a provider from env config (TogetherAI preferred when key present). */
|
|
630
631
|
declare function providerFromEnv(active: {
|
|
@@ -768,7 +769,7 @@ interface ResponseFormatWire {
|
|
|
768
769
|
interface CompleteOptions {
|
|
769
770
|
messages: ChatMessage[];
|
|
770
771
|
/** Tool metadata; `parametersSchema` (JSON Schema) lets models call with real args — omit only for zero-arg tools. */
|
|
771
|
-
tools
|
|
772
|
+
tools?: {
|
|
772
773
|
name: string;
|
|
773
774
|
description: string;
|
|
774
775
|
parametersSchema?: Record<string, unknown>;
|
package/dist/index.js
CHANGED
|
@@ -71,7 +71,7 @@ import {
|
|
|
71
71
|
usageCost,
|
|
72
72
|
user,
|
|
73
73
|
wireTextOf
|
|
74
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-AJI2QSJE.js";
|
|
75
75
|
|
|
76
76
|
// src/config/index.ts
|
|
77
77
|
import { existsSync } from "fs";
|
|
@@ -864,8 +864,8 @@ var OpenAICompatibleProvider = class {
|
|
|
864
864
|
}
|
|
865
865
|
} : {},
|
|
866
866
|
messages: options.messages.map(toWireMessage),
|
|
867
|
-
...options.tools
|
|
868
|
-
tools: options.tools.map((t) => ({
|
|
867
|
+
...options.tools?.length ? {
|
|
868
|
+
tools: (options.tools ?? []).map((t) => ({
|
|
869
869
|
type: "function",
|
|
870
870
|
function: {
|
|
871
871
|
name: t.name,
|
|
@@ -932,91 +932,107 @@ var OpenAICompatibleProvider = class {
|
|
|
932
932
|
/** SSE: `data: {...}` lines; delta.content -> onToken, delta.reasoning_content -> onThinking. */
|
|
933
933
|
async readStream(res, options) {
|
|
934
934
|
if (!res.body) throw new Error("Streaming response has no body");
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
let
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
if (
|
|
985
|
-
|
|
935
|
+
return this._readSse(res.body, options).catch((err) => {
|
|
936
|
+
if (err instanceof AgentError || err instanceof Error && err.name === "AbortError") throw err;
|
|
937
|
+
throw new AgentError("MODEL_ERROR", `Stream broke mid-response: ${err}`, false);
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
async _readSse(body, options) {
|
|
941
|
+
let delivered = false;
|
|
942
|
+
try {
|
|
943
|
+
const reader = body.getReader();
|
|
944
|
+
const decoder = new TextDecoder();
|
|
945
|
+
let text = "";
|
|
946
|
+
let thinking = "";
|
|
947
|
+
let inputTokens = 0;
|
|
948
|
+
let outputTokens = 0;
|
|
949
|
+
let reasoningTokens;
|
|
950
|
+
let finishReason;
|
|
951
|
+
const toolArgBuffers = /* @__PURE__ */ new Map();
|
|
952
|
+
let buffer = "";
|
|
953
|
+
const flush = (chunk) => {
|
|
954
|
+
buffer += chunk;
|
|
955
|
+
const lines = buffer.split("\n");
|
|
956
|
+
buffer = lines.pop() ?? "";
|
|
957
|
+
for (const line of lines) {
|
|
958
|
+
const t = line.trim();
|
|
959
|
+
if (!t.startsWith("data:")) continue;
|
|
960
|
+
const payload = t.slice(5).trim();
|
|
961
|
+
if (payload === "[DONE]") continue;
|
|
962
|
+
let evt;
|
|
963
|
+
try {
|
|
964
|
+
evt = JSON.parse(payload);
|
|
965
|
+
} catch {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
if (evt.choices?.[0]?.finish_reason) finishReason = evt.choices[0].finish_reason ?? void 0;
|
|
969
|
+
if (evt.usage) {
|
|
970
|
+
if (evt.usage.prompt_tokens != null) inputTokens = evt.usage.prompt_tokens;
|
|
971
|
+
if (evt.usage.completion_tokens != null) outputTokens = evt.usage.completion_tokens;
|
|
972
|
+
const rt = reasoningOf(evt.usage);
|
|
973
|
+
if (rt != null) reasoningTokens = rt;
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
const delta = evt.choices?.[0]?.delta;
|
|
977
|
+
if (!delta) continue;
|
|
978
|
+
const think = thinkingOf(delta);
|
|
979
|
+
if (think) {
|
|
980
|
+
thinking += think;
|
|
981
|
+
delivered = true;
|
|
982
|
+
options.onThinking?.(think);
|
|
983
|
+
}
|
|
984
|
+
if (delta.content) {
|
|
985
|
+
text += delta.content;
|
|
986
|
+
delivered = true;
|
|
987
|
+
options.onToken?.(delta.content);
|
|
988
|
+
}
|
|
989
|
+
for (const [i, tc] of (delta.tool_calls ?? []).entries()) {
|
|
990
|
+
const key = tc.index ?? i;
|
|
991
|
+
delivered = true;
|
|
992
|
+
const slot = toolArgBuffers.get(key) ?? { id: "", name: "", args: "" };
|
|
993
|
+
if (tc.id) slot.id = tc.id;
|
|
994
|
+
if (tc.function?.name) slot.name = tc.function.name;
|
|
995
|
+
if (tc.function?.arguments) slot.args += tc.function.arguments;
|
|
996
|
+
toolArgBuffers.set(key, slot);
|
|
997
|
+
}
|
|
986
998
|
}
|
|
999
|
+
};
|
|
1000
|
+
for (; ; ) {
|
|
1001
|
+
const { done, value } = await reader.read();
|
|
1002
|
+
if (done) break;
|
|
1003
|
+
flush(decoder.decode(value, { stream: true }));
|
|
987
1004
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
if (!text && !toolArgBuffers.size && thinking && finishReason === "stop") {
|
|
996
|
-
throw new AgentError(
|
|
997
|
-
"MODEL_ERROR",
|
|
998
|
-
"Stream ended at finish_reason=stop having put the entire answer in the reasoning channel (no content, no tool calls, thinking present). The reasoning/content split lost the reply \u2014 retry or raise maxTokens.",
|
|
999
|
-
true
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
if (!text && !toolArgBuffers.size && finishReason === "length") {
|
|
1003
|
-
throw new AgentError(
|
|
1004
|
-
"MODEL_ERROR",
|
|
1005
|
-
`Stream ended at finish_reason=length with no content (${outputTokens} tokens burned). Raise maxTokens.`,
|
|
1006
|
-
false
|
|
1007
|
-
);
|
|
1008
|
-
}
|
|
1009
|
-
return {
|
|
1010
|
-
text,
|
|
1011
|
-
...thinking ? { thinking } : {},
|
|
1012
|
-
...finishReason ? { finishReason } : {},
|
|
1013
|
-
toolCalls: [...toolArgBuffers.values()].filter((t) => t.name).map((t) => ({ id: t.id || `call_${++callSeq}`, name: t.name, args: safeJson(t.args) })),
|
|
1014
|
-
usage: {
|
|
1015
|
-
inputTokens,
|
|
1016
|
-
outputTokens,
|
|
1017
|
-
...reasoningTokens != null ? { reasoningTokens } : {}
|
|
1005
|
+
flush(decoder.decode());
|
|
1006
|
+
if (!text && !toolArgBuffers.size && thinking && finishReason === "stop") {
|
|
1007
|
+
throw new AgentError(
|
|
1008
|
+
"MODEL_ERROR",
|
|
1009
|
+
"Stream ended at finish_reason=stop having put the entire answer in the reasoning channel (no content, no tool calls, thinking present). The reasoning/content split lost the reply \u2014 retry or raise maxTokens.",
|
|
1010
|
+
true
|
|
1011
|
+
);
|
|
1018
1012
|
}
|
|
1019
|
-
|
|
1013
|
+
if (!text && !toolArgBuffers.size && finishReason === "length") {
|
|
1014
|
+
throw new AgentError(
|
|
1015
|
+
"MODEL_ERROR",
|
|
1016
|
+
`Stream ended at finish_reason=length with no content (${outputTokens} tokens burned). Raise maxTokens.`,
|
|
1017
|
+
false
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
return {
|
|
1021
|
+
text,
|
|
1022
|
+
...thinking ? { thinking } : {},
|
|
1023
|
+
...finishReason ? { finishReason } : {},
|
|
1024
|
+
toolCalls: [...toolArgBuffers.values()].map((t) => ({ id: t.id || `call_${++callSeq}`, name: t.name, args: safeJson(t.args) })),
|
|
1025
|
+
usage: {
|
|
1026
|
+
inputTokens,
|
|
1027
|
+
outputTokens,
|
|
1028
|
+
...reasoningTokens != null ? { reasoningTokens } : {}
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
} catch (err) {
|
|
1032
|
+
if (err instanceof Error && err.name === "AbortError") throw err;
|
|
1033
|
+
if (err instanceof AgentError) throw err;
|
|
1034
|
+
throw new AgentError("MODEL_ERROR", `Stream broke mid-response${delivered ? " (deltas already delivered)" : ""}: ${err}`, !delivered);
|
|
1035
|
+
}
|
|
1020
1036
|
}
|
|
1021
1037
|
};
|
|
1022
1038
|
var callSeq = 0;
|
|
@@ -1114,8 +1130,8 @@ var AnthropicProvider = class {
|
|
|
1114
1130
|
...thinkingBudget ? { thinking: { type: "enabled", budget_tokens: thinkingBudget } } : {},
|
|
1115
1131
|
...stream ? { stream: true } : {},
|
|
1116
1132
|
messages,
|
|
1117
|
-
...options.tools
|
|
1118
|
-
tools: options.tools.map((t) => ({
|
|
1133
|
+
...options.tools?.length ? {
|
|
1134
|
+
tools: (options.tools ?? []).map((t) => ({
|
|
1119
1135
|
name: t.name,
|
|
1120
1136
|
description: t.description,
|
|
1121
1137
|
input_schema: t.parametersSchema ?? { type: "object" }
|