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