@sema-agent/core 1.294.0 → 1.296.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/dist/agents/subagent.d.ts.map +1 -1
- package/dist/agents/subagent.js +24 -3
- package/dist/agents/subagent.js.map +1 -1
- package/dist/bin/sema-tb.d.ts +20 -0
- package/dist/bin/sema-tb.d.ts.map +1 -1
- package/dist/bin/sema-tb.js +71 -23
- package/dist/bin/sema-tb.js.map +1 -1
- package/dist/brain/anthropic.d.ts.map +1 -1
- package/dist/brain/anthropic.js +17 -3
- package/dist/brain/anthropic.js.map +1 -1
- package/dist/brain/openai.d.ts +1 -0
- package/dist/brain/openai.d.ts.map +1 -1
- package/dist/brain/openai.js +328 -298
- package/dist/brain/openai.js.map +1 -1
- package/dist/brain/stream-engine.d.ts +7 -0
- package/dist/brain/stream-engine.d.ts.map +1 -1
- package/dist/brain/stream-engine.js +44 -11
- package/dist/brain/stream-engine.js.map +1 -1
- package/dist/brain/timeout.d.ts +2 -1
- package/dist/brain/timeout.d.ts.map +1 -1
- package/dist/brain/timeout.js +14 -1
- package/dist/brain/timeout.js.map +1 -1
- package/dist/core/ask-question.d.ts.map +1 -1
- package/dist/core/ask-question.js.map +1 -1
- package/dist/core/hooks.d.ts +3 -3
- package/dist/core/hooks.d.ts.map +1 -1
- package/dist/core/hooks.js +11 -4
- package/dist/core/hooks.js.map +1 -1
- package/dist/core/present-plan-tool.d.ts.map +1 -1
- package/dist/core/present-plan-tool.js +12 -13
- package/dist/core/present-plan-tool.js.map +1 -1
- package/dist/core/runner/assemble-result.d.ts +1 -0
- package/dist/core/runner/assemble-result.d.ts.map +1 -1
- package/dist/core/runner/assemble-result.js.map +1 -1
- package/dist/core/runner/call-cap.d.ts +4 -1
- package/dist/core/runner/call-cap.d.ts.map +1 -1
- package/dist/core/runner/call-cap.js +4 -1
- package/dist/core/runner/call-cap.js.map +1 -1
- package/dist/core/runner/cut-kill.d.ts +11 -0
- package/dist/core/runner/cut-kill.d.ts.map +1 -0
- package/dist/core/runner/cut-kill.js +38 -0
- package/dist/core/runner/cut-kill.js.map +1 -0
- package/dist/core/runner/prepare-task.d.ts +5 -0
- package/dist/core/runner/prepare-task.d.ts.map +1 -1
- package/dist/core/runner/prepare-task.js +70 -23
- package/dist/core/runner/prepare-task.js.map +1 -1
- package/dist/core/runner/runtask.d.ts.map +1 -1
- package/dist/core/runner/runtask.js +22 -6
- package/dist/core/runner/runtask.js.map +1 -1
- package/dist/core/sensitive-path-policy.d.ts +2 -1
- package/dist/core/sensitive-path-policy.d.ts.map +1 -1
- package/dist/core/sensitive-path-policy.js +22 -0
- package/dist/core/sensitive-path-policy.js.map +1 -1
- package/dist/core/task-registry.d.ts +4 -0
- package/dist/core/task-registry.d.ts.map +1 -1
- package/dist/core/task-registry.js +83 -15
- package/dist/core/task-registry.js.map +1 -1
- package/dist/core/tool-policy.d.ts +5 -2
- package/dist/core/tool-policy.d.ts.map +1 -1
- package/dist/core/tool-policy.js +9 -0
- package/dist/core/tool-policy.js.map +1 -1
- package/dist/core/trace.d.ts +3 -0
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/core/trace.js.map +1 -1
- package/dist/core/types.d.ts +6 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/engine/compaction/utils.d.ts.map +1 -1
- package/dist/engine/compaction/utils.js +3 -1
- package/dist/engine/compaction/utils.js.map +1 -1
- package/dist/engine/execution-env/kill-tree.d.ts +1 -0
- package/dist/engine/execution-env/kill-tree.d.ts.map +1 -1
- package/dist/engine/execution-env/kill-tree.js +18 -2
- package/dist/engine/execution-env/kill-tree.js.map +1 -1
- package/dist/engine/execution-env/node-execution-env.d.ts +5 -0
- package/dist/engine/execution-env/node-execution-env.d.ts.map +1 -1
- package/dist/engine/execution-env/node-execution-env.js +19 -0
- package/dist/engine/execution-env/node-execution-env.js.map +1 -1
- package/dist/engine/harness/agent-harness.d.ts +1 -0
- package/dist/engine/harness/agent-harness.d.ts.map +1 -1
- package/dist/engine/harness/agent-harness.js +4 -0
- package/dist/engine/harness/agent-harness.js.map +1 -1
- package/dist/engine/harness/messages.d.ts +2 -0
- package/dist/engine/harness/messages.d.ts.map +1 -1
- package/dist/engine/harness/messages.js +52 -3
- package/dist/engine/harness/messages.js.map +1 -1
- package/dist/engine/harness/types.d.ts +7 -0
- package/dist/engine/harness/types.d.ts.map +1 -1
- package/dist/engine/harness/types.js.map +1 -1
- package/dist/engine/llm/types.d.ts +6 -0
- package/dist/engine/llm/types.d.ts.map +1 -1
- package/dist/engine/loop/agent-loop.js +3 -0
- package/dist/engine/loop/agent-loop.js.map +1 -1
- package/dist/engine/loop/types.d.ts +2 -0
- package/dist/engine/loop/types.d.ts.map +1 -1
- package/dist/engine/session/import-validate.d.ts.map +1 -1
- package/dist/engine/session/import-validate.js +64 -0
- package/dist/engine/session/import-validate.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/llm.d.ts +1 -1
- package/dist/internal/llm.d.ts.map +1 -1
- package/dist/scenarios/full-body.d.ts.map +1 -1
- package/dist/scenarios/full-body.js +3 -2
- package/dist/scenarios/full-body.js.map +1 -1
- package/dist/tools/fs/index.d.ts +8 -1
- package/dist/tools/fs/index.d.ts.map +1 -1
- package/dist/tools/fs/index.js +92 -30
- package/dist/tools/fs/index.js.map +1 -1
- package/dist/tools/fs/safety.d.ts +1 -1
- package/dist/tools/fs/safety.d.ts.map +1 -1
- package/dist/tools/fs/safety.js +3 -1
- package/dist/tools/fs/safety.js.map +1 -1
- package/dist/tools/fs/search.d.ts +7 -2
- package/dist/tools/fs/search.d.ts.map +1 -1
- package/dist/tools/fs/search.js +465 -57
- package/dist/tools/fs/search.js.map +1 -1
- package/dist/tools/monitor.d.ts.map +1 -1
- package/dist/tools/monitor.js +12 -1
- package/dist/tools/monitor.js.map +1 -1
- package/dist/tools/task-list.d.ts.map +1 -1
- package/dist/tools/task-list.js.map +1 -1
- package/dist/tools/worktree.d.ts.map +1 -1
- package/dist/tools/worktree.js +15 -2
- package/dist/tools/worktree.js.map +1 -1
- package/package.json +1 -1
package/dist/brain/openai.js
CHANGED
|
@@ -258,324 +258,354 @@ function computeUsage(model, raw) {
|
|
|
258
258
|
cost: { input: costInput, output: costOutput, cacheRead: 0, cacheWrite: 0, total: costInput + costOutput },
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
|
+
export function inferMaxTokensField(modelId) {
|
|
262
|
+
const id = modelId.toLowerCase();
|
|
263
|
+
if (/^gpt-5/.test(id) || /^chatgpt-4o/.test(id) || /^o[134](?:$|[-.])/.test(id))
|
|
264
|
+
return "max_completion_tokens";
|
|
265
|
+
return "max_tokens";
|
|
266
|
+
}
|
|
261
267
|
export function createOpenAIBrain(config = {}) {
|
|
262
268
|
const doFetch = config.fetchImpl ?? fetch;
|
|
263
|
-
const stream = (model, context, options) =>
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
stream_options: { include_usage: true },
|
|
280
|
-
};
|
|
281
|
-
const tools = toOpenAITools(context);
|
|
282
|
-
if (tools)
|
|
283
|
-
body.tools = tools;
|
|
284
|
-
if (options?.temperature !== undefined)
|
|
285
|
-
body.temperature = options.temperature;
|
|
286
|
-
if (options?.maxTokens !== undefined) {
|
|
287
|
-
const mtField = (model.compat ?? {}).maxTokensField;
|
|
288
|
-
body[mtField ?? "max_tokens"] = options.maxTokens;
|
|
289
|
-
}
|
|
290
|
-
if (options?.stop)
|
|
291
|
-
body.stop = options.stop;
|
|
292
|
-
applyThinking(body, model, options?.reasoning);
|
|
293
|
-
const headers = {
|
|
294
|
-
...model.headers,
|
|
295
|
-
...config.headers,
|
|
296
|
-
...options?.headers,
|
|
297
|
-
};
|
|
298
|
-
headers["content-type"] = "application/json";
|
|
299
|
-
if (apiKey)
|
|
300
|
-
headers["authorization"] = `Bearer ${apiKey}`;
|
|
301
|
-
return { url: `${root}/chat/completions`, headers, body: JSON.stringify(applyExtraBody(body, model.extraBody, OPENAI_RESERVED)) };
|
|
302
|
-
},
|
|
303
|
-
makeParser: (ctrl) => {
|
|
304
|
-
const { out, partial } = ctrl;
|
|
305
|
-
let accumText = "";
|
|
306
|
-
let accumReasoning = "";
|
|
307
|
-
let accumRefusal = "";
|
|
308
|
-
let degenerate = false;
|
|
309
|
-
let degenFace;
|
|
310
|
-
let repCut;
|
|
311
|
-
const repSpared = new Map();
|
|
312
|
-
const pollRepetition = (text) => {
|
|
313
|
-
const insp = inspectDegenerate(text);
|
|
314
|
-
for (const s of insp.spared) {
|
|
315
|
-
const k = `${s.rule} ${s.segment}`;
|
|
316
|
-
const prev = repSpared.get(k);
|
|
317
|
-
if (prev === undefined)
|
|
318
|
-
repSpared.set(k, s);
|
|
319
|
-
else if (s.reps > prev.reps)
|
|
320
|
-
prev.reps = s.reps;
|
|
321
|
-
}
|
|
322
|
-
if (insp.degenerate)
|
|
323
|
-
repCut = insp.cut;
|
|
324
|
-
return insp.degenerate;
|
|
325
|
-
};
|
|
326
|
-
let walltimeCut = false;
|
|
327
|
-
const callDeadlineMs = options?.callDeadlineMs;
|
|
328
|
-
let lastDegenCheck = 0;
|
|
329
|
-
let lastDegenCheckReasoning = 0;
|
|
330
|
-
const detectRep = config.detectRepetition !== false;
|
|
331
|
-
let emittedTextStart = false;
|
|
332
|
-
let emittedThinkingStart = false;
|
|
333
|
-
let thinkingClosed = false;
|
|
334
|
-
let textIndex = 0;
|
|
335
|
-
const toolAccum = new Map();
|
|
336
|
-
let finishReason = null;
|
|
337
|
-
let usageRaw;
|
|
338
|
-
const thinkingBlock = { type: "thinking", thinking: "" };
|
|
339
|
-
const textBlock = { type: "text", text: "" };
|
|
340
|
-
const pushReasoningDelta = (delta) => {
|
|
341
|
-
if (!delta)
|
|
342
|
-
return;
|
|
343
|
-
ctrl.sawContentToken();
|
|
344
|
-
if (thinkingClosed)
|
|
345
|
-
return;
|
|
346
|
-
if (!emittedThinkingStart) {
|
|
347
|
-
emittedThinkingStart = true;
|
|
348
|
-
partial.content = [thinkingBlock];
|
|
349
|
-
out.push({ type: "thinking_start", contentIndex: 0, partial: { ...partial } });
|
|
350
|
-
}
|
|
351
|
-
accumReasoning += delta;
|
|
352
|
-
thinkingBlock.thinking = accumReasoning;
|
|
353
|
-
out.push({ type: "thinking_delta", contentIndex: 0, delta, partial: { ...partial } });
|
|
354
|
-
if (detectRep && !degenerate && accumReasoning.length - lastDegenCheckReasoning >= 64) {
|
|
355
|
-
lastDegenCheckReasoning = accumReasoning.length;
|
|
356
|
-
if (pollRepetition(accumReasoning)) {
|
|
357
|
-
degenerate = true;
|
|
358
|
-
degenFace = "reasoning";
|
|
359
|
-
ctrl.cancel();
|
|
360
|
-
}
|
|
269
|
+
const stream = (model, context, options) => {
|
|
270
|
+
let sentMaxTokens;
|
|
271
|
+
let sentMaxTokensLane = "unset";
|
|
272
|
+
return runStreamingBrain({
|
|
273
|
+
model,
|
|
274
|
+
doFetch,
|
|
275
|
+
signal: options?.signal,
|
|
276
|
+
config,
|
|
277
|
+
httpLabel: "gateway",
|
|
278
|
+
callDeadlineMs: options?.callDeadlineMs,
|
|
279
|
+
stallTimeouts: options?.stallTimeouts,
|
|
280
|
+
buildRequest: () => {
|
|
281
|
+
const apiKey = options?.apiKey ?? config.apiKey;
|
|
282
|
+
const root = (model.baseUrl || config.baseUrl || "").replace(/\/+$/, "");
|
|
283
|
+
if (!root) {
|
|
284
|
+
throw new Error("createOpenAIBrain: no baseUrl configured (set config.baseUrl or model.baseUrl)");
|
|
361
285
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
286
|
+
const body = {
|
|
287
|
+
model: model.id,
|
|
288
|
+
messages: toOpenAIMessages(context, config.replayThinking, model),
|
|
289
|
+
stream: true,
|
|
290
|
+
stream_options: { include_usage: true },
|
|
291
|
+
};
|
|
292
|
+
const tools = toOpenAITools(context);
|
|
293
|
+
if (tools)
|
|
294
|
+
body.tools = tools;
|
|
295
|
+
if (options?.temperature !== undefined)
|
|
296
|
+
body.temperature = options.temperature;
|
|
297
|
+
const requestedMaxTokens = options?.maxTokens ?? model.maxTokens;
|
|
298
|
+
if (requestedMaxTokens !== undefined) {
|
|
299
|
+
const declared = (model.compat ?? {}).maxTokensField;
|
|
300
|
+
const mtField = declared ?? inferMaxTokensField(model.id);
|
|
301
|
+
const capped = Math.max(1, requestedMaxTokens);
|
|
302
|
+
body[mtField] = capped;
|
|
303
|
+
sentMaxTokens = capped;
|
|
304
|
+
sentMaxTokensLane = options?.maxTokens !== undefined ? "options" : "model";
|
|
367
305
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
return;
|
|
372
|
-
ctrl.sawContentToken();
|
|
373
|
-
if (!emittedTextStart) {
|
|
374
|
-
closeThinking();
|
|
375
|
-
emittedTextStart = true;
|
|
376
|
-
textIndex = emittedThinkingStart ? 1 : 0;
|
|
377
|
-
partial.content = [textBlock];
|
|
378
|
-
out.push({ type: "text_start", contentIndex: textIndex, partial: { ...partial } });
|
|
306
|
+
else {
|
|
307
|
+
sentMaxTokens = undefined;
|
|
308
|
+
sentMaxTokensLane = "unset";
|
|
379
309
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
310
|
+
if (options?.stop)
|
|
311
|
+
body.stop = options.stop;
|
|
312
|
+
applyThinking(body, model, options?.reasoning);
|
|
313
|
+
const headers = {
|
|
314
|
+
...model.headers,
|
|
315
|
+
...config.headers,
|
|
316
|
+
...options?.headers,
|
|
317
|
+
};
|
|
318
|
+
headers["content-type"] = "application/json";
|
|
319
|
+
if (apiKey)
|
|
320
|
+
headers["authorization"] = `Bearer ${apiKey}`;
|
|
321
|
+
return { url: `${root}/chat/completions`, headers, body: JSON.stringify(applyExtraBody(body, model.extraBody, OPENAI_RESERVED)) };
|
|
322
|
+
},
|
|
323
|
+
makeParser: (ctrl) => {
|
|
324
|
+
const { out, partial } = ctrl;
|
|
325
|
+
let accumText = "";
|
|
326
|
+
let accumReasoning = "";
|
|
327
|
+
let accumRefusal = "";
|
|
328
|
+
let degenerate = false;
|
|
329
|
+
let degenFace;
|
|
330
|
+
let repCut;
|
|
331
|
+
const repSpared = new Map();
|
|
332
|
+
const pollRepetition = (text) => {
|
|
333
|
+
const insp = inspectDegenerate(text);
|
|
334
|
+
for (const s of insp.spared) {
|
|
335
|
+
const k = `${s.rule} ${s.segment}`;
|
|
336
|
+
const prev = repSpared.get(k);
|
|
337
|
+
if (prev === undefined)
|
|
338
|
+
repSpared.set(k, s);
|
|
339
|
+
else if (s.reps > prev.reps)
|
|
340
|
+
prev.reps = s.reps;
|
|
389
341
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
if (walltimeCut)
|
|
412
|
-
return;
|
|
413
|
-
const line = rawLine.trim();
|
|
414
|
-
if (!line.startsWith("data:"))
|
|
342
|
+
if (insp.degenerate)
|
|
343
|
+
repCut = insp.cut;
|
|
344
|
+
return insp.degenerate;
|
|
345
|
+
};
|
|
346
|
+
let walltimeCut = false;
|
|
347
|
+
const callDeadlineMs = options?.callDeadlineMs;
|
|
348
|
+
let lastDegenCheck = 0;
|
|
349
|
+
let lastDegenCheckReasoning = 0;
|
|
350
|
+
const detectRep = config.detectRepetition !== false;
|
|
351
|
+
let emittedTextStart = false;
|
|
352
|
+
let emittedThinkingStart = false;
|
|
353
|
+
let thinkingClosed = false;
|
|
354
|
+
let textIndex = 0;
|
|
355
|
+
const toolAccum = new Map();
|
|
356
|
+
let finishReason = null;
|
|
357
|
+
let usageRaw;
|
|
358
|
+
const thinkingBlock = { type: "thinking", thinking: "" };
|
|
359
|
+
const textBlock = { type: "text", text: "" };
|
|
360
|
+
const pushReasoningDelta = (delta) => {
|
|
361
|
+
if (!delta)
|
|
415
362
|
return;
|
|
416
|
-
|
|
417
|
-
if (
|
|
363
|
+
ctrl.sawContentToken();
|
|
364
|
+
if (thinkingClosed)
|
|
418
365
|
return;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
366
|
+
if (!emittedThinkingStart) {
|
|
367
|
+
emittedThinkingStart = true;
|
|
368
|
+
partial.content = [thinkingBlock];
|
|
369
|
+
out.push({ type: "thinking_start", contentIndex: 0, partial: { ...partial } });
|
|
422
370
|
}
|
|
423
|
-
|
|
424
|
-
|
|
371
|
+
accumReasoning += delta;
|
|
372
|
+
thinkingBlock.thinking = accumReasoning;
|
|
373
|
+
out.push({ type: "thinking_delta", contentIndex: 0, delta, partial: { ...partial } });
|
|
374
|
+
if (detectRep && !degenerate && accumReasoning.length - lastDegenCheckReasoning >= 64) {
|
|
375
|
+
lastDegenCheckReasoning = accumReasoning.length;
|
|
376
|
+
if (pollRepetition(accumReasoning)) {
|
|
377
|
+
degenerate = true;
|
|
378
|
+
degenFace = "reasoning";
|
|
379
|
+
ctrl.cancel();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const closeThinking = () => {
|
|
384
|
+
if (emittedThinkingStart && !thinkingClosed) {
|
|
385
|
+
thinkingClosed = true;
|
|
386
|
+
out.push({ type: "thinking_end", contentIndex: 0, content: accumReasoning, partial: { ...partial } });
|
|
425
387
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if (!choice)
|
|
388
|
+
};
|
|
389
|
+
const pushTextDelta = (delta) => {
|
|
390
|
+
if (!delta)
|
|
430
391
|
return;
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
392
|
+
ctrl.sawContentToken();
|
|
393
|
+
if (!emittedTextStart) {
|
|
394
|
+
closeThinking();
|
|
395
|
+
emittedTextStart = true;
|
|
396
|
+
textIndex = emittedThinkingStart ? 1 : 0;
|
|
397
|
+
partial.content = [textBlock];
|
|
398
|
+
out.push({ type: "text_start", contentIndex: textIndex, partial: { ...partial } });
|
|
399
|
+
}
|
|
400
|
+
accumText += delta;
|
|
401
|
+
textBlock.text = accumText;
|
|
402
|
+
out.push({ type: "text_delta", contentIndex: textIndex, delta, partial: { ...partial } });
|
|
403
|
+
if (detectRep && !degenerate && accumText.length - lastDegenCheck >= 64) {
|
|
404
|
+
lastDegenCheck = accumText.length;
|
|
405
|
+
if (pollRepetition(accumText)) {
|
|
406
|
+
degenerate = true;
|
|
407
|
+
degenFace = "text";
|
|
408
|
+
ctrl.cancel();
|
|
409
|
+
}
|
|
441
410
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
411
|
+
};
|
|
412
|
+
return {
|
|
413
|
+
snapshot() {
|
|
414
|
+
const hasCompletedToolCall = [...toolAccum.values()].some((a) => a.closedTc !== undefined);
|
|
415
|
+
const hasSubstantiveText = accumText.trim() !== "" || accumRefusal.trim() !== "";
|
|
416
|
+
return {
|
|
417
|
+
hasSubstantiveText,
|
|
418
|
+
hasCompletedToolCall,
|
|
419
|
+
hasOnlyThinking: accumReasoning.length > 0 && !hasSubstantiveText && !hasCompletedToolCall,
|
|
420
|
+
};
|
|
421
|
+
},
|
|
422
|
+
sealForRetry() {
|
|
423
|
+
closeThinking();
|
|
424
|
+
},
|
|
425
|
+
onDeadline() {
|
|
426
|
+
if (degenerate)
|
|
427
|
+
return false;
|
|
428
|
+
walltimeCut = true;
|
|
429
|
+
return true;
|
|
430
|
+
},
|
|
431
|
+
onLine(rawLine) {
|
|
432
|
+
if (callDeadlineMs !== undefined && !walltimeCut && !degenerate && Date.now() >= callDeadlineMs) {
|
|
433
|
+
walltimeCut = true;
|
|
434
|
+
ctrl.cancel();
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
if (walltimeCut)
|
|
438
|
+
return;
|
|
439
|
+
const line = rawLine.trim();
|
|
440
|
+
if (!line.startsWith("data:"))
|
|
441
|
+
return;
|
|
442
|
+
const data = line.slice(5).trim();
|
|
443
|
+
if (!data || data === "[DONE]")
|
|
444
|
+
return;
|
|
445
|
+
let chunk;
|
|
446
|
+
try {
|
|
447
|
+
chunk = JSON.parse(data);
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (chunk.usage)
|
|
453
|
+
usageRaw = chunk.usage;
|
|
454
|
+
const choice = chunk.choices?.[0];
|
|
455
|
+
if (!choice)
|
|
456
|
+
return;
|
|
457
|
+
if (choice.finish_reason)
|
|
458
|
+
finishReason = choice.finish_reason;
|
|
459
|
+
const delta = choice.delta;
|
|
460
|
+
if (delta?.reasoning)
|
|
461
|
+
pushReasoningDelta(delta.reasoning);
|
|
462
|
+
else if (delta?.reasoning_content)
|
|
463
|
+
pushReasoningDelta(delta.reasoning_content);
|
|
464
|
+
if (delta?.refusal) {
|
|
465
|
+
ctrl.sawContentToken();
|
|
466
|
+
accumRefusal += delta.refusal;
|
|
467
|
+
}
|
|
468
|
+
if (delta?.content)
|
|
469
|
+
pushTextDelta(delta.content);
|
|
470
|
+
if (delta?.tool_calls) {
|
|
471
|
+
ctrl.sawContentToken();
|
|
472
|
+
for (const tc of delta.tool_calls) {
|
|
473
|
+
const idx = tc.index ?? (tc.id ? toolAccum.size : Math.max(0, toolAccum.size - 1));
|
|
474
|
+
if (!toolAccum.has(idx)) {
|
|
475
|
+
for (const [prevIdx, prevAcc] of [...toolAccum.entries()].sort((a, b) => a[0] - b[0])) {
|
|
476
|
+
if (prevIdx >= idx || prevAcc.closedTc)
|
|
477
|
+
continue;
|
|
478
|
+
if (prevAcc.args === "")
|
|
479
|
+
continue;
|
|
480
|
+
const closed = closeToolCallAccum(prevAcc);
|
|
481
|
+
if (closed) {
|
|
482
|
+
out.push({ type: "toolcall_end", contentIndex: prevIdx, toolCall: closed, partial: { ...partial } });
|
|
483
|
+
}
|
|
457
484
|
}
|
|
458
485
|
}
|
|
486
|
+
const acc = toolAccum.get(idx) ?? { id: "", name: "", args: "" };
|
|
487
|
+
if (tc.id)
|
|
488
|
+
acc.id = tc.id;
|
|
489
|
+
if (tc.function?.name)
|
|
490
|
+
acc.name = tc.function.name;
|
|
491
|
+
if (tc.function?.arguments && !acc.closedTc)
|
|
492
|
+
acc.args += tc.function.arguments;
|
|
493
|
+
toolAccum.set(idx, acc);
|
|
459
494
|
}
|
|
460
|
-
const acc = toolAccum.get(idx) ?? { id: "", name: "", args: "" };
|
|
461
|
-
if (tc.id)
|
|
462
|
-
acc.id = tc.id;
|
|
463
|
-
if (tc.function?.name)
|
|
464
|
-
acc.name = tc.function.name;
|
|
465
|
-
if (tc.function?.arguments && !acc.closedTc)
|
|
466
|
-
acc.args += tc.function.arguments;
|
|
467
|
-
toolAccum.set(idx, acc);
|
|
468
495
|
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
out.push({ type: "text_end", contentIndex: textIndex, content: accumText, partial: { ...partial } });
|
|
475
|
-
}
|
|
476
|
-
const finalContent = [];
|
|
477
|
-
if (accumReasoning)
|
|
478
|
-
finalContent.push({ type: "thinking", thinking: accumReasoning });
|
|
479
|
-
const textFace = degenerate && degenFace === "text" && repCut !== undefined ? trimDegenerateTail(accumText, repCut) : accumText;
|
|
480
|
-
if (textFace)
|
|
481
|
-
finalContent.push({ type: "text", text: textFace });
|
|
482
|
-
const toolCalls = [];
|
|
483
|
-
const malformed = [];
|
|
484
|
-
for (const acc of [...toolAccum.entries()].sort((a, b) => a[0] - b[0]).map((e) => e[1])) {
|
|
485
|
-
if (!acc.name)
|
|
486
|
-
continue;
|
|
487
|
-
const tc = closeToolCallAccum(acc);
|
|
488
|
-
if (tc) {
|
|
489
|
-
toolCalls.push(tc);
|
|
490
|
-
finalContent.push(tc);
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
malformed.push(`${acc.name}(${acc.args.slice(0, 200)})`);
|
|
496
|
+
},
|
|
497
|
+
finalize() {
|
|
498
|
+
closeThinking();
|
|
499
|
+
if (emittedTextStart) {
|
|
500
|
+
out.push({ type: "text_end", contentIndex: textIndex, content: accumText, partial: { ...partial } });
|
|
494
501
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
if (
|
|
500
|
-
finalContent.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
502
|
+
const finalContent = [];
|
|
503
|
+
if (accumReasoning)
|
|
504
|
+
finalContent.push({ type: "thinking", thinking: accumReasoning });
|
|
505
|
+
const textFace = degenerate && degenFace === "text" && repCut !== undefined ? trimDegenerateTail(accumText, repCut) : accumText;
|
|
506
|
+
if (textFace)
|
|
507
|
+
finalContent.push({ type: "text", text: textFace });
|
|
508
|
+
const toolCalls = [];
|
|
509
|
+
const malformed = [];
|
|
510
|
+
for (const acc of [...toolAccum.entries()].sort((a, b) => a[0] - b[0]).map((e) => e[1])) {
|
|
511
|
+
if (!acc.name)
|
|
512
|
+
continue;
|
|
513
|
+
const tc = closeToolCallAccum(acc);
|
|
514
|
+
if (tc) {
|
|
507
515
|
toolCalls.push(tc);
|
|
516
|
+
finalContent.push(tc);
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
malformed.push(`${acc.name}(${acc.args.slice(0, 200)})`);
|
|
508
520
|
}
|
|
509
521
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
:
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
522
|
+
if (config.repairTextToolCalls && toolCalls.length === 0 && textFace && context.tools && context.tools.length > 0) {
|
|
523
|
+
const names = new Set(context.tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
524
|
+
const repaired = repairTextToolCalls(textFace, names);
|
|
525
|
+
if (repaired.toolCalls.length > 0) {
|
|
526
|
+
finalContent.length = 0;
|
|
527
|
+
if (accumReasoning)
|
|
528
|
+
finalContent.push({ type: "thinking", thinking: accumReasoning });
|
|
529
|
+
if (repaired.cleanedText)
|
|
530
|
+
finalContent.push({ type: "text", text: repaired.cleanedText });
|
|
531
|
+
for (const tc of repaired.toolCalls) {
|
|
532
|
+
finalContent.push(tc);
|
|
533
|
+
toolCalls.push(tc);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const toolError = malformed.length > 0
|
|
538
|
+
? `tool call argument(s) not valid JSON (likely truncated, finish_reason="${finishReason ?? "?"}"): ${malformed.join("; ")}`
|
|
539
|
+
: undefined;
|
|
540
|
+
const noUsableContent = toolCalls.length === 0 && !accumText.trim();
|
|
541
|
+
if (toolError !== undefined) {
|
|
542
|
+
finalContent.push({
|
|
543
|
+
type: "text",
|
|
544
|
+
text: `\n[note: ${malformed.length} tool call(s) were truncated (finish_reason="${finishReason ?? "?"}") and dropped — re-issue them next turn: ${malformed.join("; ")}]`,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
const truncatedEmpty = finishReason === "length" && noUsableContent && !toolError;
|
|
548
|
+
const dynamicCut = truncatedEmpty && options?.maxTokensDynamic === true;
|
|
549
|
+
const safetyCut = finishReason === "content_filter";
|
|
550
|
+
const refused = accumRefusal.trim().length > 0;
|
|
551
|
+
const emptyNoFinish = noUsableContent && finishReason == null && !degenerate && !walltimeCut;
|
|
552
|
+
const maxTokensNote = sentMaxTokens !== undefined
|
|
553
|
+
? `effective max_tokens=${sentMaxTokens} (from ${sentMaxTokensLane === "options" ? "options.maxTokens" : "model.maxTokens"})`
|
|
554
|
+
: "max_tokens not set (neither options.maxTokens nor model.maxTokens — the provider's default cap applied)";
|
|
555
|
+
const errorMessage = walltimeCut
|
|
556
|
+
? WALLTIME_CUTOFF_MESSAGE
|
|
557
|
+
: degenerate
|
|
558
|
+
? DEGENERATE_MESSAGE
|
|
559
|
+
: safetyCut
|
|
560
|
+
? `response cut by the provider content filter (finish_reason="content_filter") — the output was censored/truncated for policy and is unreliable`
|
|
561
|
+
: refused
|
|
562
|
+
? `response refused by the model for policy reasons: ${accumRefusal.trim()}`
|
|
563
|
+
: (toolError ??
|
|
564
|
+
(truncatedEmpty && !dynamicCut
|
|
565
|
+
? accumReasoning.trim()
|
|
566
|
+
? `response truncated at max_tokens (finish_reason="length", ${maxTokensNote}) with no answer text — the model spent the whole budget on reasoning (${accumReasoning.length} reasoning chars). Raise max_tokens, or disable thinking for this task.`
|
|
567
|
+
: `response truncated at max_tokens (finish_reason="length", ${maxTokensNote}) with no answer text — raise max_tokens`
|
|
568
|
+
: emptyNoFinish
|
|
569
|
+
? `model stream ended with no content and no finish_reason — the response was lost (a torn stream or an in-band provider error frame)`
|
|
570
|
+
: undefined));
|
|
571
|
+
const errored = walltimeCut ||
|
|
572
|
+
degenerate ||
|
|
573
|
+
safetyCut ||
|
|
574
|
+
refused ||
|
|
575
|
+
emptyNoFinish ||
|
|
576
|
+
(toolError !== undefined && noUsableContent) ||
|
|
577
|
+
(truncatedEmpty && !dynamicCut);
|
|
578
|
+
const errorKind = !walltimeCut && !degenerate && !safetyCut && !refused && toolError === undefined && truncatedEmpty && !dynamicCut
|
|
579
|
+
? "length_empty"
|
|
580
|
+
: undefined;
|
|
581
|
+
const finalMessage = {
|
|
582
|
+
...partial,
|
|
583
|
+
content: finalContent.length > 0 ? finalContent : [{ type: "text", text: "" }],
|
|
584
|
+
stopReason: errored ? "error" : mapFinishReason(finishReason, toolCalls.length > 0),
|
|
585
|
+
usage: computeUsage(model, usageRaw),
|
|
586
|
+
...(usageRaw === undefined ? { usageMissing: true } : {}),
|
|
587
|
+
timestamp: Date.now(),
|
|
588
|
+
...(errorMessage ? { errorMessage } : {}),
|
|
589
|
+
...(errorKind ? { errorKind } : {}),
|
|
590
|
+
...(repCut !== undefined || repSpared.size > 0
|
|
591
|
+
? { repetition: { ...(repCut !== undefined ? { cut: repCut } : {}), ...(repSpared.size > 0 ? { spared: [...repSpared.values()] } : {}) } }
|
|
592
|
+
: {}),
|
|
593
|
+
};
|
|
594
|
+
if (errored) {
|
|
595
|
+
out.push({ type: "error", reason: "error", error: finalMessage });
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
out.push({
|
|
599
|
+
type: "done",
|
|
600
|
+
reason: finalMessage.stopReason,
|
|
601
|
+
message: finalMessage,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
};
|
|
606
|
+
},
|
|
607
|
+
});
|
|
608
|
+
};
|
|
579
609
|
return { stream };
|
|
580
610
|
}
|
|
581
611
|
//# sourceMappingURL=openai.js.map
|