extrait 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/extract-parse-hint.d.ts +3 -0
- package/dist/extract-shape.d.ts +21 -0
- package/dist/generate-debug.d.ts +26 -0
- package/dist/generate-model-call.d.ts +3 -0
- package/dist/generate-output.d.ts +10 -0
- package/dist/generate-shared.d.ts +12 -10
- package/dist/generate-tool-timeout.d.ts +3 -0
- package/dist/index.cjs +1301 -1156
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1301 -1156
- package/dist/parse.d.ts +1 -1
- package/dist/providers/mcp-runtime-debug.d.ts +3 -0
- package/dist/providers/mcp-runtime.d.ts +2 -2
- package/dist/structured-streaming.d.ts +1 -0
- package/dist/structured.d.ts +3 -3
- package/dist/type-definitions/llm.d.ts +246 -0
- package/dist/type-definitions/markdown.d.ts +10 -0
- package/dist/type-definitions/parse.d.ts +93 -0
- package/dist/type-definitions/structured.d.ts +123 -0
- package/dist/types.d.ts +4 -442
- package/package.json +8 -14
package/dist/index.cjs
CHANGED
|
@@ -91,9 +91,6 @@ __export(exports_src, {
|
|
|
91
91
|
});
|
|
92
92
|
module.exports = __toCommonJS(exports_src);
|
|
93
93
|
|
|
94
|
-
// src/extract.ts
|
|
95
|
-
var import_jsonrepair = require("jsonrepair");
|
|
96
|
-
|
|
97
94
|
// src/utils/common.ts
|
|
98
95
|
function toErrorMessage(error) {
|
|
99
96
|
if (error instanceof Error) {
|
|
@@ -311,184 +308,77 @@ function isOnlyWhitespace(value) {
|
|
|
311
308
|
return true;
|
|
312
309
|
}
|
|
313
310
|
|
|
314
|
-
// src/extract.ts
|
|
315
|
-
var
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
firstPassMin: 12,
|
|
320
|
-
firstPassCap: 30,
|
|
321
|
-
firstPassMultiplier: 6,
|
|
322
|
-
secondPassMin: 4,
|
|
323
|
-
secondPassCap: 8,
|
|
324
|
-
secondPassMultiplier: 2,
|
|
325
|
-
hintMaxLength: 50000
|
|
326
|
-
};
|
|
327
|
-
function extractJsonCandidates(input, options = {}) {
|
|
328
|
-
const maxCandidates = options.maxCandidates ?? 5;
|
|
329
|
-
const acceptArrays = options.acceptArrays ?? true;
|
|
330
|
-
const allowRepairHints = options.allowRepairHints ?? true;
|
|
331
|
-
const heuristics = resolveExtractionHeuristics(options.heuristics);
|
|
332
|
-
const extractionInput = input;
|
|
333
|
-
const candidates = [];
|
|
334
|
-
candidates.push(...extractFromMarkdown(extractionInput, acceptArrays));
|
|
335
|
-
candidates.push(...scanBalancedSegments(extractionInput, acceptArrays));
|
|
336
|
-
if (candidates.length === 0 && extractionInput.trim()) {
|
|
337
|
-
const content = extractionInput.trim();
|
|
338
|
-
candidates.push({
|
|
339
|
-
id: "raw:fallback",
|
|
340
|
-
source: "raw",
|
|
341
|
-
content,
|
|
342
|
-
start: 0,
|
|
343
|
-
end: extractionInput.length,
|
|
344
|
-
score: 10 + Math.floor(lengthScore(content.length) / 3)
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
const prefiltered = prefilterByJsonShape(candidates, acceptArrays);
|
|
348
|
-
const firstPassLimit = clamp(maxCandidates * heuristics.firstPassMultiplier, heuristics.firstPassMin, heuristics.firstPassCap);
|
|
349
|
-
const firstPass = prefiltered.slice(0, firstPassLimit);
|
|
350
|
-
const secondPassLimit = Math.min(firstPass.length, clamp(maxCandidates * heuristics.secondPassMultiplier, heuristics.secondPassMin, heuristics.secondPassCap));
|
|
351
|
-
for (let i = 0;i < secondPassLimit; i += 1) {
|
|
352
|
-
const candidate = firstPass[i];
|
|
353
|
-
if (!candidate) {
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
const parseHint = buildParseHint(candidate.content, allowRepairHints, heuristics.hintMaxLength);
|
|
357
|
-
if (!parseHint) {
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
candidate.parseHint = parseHint;
|
|
361
|
-
candidate.score += parseHintBonus(parseHint);
|
|
311
|
+
// src/extract-parse-hint.ts
|
|
312
|
+
var import_jsonrepair = require("jsonrepair");
|
|
313
|
+
function buildParseHint(content, allowRepair, hintMaxLength) {
|
|
314
|
+
if (content.length > hintMaxLength) {
|
|
315
|
+
return null;
|
|
362
316
|
}
|
|
363
|
-
|
|
364
|
-
const deduped = dedupeCandidates(sorted);
|
|
365
|
-
return deduped.slice(0, maxCandidates).map((candidate, index) => ({
|
|
366
|
-
id: `${candidate.source}:${index}`,
|
|
367
|
-
source: candidate.source,
|
|
368
|
-
content: candidate.content,
|
|
369
|
-
language: candidate.language,
|
|
370
|
-
parseHint: candidate.parseHint,
|
|
371
|
-
start: candidate.start,
|
|
372
|
-
end: candidate.end,
|
|
373
|
-
score: candidate.score
|
|
374
|
-
}));
|
|
375
|
-
}
|
|
376
|
-
function prefilterByJsonShape(candidates, acceptArrays) {
|
|
377
|
-
const shaped = candidates.map((candidate) => {
|
|
378
|
-
const shapeScore = jsonShapeScore(candidate.content, acceptArrays);
|
|
317
|
+
try {
|
|
379
318
|
return {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
319
|
+
success: true,
|
|
320
|
+
parsed: JSON.parse(content),
|
|
321
|
+
repaired: null,
|
|
322
|
+
usedRepair: false,
|
|
323
|
+
stage: "parse",
|
|
324
|
+
error: ""
|
|
383
325
|
};
|
|
384
|
-
})
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
326
|
+
} catch (directError) {
|
|
327
|
+
if (!allowRepair) {
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
parsed: null,
|
|
331
|
+
repaired: null,
|
|
332
|
+
usedRepair: false,
|
|
333
|
+
stage: "parse",
|
|
334
|
+
error: toErrorMessage(directError)
|
|
335
|
+
};
|
|
392
336
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
337
|
+
let repaired;
|
|
338
|
+
try {
|
|
339
|
+
repaired = import_jsonrepair.jsonrepair(content);
|
|
340
|
+
} catch (repairError) {
|
|
341
|
+
return {
|
|
342
|
+
success: false,
|
|
343
|
+
parsed: null,
|
|
344
|
+
repaired: null,
|
|
345
|
+
usedRepair: true,
|
|
346
|
+
stage: "repair",
|
|
347
|
+
error: toErrorMessage(repairError)
|
|
348
|
+
};
|
|
404
349
|
}
|
|
405
|
-
|
|
406
|
-
return
|
|
350
|
+
try {
|
|
351
|
+
return {
|
|
352
|
+
success: true,
|
|
353
|
+
parsed: JSON.parse(repaired),
|
|
354
|
+
repaired,
|
|
355
|
+
usedRepair: true,
|
|
356
|
+
stage: "parse",
|
|
357
|
+
error: ""
|
|
358
|
+
};
|
|
359
|
+
} catch (parseError) {
|
|
360
|
+
return {
|
|
361
|
+
success: false,
|
|
362
|
+
parsed: null,
|
|
363
|
+
repaired,
|
|
364
|
+
usedRepair: true,
|
|
365
|
+
stage: "parse",
|
|
366
|
+
error: toErrorMessage(parseError || directError)
|
|
367
|
+
};
|
|
407
368
|
}
|
|
408
|
-
|
|
409
|
-
return [
|
|
410
|
-
{
|
|
411
|
-
id: `fenced:${index}`,
|
|
412
|
-
source: "fenced",
|
|
413
|
-
language,
|
|
414
|
-
content,
|
|
415
|
-
start: block.start,
|
|
416
|
-
end: block.end,
|
|
417
|
-
score: 260 + langBonus + lengthScore(content.length)
|
|
418
|
-
}
|
|
419
|
-
];
|
|
420
|
-
});
|
|
369
|
+
}
|
|
421
370
|
}
|
|
422
|
-
function
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
let inString = false;
|
|
426
|
-
let quote = null;
|
|
427
|
-
let escaped = false;
|
|
428
|
-
for (let i = 0;i < input.length; i += 1) {
|
|
429
|
-
const char = input[i];
|
|
430
|
-
if (!char) {
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
if (inString) {
|
|
434
|
-
if (escaped) {
|
|
435
|
-
escaped = false;
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
if (char === "\\") {
|
|
439
|
-
escaped = true;
|
|
440
|
-
continue;
|
|
441
|
-
}
|
|
442
|
-
if (char === quote) {
|
|
443
|
-
inString = false;
|
|
444
|
-
quote = null;
|
|
445
|
-
}
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
const allowSingleQuoted = stack.length > 0;
|
|
449
|
-
if (char === '"' || allowSingleQuoted && (char === "'" || char === "`")) {
|
|
450
|
-
inString = true;
|
|
451
|
-
quote = char;
|
|
452
|
-
continue;
|
|
453
|
-
}
|
|
454
|
-
if (char === "{" || char === "[") {
|
|
455
|
-
stack.push({ char, index: i });
|
|
456
|
-
continue;
|
|
457
|
-
}
|
|
458
|
-
if (char !== "}" && char !== "]") {
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
const expectedOpen = char === "}" ? "{" : "[";
|
|
462
|
-
while (stack.length > 0 && stack[stack.length - 1]?.char !== expectedOpen) {
|
|
463
|
-
stack.pop();
|
|
464
|
-
}
|
|
465
|
-
const opened = stack.pop();
|
|
466
|
-
if (!opened) {
|
|
467
|
-
continue;
|
|
468
|
-
}
|
|
469
|
-
if (stack.length > 0) {
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
if (!acceptArrays && opened.char === "[") {
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
const content = input.slice(opened.index, i + 1).trim();
|
|
476
|
-
if (!content) {
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
const rootBonus = opened.char === "{" ? 40 : 20;
|
|
480
|
-
const boundaryBonus = boundaryScore(input, opened.index, i + 1);
|
|
481
|
-
results.push({
|
|
482
|
-
id: `scan:${results.length}`,
|
|
483
|
-
source: "scan",
|
|
484
|
-
content,
|
|
485
|
-
start: opened.index,
|
|
486
|
-
end: i + 1,
|
|
487
|
-
score: 120 + rootBonus + boundaryBonus + lengthScore(content.length)
|
|
488
|
-
});
|
|
371
|
+
function parseHintBonus(hint) {
|
|
372
|
+
if (hint.success) {
|
|
373
|
+
return hint.usedRepair ? 70 : 120;
|
|
489
374
|
}
|
|
490
|
-
return
|
|
375
|
+
return hint.usedRepair ? -20 : -10;
|
|
491
376
|
}
|
|
377
|
+
|
|
378
|
+
// src/extract-shape.ts
|
|
379
|
+
var RE_EMPTY_OBJECT = /^\{\s*\}$/;
|
|
380
|
+
var RE_EMPTY_ARRAY = /^\[\s*\]$/;
|
|
381
|
+
var RE_BOUNDARY_CHAR = /[\s,.;:!?`"'()[\]{}<>]/;
|
|
492
382
|
function looksLikeJsonEnvelope(content, acceptArrays) {
|
|
493
383
|
const trimmed = content.trim();
|
|
494
384
|
if (trimmed.startsWith("{")) {
|
|
@@ -597,95 +487,6 @@ function passesShapeFilter(candidate) {
|
|
|
597
487
|
}
|
|
598
488
|
return candidate.shapeScore >= 35;
|
|
599
489
|
}
|
|
600
|
-
function buildParseHint(content, allowRepair, hintMaxLength) {
|
|
601
|
-
if (content.length > hintMaxLength) {
|
|
602
|
-
return null;
|
|
603
|
-
}
|
|
604
|
-
try {
|
|
605
|
-
return {
|
|
606
|
-
success: true,
|
|
607
|
-
parsed: JSON.parse(content),
|
|
608
|
-
repaired: null,
|
|
609
|
-
usedRepair: false,
|
|
610
|
-
stage: "parse",
|
|
611
|
-
error: ""
|
|
612
|
-
};
|
|
613
|
-
} catch (directError) {
|
|
614
|
-
if (!allowRepair) {
|
|
615
|
-
return {
|
|
616
|
-
success: false,
|
|
617
|
-
parsed: null,
|
|
618
|
-
repaired: null,
|
|
619
|
-
usedRepair: false,
|
|
620
|
-
stage: "parse",
|
|
621
|
-
error: toErrorMessage(directError)
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
let repaired;
|
|
625
|
-
try {
|
|
626
|
-
repaired = import_jsonrepair.jsonrepair(content);
|
|
627
|
-
} catch (repairError) {
|
|
628
|
-
return {
|
|
629
|
-
success: false,
|
|
630
|
-
parsed: null,
|
|
631
|
-
repaired: null,
|
|
632
|
-
usedRepair: true,
|
|
633
|
-
stage: "repair",
|
|
634
|
-
error: toErrorMessage(repairError)
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
try {
|
|
638
|
-
return {
|
|
639
|
-
success: true,
|
|
640
|
-
parsed: JSON.parse(repaired),
|
|
641
|
-
repaired,
|
|
642
|
-
usedRepair: true,
|
|
643
|
-
stage: "parse",
|
|
644
|
-
error: ""
|
|
645
|
-
};
|
|
646
|
-
} catch (parseError) {
|
|
647
|
-
return {
|
|
648
|
-
success: false,
|
|
649
|
-
parsed: null,
|
|
650
|
-
repaired,
|
|
651
|
-
usedRepair: true,
|
|
652
|
-
stage: "parse",
|
|
653
|
-
error: toErrorMessage(parseError || directError)
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
function resolveExtractionHeuristics(input) {
|
|
659
|
-
const merged = {
|
|
660
|
-
...DEFAULT_EXTRACTION_HEURISTICS,
|
|
661
|
-
...input
|
|
662
|
-
};
|
|
663
|
-
const firstPassMin = normalizeInteger(merged.firstPassMin, DEFAULT_EXTRACTION_HEURISTICS.firstPassMin);
|
|
664
|
-
const firstPassCap = Math.max(firstPassMin, normalizeInteger(merged.firstPassCap, DEFAULT_EXTRACTION_HEURISTICS.firstPassCap));
|
|
665
|
-
const secondPassMin = normalizeInteger(merged.secondPassMin, DEFAULT_EXTRACTION_HEURISTICS.secondPassMin);
|
|
666
|
-
const secondPassCap = Math.max(secondPassMin, normalizeInteger(merged.secondPassCap, DEFAULT_EXTRACTION_HEURISTICS.secondPassCap));
|
|
667
|
-
return {
|
|
668
|
-
firstPassMin,
|
|
669
|
-
firstPassCap,
|
|
670
|
-
firstPassMultiplier: normalizeInteger(merged.firstPassMultiplier, DEFAULT_EXTRACTION_HEURISTICS.firstPassMultiplier),
|
|
671
|
-
secondPassMin,
|
|
672
|
-
secondPassCap,
|
|
673
|
-
secondPassMultiplier: normalizeInteger(merged.secondPassMultiplier, DEFAULT_EXTRACTION_HEURISTICS.secondPassMultiplier),
|
|
674
|
-
hintMaxLength: normalizeInteger(merged.hintMaxLength, DEFAULT_EXTRACTION_HEURISTICS.hintMaxLength)
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
function normalizeInteger(value, fallback) {
|
|
678
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
679
|
-
return fallback;
|
|
680
|
-
}
|
|
681
|
-
return Math.max(0, Math.floor(value));
|
|
682
|
-
}
|
|
683
|
-
function parseHintBonus(hint) {
|
|
684
|
-
if (hint.success) {
|
|
685
|
-
return hint.usedRepair ? 70 : 120;
|
|
686
|
-
}
|
|
687
|
-
return hint.usedRepair ? -20 : -10;
|
|
688
|
-
}
|
|
689
490
|
function hasBalancedJsonDelimiters(input) {
|
|
690
491
|
const stack = [];
|
|
691
492
|
let inString = false;
|
|
@@ -771,6 +572,207 @@ function dedupeCandidates(candidates) {
|
|
|
771
572
|
function clamp(value, min, max) {
|
|
772
573
|
return Math.max(min, Math.min(max, Math.floor(value)));
|
|
773
574
|
}
|
|
575
|
+
function normalizeInteger(value, fallback) {
|
|
576
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
577
|
+
return fallback;
|
|
578
|
+
}
|
|
579
|
+
return Math.max(0, Math.floor(value));
|
|
580
|
+
}
|
|
581
|
+
function resolveExtractionHeuristics(input, defaults) {
|
|
582
|
+
const merged = {
|
|
583
|
+
...defaults,
|
|
584
|
+
...input
|
|
585
|
+
};
|
|
586
|
+
const firstPassMin = normalizeInteger(merged.firstPassMin, defaults.firstPassMin);
|
|
587
|
+
const firstPassCap = Math.max(firstPassMin, normalizeInteger(merged.firstPassCap, defaults.firstPassCap));
|
|
588
|
+
const secondPassMin = normalizeInteger(merged.secondPassMin, defaults.secondPassMin);
|
|
589
|
+
const secondPassCap = Math.max(secondPassMin, normalizeInteger(merged.secondPassCap, defaults.secondPassCap));
|
|
590
|
+
return {
|
|
591
|
+
firstPassMin,
|
|
592
|
+
firstPassCap,
|
|
593
|
+
firstPassMultiplier: normalizeInteger(merged.firstPassMultiplier, defaults.firstPassMultiplier),
|
|
594
|
+
secondPassMin,
|
|
595
|
+
secondPassCap,
|
|
596
|
+
secondPassMultiplier: normalizeInteger(merged.secondPassMultiplier, defaults.secondPassMultiplier),
|
|
597
|
+
hintMaxLength: normalizeInteger(merged.hintMaxLength, defaults.hintMaxLength)
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/extract.ts
|
|
602
|
+
var DEFAULT_EXTRACTION_HEURISTICS = {
|
|
603
|
+
firstPassMin: 12,
|
|
604
|
+
firstPassCap: 30,
|
|
605
|
+
firstPassMultiplier: 6,
|
|
606
|
+
secondPassMin: 4,
|
|
607
|
+
secondPassCap: 8,
|
|
608
|
+
secondPassMultiplier: 2,
|
|
609
|
+
hintMaxLength: 50000
|
|
610
|
+
};
|
|
611
|
+
function extractJsonCandidates(input, options = {}) {
|
|
612
|
+
const maxCandidates = options.maxCandidates ?? 5;
|
|
613
|
+
const acceptArrays = options.acceptArrays ?? true;
|
|
614
|
+
const allowRepairHints = options.allowRepairHints ?? true;
|
|
615
|
+
const heuristics = resolveExtractionHeuristics(options.heuristics, DEFAULT_EXTRACTION_HEURISTICS);
|
|
616
|
+
const extractionInput = input;
|
|
617
|
+
const candidates = [];
|
|
618
|
+
candidates.push(...extractFromMarkdown(extractionInput, acceptArrays));
|
|
619
|
+
candidates.push(...scanBalancedSegments(extractionInput, acceptArrays));
|
|
620
|
+
if (candidates.length === 0 && extractionInput.trim()) {
|
|
621
|
+
const content = extractionInput.trim();
|
|
622
|
+
candidates.push({
|
|
623
|
+
id: "raw:fallback",
|
|
624
|
+
source: "raw",
|
|
625
|
+
content,
|
|
626
|
+
start: 0,
|
|
627
|
+
end: extractionInput.length,
|
|
628
|
+
score: 10 + Math.floor(lengthScore(content.length) / 3)
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
const prefiltered = prefilterByJsonShape(candidates, acceptArrays);
|
|
632
|
+
const firstPassLimit = clamp(maxCandidates * heuristics.firstPassMultiplier, heuristics.firstPassMin, heuristics.firstPassCap);
|
|
633
|
+
const firstPass = prefiltered.slice(0, firstPassLimit);
|
|
634
|
+
const secondPassLimit = Math.min(firstPass.length, clamp(maxCandidates * heuristics.secondPassMultiplier, heuristics.secondPassMin, heuristics.secondPassCap));
|
|
635
|
+
for (let i = 0;i < secondPassLimit; i += 1) {
|
|
636
|
+
const candidate = firstPass[i];
|
|
637
|
+
if (!candidate) {
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
const parseHint = buildParseHint(candidate.content, allowRepairHints, heuristics.hintMaxLength);
|
|
641
|
+
if (!parseHint) {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
candidate.parseHint = parseHint;
|
|
645
|
+
candidate.score += parseHintBonus(parseHint);
|
|
646
|
+
}
|
|
647
|
+
const sorted = sortCandidates(firstPass);
|
|
648
|
+
const deduped = dedupeCandidates(sorted);
|
|
649
|
+
return deduped.slice(0, maxCandidates).map((candidate, index) => ({
|
|
650
|
+
id: `${candidate.source}:${index}`,
|
|
651
|
+
source: candidate.source,
|
|
652
|
+
content: candidate.content,
|
|
653
|
+
language: candidate.language,
|
|
654
|
+
parseHint: candidate.parseHint,
|
|
655
|
+
start: candidate.start,
|
|
656
|
+
end: candidate.end,
|
|
657
|
+
score: candidate.score
|
|
658
|
+
}));
|
|
659
|
+
}
|
|
660
|
+
function prefilterByJsonShape(candidates, acceptArrays) {
|
|
661
|
+
const shaped = candidates.map((candidate) => {
|
|
662
|
+
const shapeScore = jsonShapeScore(candidate.content, acceptArrays);
|
|
663
|
+
return {
|
|
664
|
+
...candidate,
|
|
665
|
+
shapeScore,
|
|
666
|
+
score: candidate.score + shapeScore
|
|
667
|
+
};
|
|
668
|
+
});
|
|
669
|
+
const sorted = sortCandidates(shaped);
|
|
670
|
+
const deduped = dedupeCandidates(sorted);
|
|
671
|
+
const filtered = deduped.filter((candidate) => passesShapeFilter(candidate));
|
|
672
|
+
if (filtered.length > 0) {
|
|
673
|
+
const fallback = deduped.find((candidate) => !passesShapeFilter(candidate));
|
|
674
|
+
if (fallback) {
|
|
675
|
+
filtered.push(fallback);
|
|
676
|
+
}
|
|
677
|
+
return sortCandidates(filtered);
|
|
678
|
+
}
|
|
679
|
+
return deduped.slice(0, Math.min(1, deduped.length));
|
|
680
|
+
}
|
|
681
|
+
function extractFromMarkdown(input, acceptArrays) {
|
|
682
|
+
const blocks = extractMarkdownCodeBlocks(input);
|
|
683
|
+
return blocks.flatMap((block, index) => {
|
|
684
|
+
const language = block.language || null;
|
|
685
|
+
const content = block.code.trim();
|
|
686
|
+
if (!content) {
|
|
687
|
+
return [];
|
|
688
|
+
}
|
|
689
|
+
if (!looksLikeJsonEnvelope(content, acceptArrays)) {
|
|
690
|
+
return [];
|
|
691
|
+
}
|
|
692
|
+
const langBonus = languageBonus(language);
|
|
693
|
+
return [
|
|
694
|
+
{
|
|
695
|
+
id: `fenced:${index}`,
|
|
696
|
+
source: "fenced",
|
|
697
|
+
language,
|
|
698
|
+
content,
|
|
699
|
+
start: block.start,
|
|
700
|
+
end: block.end,
|
|
701
|
+
score: 260 + langBonus + lengthScore(content.length)
|
|
702
|
+
}
|
|
703
|
+
];
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
function scanBalancedSegments(input, acceptArrays) {
|
|
707
|
+
const results = [];
|
|
708
|
+
const stack = [];
|
|
709
|
+
let inString = false;
|
|
710
|
+
let quote = null;
|
|
711
|
+
let escaped = false;
|
|
712
|
+
for (let i = 0;i < input.length; i += 1) {
|
|
713
|
+
const char = input[i];
|
|
714
|
+
if (!char) {
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
if (inString) {
|
|
718
|
+
if (escaped) {
|
|
719
|
+
escaped = false;
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
if (char === "\\") {
|
|
723
|
+
escaped = true;
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
if (char === quote) {
|
|
727
|
+
inString = false;
|
|
728
|
+
quote = null;
|
|
729
|
+
}
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
const allowSingleQuoted = stack.length > 0;
|
|
733
|
+
if (char === '"' || allowSingleQuoted && (char === "'" || char === "`")) {
|
|
734
|
+
inString = true;
|
|
735
|
+
quote = char;
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
if (char === "{" || char === "[") {
|
|
739
|
+
stack.push({ char, index: i });
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (char !== "}" && char !== "]") {
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
const expectedOpen = char === "}" ? "{" : "[";
|
|
746
|
+
while (stack.length > 0 && stack[stack.length - 1]?.char !== expectedOpen) {
|
|
747
|
+
stack.pop();
|
|
748
|
+
}
|
|
749
|
+
const opened = stack.pop();
|
|
750
|
+
if (!opened) {
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
if (stack.length > 0) {
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (!acceptArrays && opened.char === "[") {
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
const content = input.slice(opened.index, i + 1).trim();
|
|
760
|
+
if (!content) {
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const rootBonus = opened.char === "{" ? 40 : 20;
|
|
764
|
+
const boundaryBonus = boundaryScore(input, opened.index, i + 1);
|
|
765
|
+
results.push({
|
|
766
|
+
id: `scan:${results.length}`,
|
|
767
|
+
source: "scan",
|
|
768
|
+
content,
|
|
769
|
+
start: opened.index,
|
|
770
|
+
end: i + 1,
|
|
771
|
+
score: 120 + rootBonus + boundaryBonus + lengthScore(content.length)
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
return results;
|
|
775
|
+
}
|
|
774
776
|
// src/schema.ts
|
|
775
777
|
var RE_SIMPLE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
776
778
|
var RE_WHITESPACE = /\s+/g;
|
|
@@ -790,36 +792,36 @@ function unwrap(schema) {
|
|
|
790
792
|
let optional = false;
|
|
791
793
|
let nullable = false;
|
|
792
794
|
while (true) {
|
|
793
|
-
const typeName = current?.
|
|
795
|
+
const typeName = current?.def?.type;
|
|
794
796
|
if (!typeName) {
|
|
795
797
|
break;
|
|
796
798
|
}
|
|
797
799
|
if (typeName === "optional") {
|
|
798
800
|
optional = true;
|
|
799
|
-
current = current.
|
|
801
|
+
current = current.def?.innerType ?? current;
|
|
800
802
|
continue;
|
|
801
803
|
}
|
|
802
804
|
if (typeName === "default") {
|
|
803
805
|
optional = true;
|
|
804
|
-
current = current.
|
|
806
|
+
current = current.def?.innerType ?? current;
|
|
805
807
|
continue;
|
|
806
808
|
}
|
|
807
809
|
if (typeName === "nullable") {
|
|
808
810
|
nullable = true;
|
|
809
|
-
current = current.
|
|
811
|
+
current = current.def?.innerType ?? current;
|
|
810
812
|
continue;
|
|
811
813
|
}
|
|
812
814
|
if (typeName === "pipe") {
|
|
813
|
-
const outType = current.
|
|
815
|
+
const outType = current.def?.out?.def?.type;
|
|
814
816
|
if (outType === "transform") {
|
|
815
|
-
current = current.
|
|
817
|
+
current = current.def?.in ?? current;
|
|
816
818
|
} else {
|
|
817
|
-
current = current.
|
|
819
|
+
current = current.def?.out ?? current;
|
|
818
820
|
}
|
|
819
821
|
continue;
|
|
820
822
|
}
|
|
821
823
|
if (typeName === "catch" || typeName === "readonly") {
|
|
822
|
-
current = current.
|
|
824
|
+
current = current.def?.innerType ?? current;
|
|
823
825
|
continue;
|
|
824
826
|
}
|
|
825
827
|
break;
|
|
@@ -835,7 +837,7 @@ function formatCore(schema, depth, seen) {
|
|
|
835
837
|
return "unknown";
|
|
836
838
|
}
|
|
837
839
|
seen.add(schema);
|
|
838
|
-
const typeName = schema?.
|
|
840
|
+
const typeName = schema?.def?.type;
|
|
839
841
|
switch (typeName) {
|
|
840
842
|
case "string":
|
|
841
843
|
return "string";
|
|
@@ -860,44 +862,44 @@ function formatCore(schema, depth, seen) {
|
|
|
860
862
|
case "void":
|
|
861
863
|
return "void";
|
|
862
864
|
case "literal": {
|
|
863
|
-
const value = schema.
|
|
865
|
+
const value = schema.def?.values?.[0];
|
|
864
866
|
return JSON.stringify(value);
|
|
865
867
|
}
|
|
866
868
|
case "enum": {
|
|
867
|
-
const entries = schema.
|
|
869
|
+
const entries = schema.def?.entries;
|
|
868
870
|
const values = Object.values(entries ?? {});
|
|
869
871
|
const unique = [...new Set(values.filter((v) => typeof v !== "string" || Number.isNaN(Number(v))))];
|
|
870
872
|
return unique.map((v) => JSON.stringify(v)).join(" | ") || "string";
|
|
871
873
|
}
|
|
872
874
|
case "array": {
|
|
873
|
-
const inner = formatType(schema.
|
|
875
|
+
const inner = formatType(schema.def?.element ?? schema, depth, seen);
|
|
874
876
|
return requiresParentheses(inner) ? `(${inner})[]` : `${inner}[]`;
|
|
875
877
|
}
|
|
876
878
|
case "tuple": {
|
|
877
|
-
const items = (schema.
|
|
879
|
+
const items = (schema.def?.items ?? []).map((item) => formatType(item, depth, seen));
|
|
878
880
|
return `[${items.join(", ")}]`;
|
|
879
881
|
}
|
|
880
882
|
case "union": {
|
|
881
|
-
const options = (schema.
|
|
883
|
+
const options = (schema.def?.options ?? []).map((option) => formatType(option, depth, seen));
|
|
882
884
|
return options.join(" | ") || "unknown";
|
|
883
885
|
}
|
|
884
886
|
case "intersection": {
|
|
885
|
-
const left = formatType(schema.
|
|
886
|
-
const right = formatType(schema.
|
|
887
|
+
const left = formatType(schema.def?.left ?? schema, depth, seen);
|
|
888
|
+
const right = formatType(schema.def?.right ?? schema, depth, seen);
|
|
887
889
|
return `${left} & ${right}`;
|
|
888
890
|
}
|
|
889
891
|
case "record": {
|
|
890
|
-
const keyType = formatType(schema.
|
|
891
|
-
const valueType = formatType(schema.
|
|
892
|
+
const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
|
|
893
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
892
894
|
return `Record<${keyType}, ${valueType}>`;
|
|
893
895
|
}
|
|
894
896
|
case "map": {
|
|
895
|
-
const keyType = formatType(schema.
|
|
896
|
-
const valueType = formatType(schema.
|
|
897
|
+
const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
|
|
898
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
897
899
|
return `Map<${keyType}, ${valueType}>`;
|
|
898
900
|
}
|
|
899
901
|
case "set": {
|
|
900
|
-
const valueType = formatType(schema.
|
|
902
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
901
903
|
return `Set<${valueType}>`;
|
|
902
904
|
}
|
|
903
905
|
case "object":
|
|
@@ -911,7 +913,7 @@ function formatCore(schema, depth, seen) {
|
|
|
911
913
|
function formatObject(schema, depth, seen) {
|
|
912
914
|
const indent = " ".repeat(depth);
|
|
913
915
|
const innerIndent = " ".repeat(depth + 1);
|
|
914
|
-
const rawShape = schema.
|
|
916
|
+
const rawShape = schema.def?.shape;
|
|
915
917
|
const shape = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
|
|
916
918
|
const entries = Object.entries(shape);
|
|
917
919
|
if (entries.length === 0) {
|
|
@@ -937,27 +939,27 @@ function requiresParentheses(typeText) {
|
|
|
937
939
|
return typeText.includes(" | ") || typeText.includes(" & ");
|
|
938
940
|
}
|
|
939
941
|
function isIntegerNumber(schema) {
|
|
940
|
-
const checks = schema.
|
|
942
|
+
const checks = schema.def?.checks ?? [];
|
|
941
943
|
return checks.some((check) => check.isInt === true);
|
|
942
944
|
}
|
|
943
945
|
function readSchemaDescription(schema) {
|
|
944
946
|
let current = schema;
|
|
945
|
-
while (current?.
|
|
947
|
+
while (current?.def?.type) {
|
|
946
948
|
const desc = current.description;
|
|
947
949
|
if (typeof desc === "string" && desc.trim().length > 0) {
|
|
948
950
|
return sanitizeDescription(desc);
|
|
949
951
|
}
|
|
950
|
-
const typeName = current.
|
|
952
|
+
const typeName = current.def.type;
|
|
951
953
|
if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
|
|
952
|
-
current = current.
|
|
954
|
+
current = current.def.innerType ?? current;
|
|
953
955
|
continue;
|
|
954
956
|
}
|
|
955
957
|
if (typeName === "pipe") {
|
|
956
|
-
current = current.
|
|
958
|
+
current = current.def.in ?? current;
|
|
957
959
|
continue;
|
|
958
960
|
}
|
|
959
961
|
if (typeName === "catch" || typeName === "readonly") {
|
|
960
|
-
current = current.
|
|
962
|
+
current = current.def.innerType ?? current;
|
|
961
963
|
continue;
|
|
962
964
|
}
|
|
963
965
|
break;
|
|
@@ -1219,18 +1221,108 @@ function findSSEBoundary(buffer) {
|
|
|
1219
1221
|
return Math.max(crlfIndex, lfIndex);
|
|
1220
1222
|
}
|
|
1221
1223
|
|
|
1222
|
-
// src/providers/mcp-runtime.ts
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1224
|
+
// src/providers/mcp-runtime-debug.ts
|
|
1225
|
+
function formatToolExecutionDebugLine(execution) {
|
|
1226
|
+
const status = execution.error ? "error" : "ok";
|
|
1227
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1228
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1229
|
+
const duration = typeof execution.durationMs === "number" ? ` ${execution.durationMs}ms` : "";
|
|
1230
|
+
const base = `[tool:mcp:${status}] ${scope} ${toolRef}#${execution.callId}${duration}`;
|
|
1231
|
+
if (execution.error) {
|
|
1232
|
+
return `${base} -> ${execution.error}`;
|
|
1233
|
+
}
|
|
1234
|
+
return base;
|
|
1235
|
+
}
|
|
1236
|
+
function emitToolExecution(request, execution) {
|
|
1237
|
+
request.onToolExecution?.(execution);
|
|
1238
|
+
const debug = resolveToolDebugOptions(request.toolDebug);
|
|
1239
|
+
if (!debug.enabled) {
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
debug.logger(formatToolExecutionDebugLine(execution));
|
|
1243
|
+
if (debug.includeRequest) {
|
|
1244
|
+
debug.logger(formatToolExecutionRequestDebugLine(execution, debug));
|
|
1245
|
+
}
|
|
1246
|
+
if (debug.includeResult && (!execution.error || debug.includeResultOnError)) {
|
|
1247
|
+
debug.logger(formatToolExecutionResultDebugLine(execution, debug));
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
function resolveToolDebugOptions(value) {
|
|
1251
|
+
if (value === true) {
|
|
1252
|
+
return {
|
|
1253
|
+
enabled: true,
|
|
1254
|
+
logger: defaultToolDebugLogger,
|
|
1255
|
+
includeRequest: true,
|
|
1256
|
+
includeResult: true,
|
|
1257
|
+
includeResultOnError: true,
|
|
1258
|
+
pretty: false
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
if (value === undefined || value === false) {
|
|
1262
|
+
return {
|
|
1263
|
+
enabled: false,
|
|
1264
|
+
logger: () => {
|
|
1265
|
+
return;
|
|
1266
|
+
},
|
|
1267
|
+
includeRequest: false,
|
|
1268
|
+
includeResult: false,
|
|
1269
|
+
includeResultOnError: false,
|
|
1270
|
+
pretty: false
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
return {
|
|
1274
|
+
enabled: value.enabled ?? true,
|
|
1275
|
+
logger: value.logger ?? defaultToolDebugLogger,
|
|
1276
|
+
includeRequest: value.includeRequest ?? true,
|
|
1277
|
+
includeResult: value.includeResult ?? true,
|
|
1278
|
+
includeResultOnError: value.includeResultOnError ?? true,
|
|
1279
|
+
pretty: value.pretty ?? false
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
function defaultToolDebugLogger(line) {
|
|
1283
|
+
const { log } = globalThis.console;
|
|
1284
|
+
log(line);
|
|
1285
|
+
}
|
|
1286
|
+
function formatToolExecutionRequestDebugLine(execution, debug) {
|
|
1287
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1288
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1289
|
+
const payload = formatDebugPayload(execution.arguments, debug.pretty);
|
|
1290
|
+
return `[tool:mcp:request] ${scope} ${toolRef}#${execution.callId} arguments=${payload}`;
|
|
1291
|
+
}
|
|
1292
|
+
function formatToolExecutionResultDebugLine(execution, debug) {
|
|
1293
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1294
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1295
|
+
if (execution.error) {
|
|
1296
|
+
const payload2 = formatDebugPayload({ error: execution.error }, debug.pretty);
|
|
1297
|
+
return `[tool:mcp:result:error] ${scope} ${toolRef}#${execution.callId} output=${payload2}`;
|
|
1298
|
+
}
|
|
1299
|
+
const payload = formatDebugPayload(execution.output, debug.pretty);
|
|
1300
|
+
return `[tool:mcp:result:ok] ${scope} ${toolRef}#${execution.callId} output=${payload}`;
|
|
1301
|
+
}
|
|
1302
|
+
function formatDebugPayload(value, pretty) {
|
|
1303
|
+
if (value === undefined) {
|
|
1304
|
+
return "undefined";
|
|
1305
|
+
}
|
|
1306
|
+
try {
|
|
1307
|
+
const serialized = JSON.stringify(value, null, pretty ? 2 : 0);
|
|
1308
|
+
return serialized ?? "undefined";
|
|
1309
|
+
} catch {
|
|
1310
|
+
return String(value);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// src/providers/mcp-runtime.ts
|
|
1315
|
+
var DEFAULT_MAX_TOOL_ROUNDS = 100;
|
|
1316
|
+
async function resolveMCPToolset(clients) {
|
|
1317
|
+
if (!Array.isArray(clients) || clients.length === 0) {
|
|
1318
|
+
return {
|
|
1319
|
+
tools: [],
|
|
1320
|
+
byName: new Map
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
const listed = [];
|
|
1324
|
+
for (const client of clients) {
|
|
1325
|
+
let cursor;
|
|
1234
1326
|
do {
|
|
1235
1327
|
const page = await client.listTools(cursor ? { cursor } : undefined);
|
|
1236
1328
|
for (const tool of page.tools ?? []) {
|
|
@@ -1411,90 +1503,6 @@ function stringifyToolOutput(value) {
|
|
|
1411
1503
|
}
|
|
1412
1504
|
return JSON.stringify(value ?? null);
|
|
1413
1505
|
}
|
|
1414
|
-
function formatToolExecutionDebugLine(execution) {
|
|
1415
|
-
const status = execution.error ? "error" : "ok";
|
|
1416
|
-
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1417
|
-
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1418
|
-
const duration = typeof execution.durationMs === "number" ? ` ${execution.durationMs}ms` : "";
|
|
1419
|
-
const base = `[tool:mcp:${status}] ${scope} ${toolRef}#${execution.callId}${duration}`;
|
|
1420
|
-
if (execution.error) {
|
|
1421
|
-
return `${base} -> ${execution.error}`;
|
|
1422
|
-
}
|
|
1423
|
-
return base;
|
|
1424
|
-
}
|
|
1425
|
-
function emitToolExecution(request, execution) {
|
|
1426
|
-
request.onToolExecution?.(execution);
|
|
1427
|
-
const debug = resolveToolDebugOptions(request.toolDebug);
|
|
1428
|
-
if (!debug.enabled) {
|
|
1429
|
-
return;
|
|
1430
|
-
}
|
|
1431
|
-
debug.logger(formatToolExecutionDebugLine(execution));
|
|
1432
|
-
if (debug.includeRequest) {
|
|
1433
|
-
debug.logger(formatToolExecutionRequestDebugLine(execution, debug));
|
|
1434
|
-
}
|
|
1435
|
-
if (debug.includeResult && (!execution.error || debug.includeResultOnError)) {
|
|
1436
|
-
debug.logger(formatToolExecutionResultDebugLine(execution, debug));
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
function resolveToolDebugOptions(value) {
|
|
1440
|
-
if (value === true) {
|
|
1441
|
-
return {
|
|
1442
|
-
enabled: true,
|
|
1443
|
-
logger: (line) => console.log(line),
|
|
1444
|
-
includeRequest: true,
|
|
1445
|
-
includeResult: true,
|
|
1446
|
-
includeResultOnError: true,
|
|
1447
|
-
pretty: false
|
|
1448
|
-
};
|
|
1449
|
-
}
|
|
1450
|
-
if (value === undefined || value === false) {
|
|
1451
|
-
return {
|
|
1452
|
-
enabled: false,
|
|
1453
|
-
logger: () => {
|
|
1454
|
-
return;
|
|
1455
|
-
},
|
|
1456
|
-
includeRequest: false,
|
|
1457
|
-
includeResult: false,
|
|
1458
|
-
includeResultOnError: false,
|
|
1459
|
-
pretty: false
|
|
1460
|
-
};
|
|
1461
|
-
}
|
|
1462
|
-
return {
|
|
1463
|
-
enabled: value.enabled ?? true,
|
|
1464
|
-
logger: value.logger ?? ((line) => console.log(line)),
|
|
1465
|
-
includeRequest: value.includeRequest ?? true,
|
|
1466
|
-
includeResult: value.includeResult ?? true,
|
|
1467
|
-
includeResultOnError: value.includeResultOnError ?? true,
|
|
1468
|
-
pretty: value.pretty ?? false
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1471
|
-
function formatToolExecutionRequestDebugLine(execution, debug) {
|
|
1472
|
-
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1473
|
-
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1474
|
-
const payload = formatDebugPayload(execution.arguments, debug.pretty);
|
|
1475
|
-
return `[tool:mcp:request] ${scope} ${toolRef}#${execution.callId} arguments=${payload}`;
|
|
1476
|
-
}
|
|
1477
|
-
function formatToolExecutionResultDebugLine(execution, debug) {
|
|
1478
|
-
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1479
|
-
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1480
|
-
if (execution.error) {
|
|
1481
|
-
const payload2 = formatDebugPayload({ error: execution.error }, debug.pretty);
|
|
1482
|
-
return `[tool:mcp:result:error] ${scope} ${toolRef}#${execution.callId} output=${payload2}`;
|
|
1483
|
-
}
|
|
1484
|
-
const payload = formatDebugPayload(execution.output, debug.pretty);
|
|
1485
|
-
return `[tool:mcp:result:ok] ${scope} ${toolRef}#${execution.callId} output=${payload}`;
|
|
1486
|
-
}
|
|
1487
|
-
function formatDebugPayload(value, pretty) {
|
|
1488
|
-
if (value === undefined) {
|
|
1489
|
-
return "undefined";
|
|
1490
|
-
}
|
|
1491
|
-
try {
|
|
1492
|
-
const serialized = JSON.stringify(value, null, pretty ? 2 : 0);
|
|
1493
|
-
return serialized ?? "undefined";
|
|
1494
|
-
} catch {
|
|
1495
|
-
return String(value);
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
1506
|
function countNameCollisions(names) {
|
|
1499
1507
|
const out = new Map;
|
|
1500
1508
|
for (const name of names) {
|
|
@@ -1691,103 +1699,89 @@ function createOpenAICompatibleAdapter(options) {
|
|
|
1691
1699
|
if (usesMCP) {
|
|
1692
1700
|
return streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks);
|
|
1693
1701
|
}
|
|
1694
|
-
|
|
1695
|
-
method: "POST",
|
|
1696
|
-
headers: buildHeaders(options),
|
|
1697
|
-
body: JSON.stringify(cleanUndefined({
|
|
1698
|
-
...options.defaultBody,
|
|
1699
|
-
...request.body,
|
|
1700
|
-
model: options.model,
|
|
1701
|
-
messages: buildMessages(request),
|
|
1702
|
-
temperature: request.temperature,
|
|
1703
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1704
|
-
max_tokens: request.maxTokens,
|
|
1705
|
-
stream: true
|
|
1706
|
-
})),
|
|
1707
|
-
signal: request.signal
|
|
1708
|
-
});
|
|
1709
|
-
if (!response.ok) {
|
|
1710
|
-
const message = await response.text();
|
|
1711
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1712
|
-
}
|
|
1713
|
-
callbacks.onStart?.();
|
|
1714
|
-
let text = "";
|
|
1715
|
-
let reasoning = "";
|
|
1716
|
-
let usage;
|
|
1717
|
-
let finishReason;
|
|
1718
|
-
await consumeSSE(response, (data) => {
|
|
1719
|
-
if (data === "[DONE]") {
|
|
1720
|
-
return;
|
|
1721
|
-
}
|
|
1722
|
-
const json = safeJSONParse(data);
|
|
1723
|
-
if (!isRecord2(json)) {
|
|
1724
|
-
return;
|
|
1725
|
-
}
|
|
1726
|
-
const delta = pickAssistantDelta(json);
|
|
1727
|
-
const reasoningDelta = pickAssistantReasoningDelta(json);
|
|
1728
|
-
const chunkUsage = pickUsage(json);
|
|
1729
|
-
const chunkFinishReason = pickFinishReason(json);
|
|
1730
|
-
usage = preferLatestUsage(usage, chunkUsage);
|
|
1731
|
-
if (chunkFinishReason) {
|
|
1732
|
-
finishReason = chunkFinishReason;
|
|
1733
|
-
}
|
|
1734
|
-
if (delta) {
|
|
1735
|
-
text += delta;
|
|
1736
|
-
callbacks.onToken?.(delta);
|
|
1737
|
-
}
|
|
1738
|
-
if (reasoningDelta) {
|
|
1739
|
-
reasoning += reasoningDelta;
|
|
1740
|
-
}
|
|
1741
|
-
if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
|
|
1742
|
-
const chunk = {
|
|
1743
|
-
textDelta: delta,
|
|
1744
|
-
reasoningDelta: reasoningDelta || undefined,
|
|
1745
|
-
raw: json,
|
|
1746
|
-
usage: chunkUsage,
|
|
1747
|
-
finishReason: chunkFinishReason
|
|
1748
|
-
};
|
|
1749
|
-
callbacks.onChunk?.(chunk);
|
|
1750
|
-
}
|
|
1751
|
-
});
|
|
1752
|
-
const out = {
|
|
1753
|
-
text,
|
|
1754
|
-
reasoning: reasoning.length > 0 ? reasoning : undefined,
|
|
1755
|
-
usage,
|
|
1756
|
-
finishReason
|
|
1757
|
-
};
|
|
1758
|
-
callbacks.onComplete?.(out);
|
|
1759
|
-
return out;
|
|
1702
|
+
return streamWithChatCompletionsPassThrough(options, fetcher, path, request, callbacks);
|
|
1760
1703
|
},
|
|
1761
1704
|
async embed(request) {
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1705
|
+
return embedOpenAI(options, fetcher, embeddingPath, request);
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
async function streamWithChatCompletionsPassThrough(options, fetcher, path, request, callbacks) {
|
|
1710
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1711
|
+
messages: buildMessages(request),
|
|
1712
|
+
stream: true
|
|
1713
|
+
}));
|
|
1714
|
+
if (!response.ok) {
|
|
1715
|
+
const message = await response.text();
|
|
1716
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1717
|
+
}
|
|
1718
|
+
callbacks.onStart?.();
|
|
1719
|
+
let text = "";
|
|
1720
|
+
let reasoning = "";
|
|
1721
|
+
let usage;
|
|
1722
|
+
let finishReason;
|
|
1723
|
+
await consumeSSE(response, (data) => {
|
|
1724
|
+
if (data === "[DONE]") {
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
const json = safeJSONParse(data);
|
|
1728
|
+
if (!isRecord2(json)) {
|
|
1729
|
+
return;
|
|
1730
|
+
}
|
|
1731
|
+
const delta = pickAssistantDelta(json);
|
|
1732
|
+
const reasoningDelta = pickAssistantReasoningDelta(json);
|
|
1733
|
+
const chunkUsage = pickUsage(json);
|
|
1734
|
+
const chunkFinishReason = pickFinishReason(json);
|
|
1735
|
+
usage = preferLatestUsage(usage, chunkUsage);
|
|
1736
|
+
if (chunkFinishReason) {
|
|
1737
|
+
finishReason = chunkFinishReason;
|
|
1738
|
+
}
|
|
1739
|
+
if (delta) {
|
|
1740
|
+
text += delta;
|
|
1741
|
+
callbacks.onToken?.(delta);
|
|
1790
1742
|
}
|
|
1743
|
+
if (reasoningDelta) {
|
|
1744
|
+
reasoning += reasoningDelta;
|
|
1745
|
+
}
|
|
1746
|
+
emitOpenAIStreamChunk(callbacks, undefined, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
1747
|
+
});
|
|
1748
|
+
const out = {
|
|
1749
|
+
text,
|
|
1750
|
+
reasoning: reasoning.length > 0 ? reasoning : undefined,
|
|
1751
|
+
usage,
|
|
1752
|
+
finishReason
|
|
1753
|
+
};
|
|
1754
|
+
callbacks.onComplete?.(out);
|
|
1755
|
+
return out;
|
|
1756
|
+
}
|
|
1757
|
+
async function embedOpenAI(options, fetcher, path, request) {
|
|
1758
|
+
const body = cleanUndefined({
|
|
1759
|
+
...options.defaultBody,
|
|
1760
|
+
...request.body,
|
|
1761
|
+
model: request.model ?? options.model,
|
|
1762
|
+
input: request.input,
|
|
1763
|
+
dimensions: request.dimensions,
|
|
1764
|
+
encoding_format: "float"
|
|
1765
|
+
});
|
|
1766
|
+
const response = await fetcher(buildURL(options.baseURL, path), {
|
|
1767
|
+
method: "POST",
|
|
1768
|
+
headers: buildHeaders(options),
|
|
1769
|
+
body: JSON.stringify(body)
|
|
1770
|
+
});
|
|
1771
|
+
if (!response.ok) {
|
|
1772
|
+
const message = await response.text();
|
|
1773
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1774
|
+
}
|
|
1775
|
+
const json = await response.json();
|
|
1776
|
+
const data = json.data;
|
|
1777
|
+
if (!Array.isArray(data)) {
|
|
1778
|
+
throw new Error("Unexpected embedding response: missing data array");
|
|
1779
|
+
}
|
|
1780
|
+
return {
|
|
1781
|
+
embeddings: data.map((d) => isRecord2(d) && Array.isArray(d.embedding) ? d.embedding : []),
|
|
1782
|
+
model: pickString(json.model) ?? body.model,
|
|
1783
|
+
usage: pickUsage(json),
|
|
1784
|
+
raw: json
|
|
1791
1785
|
};
|
|
1792
1786
|
}
|
|
1793
1787
|
async function completeOpenAIRequest(options, fetcher, chatPath, responsesPath, request) {
|
|
@@ -1804,22 +1798,80 @@ async function completeOpenAIRequest(options, fetcher, chatPath, responsesPath,
|
|
|
1804
1798
|
}
|
|
1805
1799
|
return completeWithChatCompletionsPassThrough(options, fetcher, chatPath, request);
|
|
1806
1800
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
1801
|
+
function buildChatCompletionsBody(options, request, overrides) {
|
|
1802
|
+
return buildOpenAIRequestBody(options, request, "max_tokens", overrides);
|
|
1803
|
+
}
|
|
1804
|
+
function buildResponsesBody(options, request, overrides) {
|
|
1805
|
+
return buildOpenAIRequestBody(options, request, "max_output_tokens", overrides);
|
|
1806
|
+
}
|
|
1807
|
+
function buildOpenAIRequestBody(options, request, maxTokenKey, overrides) {
|
|
1808
|
+
return cleanUndefined({
|
|
1809
|
+
...options.defaultBody,
|
|
1810
|
+
...request.body,
|
|
1811
|
+
model: options.model,
|
|
1812
|
+
temperature: request.temperature,
|
|
1813
|
+
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1814
|
+
[maxTokenKey]: request.maxTokens,
|
|
1815
|
+
...overrides
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
function sendOpenAIRequest(options, fetcher, path, request, body) {
|
|
1819
|
+
return fetcher(buildURL(options.baseURL, path), {
|
|
1809
1820
|
method: "POST",
|
|
1810
1821
|
headers: buildHeaders(options),
|
|
1811
|
-
body: JSON.stringify(
|
|
1812
|
-
...options.defaultBody,
|
|
1813
|
-
...request.body,
|
|
1814
|
-
model: options.model,
|
|
1815
|
-
messages: buildMessages(request),
|
|
1816
|
-
temperature: request.temperature,
|
|
1817
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1818
|
-
max_tokens: request.maxTokens,
|
|
1819
|
-
stream: false
|
|
1820
|
-
})),
|
|
1822
|
+
body: JSON.stringify(body),
|
|
1821
1823
|
signal: request.signal
|
|
1822
1824
|
});
|
|
1825
|
+
}
|
|
1826
|
+
async function sendOpenAIJsonRequest(options, fetcher, path, request, body) {
|
|
1827
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, body);
|
|
1828
|
+
if (!response.ok) {
|
|
1829
|
+
const message = await response.text();
|
|
1830
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1831
|
+
}
|
|
1832
|
+
return await response.json();
|
|
1833
|
+
}
|
|
1834
|
+
function createResponsesMCPState(request) {
|
|
1835
|
+
return {
|
|
1836
|
+
input: buildResponsesInput(request),
|
|
1837
|
+
previousResponseId: pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined),
|
|
1838
|
+
aggregatedUsage: undefined,
|
|
1839
|
+
finishReason: undefined,
|
|
1840
|
+
lastPayload: undefined,
|
|
1841
|
+
executedToolCalls: [],
|
|
1842
|
+
toolExecutions: [],
|
|
1843
|
+
reasoningBlocks: []
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
function buildResponsesMCPResult(state, text, raw) {
|
|
1847
|
+
return {
|
|
1848
|
+
text,
|
|
1849
|
+
reasoning: joinReasoningBlocks(state.reasoningBlocks) || undefined,
|
|
1850
|
+
reasoningBlocks: state.reasoningBlocks.length > 0 ? state.reasoningBlocks : undefined,
|
|
1851
|
+
raw,
|
|
1852
|
+
usage: state.aggregatedUsage,
|
|
1853
|
+
finishReason: state.finishReason,
|
|
1854
|
+
toolCalls: state.executedToolCalls.length > 0 ? state.executedToolCalls : undefined,
|
|
1855
|
+
toolExecutions: state.toolExecutions.length > 0 ? state.toolExecutions : undefined
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
function emitOpenAIStreamChunk(callbacks, round, raw, delta, reasoningDelta, usage, finishReason) {
|
|
1859
|
+
if (delta || reasoningDelta || usage || finishReason) {
|
|
1860
|
+
callbacks.onChunk?.({
|
|
1861
|
+
textDelta: delta,
|
|
1862
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
1863
|
+
...round !== undefined ? { turnIndex: round } : {},
|
|
1864
|
+
raw,
|
|
1865
|
+
usage,
|
|
1866
|
+
finishReason
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
async function completeWithChatCompletionsPassThrough(options, fetcher, path, request) {
|
|
1871
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1872
|
+
messages: buildMessages(request),
|
|
1873
|
+
stream: false
|
|
1874
|
+
}));
|
|
1823
1875
|
if (!response.ok) {
|
|
1824
1876
|
const message = await response.text();
|
|
1825
1877
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -1867,44 +1919,32 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1867
1919
|
let lastPayload;
|
|
1868
1920
|
const toolCalls = [];
|
|
1869
1921
|
const toolExecutions = [];
|
|
1922
|
+
const reasoningBlocks = [];
|
|
1870
1923
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1871
1924
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1872
1925
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
model: options.model,
|
|
1880
|
-
messages,
|
|
1881
|
-
temperature: request.temperature,
|
|
1882
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1883
|
-
max_tokens: request.maxTokens,
|
|
1884
|
-
tools: transportTools,
|
|
1885
|
-
tool_choice: request.toolChoice,
|
|
1886
|
-
parallel_tool_calls: request.parallelToolCalls
|
|
1887
|
-
})),
|
|
1888
|
-
signal: request.signal
|
|
1889
|
-
});
|
|
1890
|
-
if (!response.ok) {
|
|
1891
|
-
const message = await response.text();
|
|
1892
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1893
|
-
}
|
|
1894
|
-
const payload = await response.json();
|
|
1926
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1927
|
+
messages,
|
|
1928
|
+
tools: transportTools,
|
|
1929
|
+
tool_choice: request.toolChoice,
|
|
1930
|
+
parallel_tool_calls: request.parallelToolCalls
|
|
1931
|
+
}));
|
|
1895
1932
|
lastPayload = payload;
|
|
1896
1933
|
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
1897
1934
|
finishReason = pickFinishReason(payload);
|
|
1898
1935
|
const assistantMessage = pickAssistantMessage(payload);
|
|
1899
1936
|
const calledTools = pickChatToolCalls(payload);
|
|
1937
|
+
const roundReasoning = pickAssistantReasoning(payload);
|
|
1938
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
1900
1939
|
if (!assistantMessage) {
|
|
1901
1940
|
throw new Error("No assistant message in OpenAI-compatible response.");
|
|
1902
1941
|
}
|
|
1903
1942
|
if (calledTools.length === 0) {
|
|
1904
|
-
const reasoning =
|
|
1943
|
+
const reasoning = joinReasoningBlocks(reasoningBlocks) || undefined;
|
|
1905
1944
|
return {
|
|
1906
1945
|
text: pickAssistantText(payload),
|
|
1907
|
-
reasoning
|
|
1946
|
+
reasoning,
|
|
1947
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1908
1948
|
raw: payload,
|
|
1909
1949
|
usage: aggregatedUsage,
|
|
1910
1950
|
finishReason,
|
|
@@ -1932,10 +1972,8 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1932
1972
|
}
|
|
1933
1973
|
return {
|
|
1934
1974
|
text: pickAssistantText(lastPayload ?? {}),
|
|
1935
|
-
reasoning: (
|
|
1936
|
-
|
|
1937
|
-
return value.length > 0 ? value : undefined;
|
|
1938
|
-
})(),
|
|
1975
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
1976
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1939
1977
|
raw: lastPayload,
|
|
1940
1978
|
usage: aggregatedUsage,
|
|
1941
1979
|
finishReason,
|
|
@@ -1945,26 +1983,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1945
1983
|
}
|
|
1946
1984
|
async function completeWithResponsesAPIPassThrough(options, fetcher, path, request) {
|
|
1947
1985
|
const body = isRecord2(request.body) ? request.body : undefined;
|
|
1948
|
-
const
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
...options.defaultBody,
|
|
1953
|
-
...request.body,
|
|
1954
|
-
model: options.model,
|
|
1955
|
-
input: buildResponsesInput(request),
|
|
1956
|
-
previous_response_id: pickString(body?.previous_response_id),
|
|
1957
|
-
temperature: request.temperature,
|
|
1958
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1959
|
-
max_output_tokens: request.maxTokens
|
|
1960
|
-
})),
|
|
1961
|
-
signal: request.signal
|
|
1962
|
-
});
|
|
1963
|
-
if (!response.ok) {
|
|
1964
|
-
const message = await response.text();
|
|
1965
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1966
|
-
}
|
|
1967
|
-
const payload = await response.json();
|
|
1986
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
1987
|
+
input: buildResponsesInput(request),
|
|
1988
|
+
previous_response_id: pickString(body?.previous_response_id)
|
|
1989
|
+
}));
|
|
1968
1990
|
const toolCalls = pickResponsesToolCalls(payload);
|
|
1969
1991
|
return {
|
|
1970
1992
|
text: pickResponsesText(payload) || pickAssistantText(payload),
|
|
@@ -1976,54 +1998,26 @@ async function completeWithResponsesAPIPassThrough(options, fetcher, path, reque
|
|
|
1976
1998
|
}
|
|
1977
1999
|
async function completeWithResponsesAPIWithMCP(options, fetcher, path, request) {
|
|
1978
2000
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
1979
|
-
|
|
1980
|
-
let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
|
|
1981
|
-
let aggregatedUsage;
|
|
1982
|
-
let finishReason;
|
|
1983
|
-
let lastPayload;
|
|
1984
|
-
const executedToolCalls = [];
|
|
1985
|
-
const toolExecutions = [];
|
|
2001
|
+
const state = createResponsesMCPState(request);
|
|
1986
2002
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1987
2003
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1988
2004
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
1989
|
-
const
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
max_output_tokens: request.maxTokens,
|
|
2001
|
-
tools: transportTools,
|
|
2002
|
-
tool_choice: request.toolChoice,
|
|
2003
|
-
parallel_tool_calls: request.parallelToolCalls
|
|
2004
|
-
})),
|
|
2005
|
-
signal: request.signal
|
|
2006
|
-
});
|
|
2007
|
-
if (!response.ok) {
|
|
2008
|
-
const message = await response.text();
|
|
2009
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2010
|
-
}
|
|
2011
|
-
const payload = await response.json();
|
|
2012
|
-
lastPayload = payload;
|
|
2013
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
2014
|
-
finishReason = pickResponsesFinishReason(payload) ?? finishReason;
|
|
2005
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
2006
|
+
input: state.input,
|
|
2007
|
+
previous_response_id: state.previousResponseId,
|
|
2008
|
+
tools: transportTools,
|
|
2009
|
+
tool_choice: request.toolChoice,
|
|
2010
|
+
parallel_tool_calls: request.parallelToolCalls
|
|
2011
|
+
}));
|
|
2012
|
+
state.lastPayload = payload;
|
|
2013
|
+
state.aggregatedUsage = mergeUsage(state.aggregatedUsage, pickUsage(payload));
|
|
2014
|
+
state.finishReason = pickResponsesFinishReason(payload) ?? state.finishReason;
|
|
2015
|
+
pushReasoningBlock(state.reasoningBlocks, round, pickResponsesReasoning(payload));
|
|
2015
2016
|
const providerToolCalls = pickResponsesToolCalls(payload);
|
|
2016
2017
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
2017
2018
|
if (functionCalls.length === 0) {
|
|
2018
2019
|
const text = pickResponsesText(payload) || pickAssistantText(payload);
|
|
2019
|
-
return
|
|
2020
|
-
text,
|
|
2021
|
-
raw: payload,
|
|
2022
|
-
usage: aggregatedUsage,
|
|
2023
|
-
finishReason,
|
|
2024
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2025
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2026
|
-
};
|
|
2020
|
+
return buildResponsesMCPResult(state, text, payload);
|
|
2027
2021
|
}
|
|
2028
2022
|
if (round > maxToolRounds) {
|
|
2029
2023
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
@@ -2034,23 +2028,16 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
2034
2028
|
provider: "openai-compatible",
|
|
2035
2029
|
model: options.model
|
|
2036
2030
|
});
|
|
2037
|
-
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2038
|
-
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2039
|
-
input = outputs.map((entry) => ({
|
|
2031
|
+
state.executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2032
|
+
state.toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2033
|
+
state.input = outputs.map((entry) => ({
|
|
2040
2034
|
type: "function_call_output",
|
|
2041
2035
|
call_id: entry.call.id,
|
|
2042
2036
|
output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
|
|
2043
2037
|
}));
|
|
2044
|
-
previousResponseId = pickString(payload.id);
|
|
2038
|
+
state.previousResponseId = pickString(payload.id);
|
|
2045
2039
|
}
|
|
2046
|
-
return {
|
|
2047
|
-
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
2048
|
-
raw: lastPayload,
|
|
2049
|
-
usage: aggregatedUsage,
|
|
2050
|
-
finishReason,
|
|
2051
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2052
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2053
|
-
};
|
|
2040
|
+
return buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
|
|
2054
2041
|
}
|
|
2055
2042
|
async function streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks) {
|
|
2056
2043
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
@@ -2060,30 +2047,19 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2060
2047
|
let lastPayload;
|
|
2061
2048
|
const executedToolCalls = [];
|
|
2062
2049
|
const toolExecutions = [];
|
|
2050
|
+
const reasoningBlocks = [];
|
|
2063
2051
|
callbacks.onStart?.();
|
|
2064
2052
|
let lastRoundText = "";
|
|
2065
|
-
let lastRoundReasoning = "";
|
|
2066
2053
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2067
2054
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2068
2055
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
2069
|
-
const response = await
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
messages,
|
|
2077
|
-
temperature: request.temperature,
|
|
2078
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2079
|
-
max_tokens: request.maxTokens,
|
|
2080
|
-
tools: transportTools,
|
|
2081
|
-
tool_choice: request.toolChoice,
|
|
2082
|
-
parallel_tool_calls: request.parallelToolCalls,
|
|
2083
|
-
stream: true
|
|
2084
|
-
})),
|
|
2085
|
-
signal: request.signal
|
|
2086
|
-
});
|
|
2056
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
2057
|
+
messages,
|
|
2058
|
+
tools: transportTools,
|
|
2059
|
+
tool_choice: request.toolChoice,
|
|
2060
|
+
parallel_tool_calls: request.parallelToolCalls,
|
|
2061
|
+
stream: true
|
|
2062
|
+
}));
|
|
2087
2063
|
if (!response.ok) {
|
|
2088
2064
|
const message = await response.text();
|
|
2089
2065
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2120,38 +2096,48 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2120
2096
|
roundReasoning += reasoningDelta;
|
|
2121
2097
|
reasoningFieldName ??= pickAssistantReasoningDeltaFieldName(json);
|
|
2122
2098
|
}
|
|
2123
|
-
|
|
2124
|
-
const chunk = {
|
|
2125
|
-
textDelta: delta,
|
|
2126
|
-
reasoningDelta: reasoningDelta || undefined,
|
|
2127
|
-
raw: json,
|
|
2128
|
-
usage: chunkUsage,
|
|
2129
|
-
finishReason: chunkFinishReason
|
|
2130
|
-
};
|
|
2131
|
-
callbacks.onChunk?.(chunk);
|
|
2132
|
-
}
|
|
2099
|
+
emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
2133
2100
|
});
|
|
2134
2101
|
aggregatedUsage = mergeUsage(aggregatedUsage, roundUsage);
|
|
2135
2102
|
if (roundFinishReason) {
|
|
2136
2103
|
finishReason = roundFinishReason;
|
|
2137
2104
|
}
|
|
2138
2105
|
const calledTools = buildOpenAIStreamToolCalls(streamedToolCalls);
|
|
2106
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
2107
|
+
request.onTurnTransition?.({
|
|
2108
|
+
turnIndex: round,
|
|
2109
|
+
kind: "reasoningComplete",
|
|
2110
|
+
reasoningText: roundReasoning
|
|
2111
|
+
});
|
|
2139
2112
|
if (calledTools.length === 0) {
|
|
2140
2113
|
const out2 = {
|
|
2141
2114
|
text: roundText,
|
|
2142
|
-
reasoning:
|
|
2115
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2116
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2143
2117
|
raw: lastPayload,
|
|
2144
2118
|
usage: aggregatedUsage,
|
|
2145
2119
|
finishReason,
|
|
2146
2120
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2147
2121
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2148
2122
|
};
|
|
2123
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2149
2124
|
callbacks.onComplete?.(out2);
|
|
2150
2125
|
return out2;
|
|
2151
2126
|
}
|
|
2152
2127
|
if (round > maxToolRounds) {
|
|
2153
2128
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
2154
2129
|
}
|
|
2130
|
+
request.onTurnTransition?.({
|
|
2131
|
+
turnIndex: round,
|
|
2132
|
+
kind: "toolCallsEmit",
|
|
2133
|
+
toolCalls: calledTools
|
|
2134
|
+
});
|
|
2135
|
+
callbacks.onChunk?.({
|
|
2136
|
+
textDelta: "",
|
|
2137
|
+
turnIndex: round,
|
|
2138
|
+
toolCalls: calledTools,
|
|
2139
|
+
finishReason: roundFinishReason
|
|
2140
|
+
});
|
|
2155
2141
|
const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
|
|
2156
2142
|
round,
|
|
2157
2143
|
request,
|
|
@@ -2160,8 +2146,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2160
2146
|
});
|
|
2161
2147
|
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2162
2148
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2149
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2163
2150
|
lastRoundText = roundText;
|
|
2164
|
-
lastRoundReasoning = roundReasoning;
|
|
2165
2151
|
const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
|
|
2166
2152
|
reasoning: roundReasoning,
|
|
2167
2153
|
reasoningFieldName
|
|
@@ -2175,34 +2161,25 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2175
2161
|
}
|
|
2176
2162
|
const out = {
|
|
2177
2163
|
text: lastRoundText,
|
|
2178
|
-
reasoning:
|
|
2164
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2165
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2179
2166
|
raw: lastPayload,
|
|
2180
2167
|
usage: aggregatedUsage,
|
|
2181
2168
|
finishReason,
|
|
2182
2169
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2183
2170
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2184
2171
|
};
|
|
2172
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2185
2173
|
callbacks.onComplete?.(out);
|
|
2186
2174
|
return out;
|
|
2187
2175
|
}
|
|
2188
2176
|
async function streamWithResponsesAPIPassThrough(options, fetcher, path, request, callbacks) {
|
|
2189
2177
|
const body = isRecord2(request.body) ? request.body : undefined;
|
|
2190
|
-
const response = await
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
...request.body,
|
|
2196
|
-
model: options.model,
|
|
2197
|
-
input: buildResponsesInput(request),
|
|
2198
|
-
previous_response_id: pickString(body?.previous_response_id),
|
|
2199
|
-
temperature: request.temperature,
|
|
2200
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2201
|
-
max_output_tokens: request.maxTokens,
|
|
2202
|
-
stream: true
|
|
2203
|
-
})),
|
|
2204
|
-
signal: request.signal
|
|
2205
|
-
});
|
|
2178
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
2179
|
+
input: buildResponsesInput(request),
|
|
2180
|
+
previous_response_id: pickString(body?.previous_response_id),
|
|
2181
|
+
stream: true
|
|
2182
|
+
}));
|
|
2206
2183
|
if (!response.ok) {
|
|
2207
2184
|
const message = await response.text();
|
|
2208
2185
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2235,15 +2212,7 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
|
|
|
2235
2212
|
text += delta;
|
|
2236
2213
|
callbacks.onToken?.(delta);
|
|
2237
2214
|
}
|
|
2238
|
-
|
|
2239
|
-
const chunk = {
|
|
2240
|
-
textDelta: delta,
|
|
2241
|
-
raw: json,
|
|
2242
|
-
usage: chunkUsage,
|
|
2243
|
-
finishReason: chunkFinishReason
|
|
2244
|
-
};
|
|
2245
|
-
callbacks.onChunk?.(chunk);
|
|
2246
|
-
}
|
|
2215
|
+
emitOpenAIStreamChunk(callbacks, undefined, json, delta, "", chunkUsage, chunkFinishReason);
|
|
2247
2216
|
});
|
|
2248
2217
|
const finalPayload = lastPayload ?? {};
|
|
2249
2218
|
const out = {
|
|
@@ -2257,41 +2226,25 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
|
|
|
2257
2226
|
}
|
|
2258
2227
|
async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, callbacks) {
|
|
2259
2228
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
2260
|
-
|
|
2261
|
-
let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
|
|
2262
|
-
let aggregatedUsage;
|
|
2263
|
-
let finishReason;
|
|
2264
|
-
let lastPayload;
|
|
2265
|
-
const executedToolCalls = [];
|
|
2266
|
-
const toolExecutions = [];
|
|
2229
|
+
const state = createResponsesMCPState(request);
|
|
2267
2230
|
callbacks.onStart?.();
|
|
2268
2231
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2269
2232
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2270
2233
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
2271
|
-
const response = await
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
previous_response_id: previousResponseId,
|
|
2280
|
-
temperature: request.temperature,
|
|
2281
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2282
|
-
max_output_tokens: request.maxTokens,
|
|
2283
|
-
tools: transportTools,
|
|
2284
|
-
tool_choice: request.toolChoice,
|
|
2285
|
-
parallel_tool_calls: request.parallelToolCalls,
|
|
2286
|
-
stream: true
|
|
2287
|
-
})),
|
|
2288
|
-
signal: request.signal
|
|
2289
|
-
});
|
|
2234
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
2235
|
+
input: state.input,
|
|
2236
|
+
previous_response_id: state.previousResponseId,
|
|
2237
|
+
tools: transportTools,
|
|
2238
|
+
tool_choice: request.toolChoice,
|
|
2239
|
+
parallel_tool_calls: request.parallelToolCalls,
|
|
2240
|
+
stream: true
|
|
2241
|
+
}));
|
|
2290
2242
|
if (!response.ok) {
|
|
2291
2243
|
const message = await response.text();
|
|
2292
2244
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2293
2245
|
}
|
|
2294
2246
|
let roundText = "";
|
|
2247
|
+
let roundReasoning = "";
|
|
2295
2248
|
let roundUsage;
|
|
2296
2249
|
let roundFinishReason;
|
|
2297
2250
|
let roundPayload;
|
|
@@ -2307,9 +2260,10 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2307
2260
|
const payload = pickResponsesStreamPayload(json);
|
|
2308
2261
|
if (payload) {
|
|
2309
2262
|
roundPayload = payload;
|
|
2310
|
-
lastPayload = payload;
|
|
2263
|
+
state.lastPayload = payload;
|
|
2311
2264
|
}
|
|
2312
2265
|
const delta = pickResponsesStreamTextDelta(json);
|
|
2266
|
+
const reasoningDelta = pickResponsesStreamReasoningDelta(json);
|
|
2313
2267
|
const chunkUsage = pickResponsesStreamUsage(json);
|
|
2314
2268
|
const chunkFinishReason = pickResponsesStreamFinishReason(json);
|
|
2315
2269
|
collectResponsesStreamToolCalls(json, streamedToolCalls);
|
|
@@ -2321,66 +2275,70 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2321
2275
|
roundText += delta;
|
|
2322
2276
|
callbacks.onToken?.(delta);
|
|
2323
2277
|
}
|
|
2324
|
-
if (
|
|
2325
|
-
|
|
2326
|
-
textDelta: delta,
|
|
2327
|
-
raw: json,
|
|
2328
|
-
usage: chunkUsage,
|
|
2329
|
-
finishReason: chunkFinishReason
|
|
2330
|
-
};
|
|
2331
|
-
callbacks.onChunk?.(chunk);
|
|
2278
|
+
if (reasoningDelta) {
|
|
2279
|
+
roundReasoning += reasoningDelta;
|
|
2332
2280
|
}
|
|
2281
|
+
emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
2333
2282
|
});
|
|
2334
2283
|
const resolvedRoundUsage = preferLatestUsage(roundUsage, roundPayload ? pickUsage(roundPayload) : undefined);
|
|
2335
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, resolvedRoundUsage);
|
|
2284
|
+
state.aggregatedUsage = mergeUsage(state.aggregatedUsage, resolvedRoundUsage);
|
|
2336
2285
|
if (roundFinishReason) {
|
|
2337
|
-
finishReason = roundFinishReason;
|
|
2286
|
+
state.finishReason = roundFinishReason;
|
|
2338
2287
|
} else if (roundPayload) {
|
|
2339
|
-
finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
|
|
2288
|
+
state.finishReason = pickResponsesFinishReason(roundPayload) ?? state.finishReason;
|
|
2340
2289
|
}
|
|
2341
2290
|
const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
|
|
2291
|
+
if (roundPayload && roundReasoning.length === 0) {
|
|
2292
|
+
roundReasoning = pickResponsesReasoning(roundPayload);
|
|
2293
|
+
}
|
|
2342
2294
|
const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
|
|
2343
2295
|
const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
|
|
2344
2296
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
2297
|
+
pushReasoningBlock(state.reasoningBlocks, round, roundReasoning);
|
|
2298
|
+
request.onTurnTransition?.({
|
|
2299
|
+
turnIndex: round,
|
|
2300
|
+
kind: "reasoningComplete",
|
|
2301
|
+
reasoningText: roundReasoning
|
|
2302
|
+
});
|
|
2345
2303
|
if (functionCalls.length === 0) {
|
|
2346
2304
|
const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
|
|
2347
|
-
const out2 =
|
|
2348
|
-
|
|
2349
|
-
raw: roundPayload ?? lastPayload,
|
|
2350
|
-
usage: aggregatedUsage,
|
|
2351
|
-
finishReason,
|
|
2352
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2353
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2354
|
-
};
|
|
2305
|
+
const out2 = buildResponsesMCPResult(state, finalText, roundPayload ?? state.lastPayload);
|
|
2306
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2355
2307
|
callbacks.onComplete?.(out2);
|
|
2356
2308
|
return out2;
|
|
2357
2309
|
}
|
|
2358
2310
|
if (round > maxToolRounds) {
|
|
2359
2311
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
2360
2312
|
}
|
|
2313
|
+
request.onTurnTransition?.({
|
|
2314
|
+
turnIndex: round,
|
|
2315
|
+
kind: "toolCallsEmit",
|
|
2316
|
+
toolCalls: functionCalls
|
|
2317
|
+
});
|
|
2318
|
+
callbacks.onChunk?.({
|
|
2319
|
+
textDelta: "",
|
|
2320
|
+
turnIndex: round,
|
|
2321
|
+
toolCalls: functionCalls,
|
|
2322
|
+
finishReason: roundFinishReason
|
|
2323
|
+
});
|
|
2361
2324
|
const outputs = await executeMCPToolCalls(functionCalls, mcpToolset, {
|
|
2362
2325
|
round,
|
|
2363
2326
|
request,
|
|
2364
2327
|
provider: "openai-compatible",
|
|
2365
2328
|
model: options.model
|
|
2366
2329
|
});
|
|
2367
|
-
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2368
|
-
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2369
|
-
|
|
2330
|
+
state.executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2331
|
+
state.toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2332
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2333
|
+
state.input = outputs.map((entry) => ({
|
|
2370
2334
|
type: "function_call_output",
|
|
2371
2335
|
call_id: entry.call.id,
|
|
2372
2336
|
output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
|
|
2373
2337
|
}));
|
|
2374
|
-
previousResponseId = pickString(roundPayload?.id);
|
|
2338
|
+
state.previousResponseId = pickString(roundPayload?.id);
|
|
2375
2339
|
}
|
|
2376
|
-
const out = {
|
|
2377
|
-
|
|
2378
|
-
raw: lastPayload,
|
|
2379
|
-
usage: aggregatedUsage,
|
|
2380
|
-
finishReason,
|
|
2381
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2382
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2383
|
-
};
|
|
2340
|
+
const out = buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
|
|
2341
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2384
2342
|
callbacks.onComplete?.(out);
|
|
2385
2343
|
return out;
|
|
2386
2344
|
}
|
|
@@ -2532,7 +2490,7 @@ function pickAssistantDelta(payload) {
|
|
|
2532
2490
|
if (!isRecord2(delta)) {
|
|
2533
2491
|
return "";
|
|
2534
2492
|
}
|
|
2535
|
-
return
|
|
2493
|
+
return pickTextLike(delta.content);
|
|
2536
2494
|
}
|
|
2537
2495
|
function pickAssistantReasoning(payload) {
|
|
2538
2496
|
const message = pickAssistantMessage(payload);
|
|
@@ -2665,6 +2623,20 @@ function pickResponsesStreamTextDelta(payload) {
|
|
|
2665
2623
|
}
|
|
2666
2624
|
return "";
|
|
2667
2625
|
}
|
|
2626
|
+
function pickResponsesStreamReasoningDelta(payload) {
|
|
2627
|
+
const eventType = pickString(payload.type) ?? "";
|
|
2628
|
+
if (!eventType.includes("reasoning") && !eventType.includes("thinking")) {
|
|
2629
|
+
return "";
|
|
2630
|
+
}
|
|
2631
|
+
const direct = pickString(payload.delta);
|
|
2632
|
+
if (direct) {
|
|
2633
|
+
return direct;
|
|
2634
|
+
}
|
|
2635
|
+
if (isRecord2(payload.delta)) {
|
|
2636
|
+
return pickReasoningText(payload.delta) || pickString(payload.delta.text) || pickString(payload.delta.summary_text) || "";
|
|
2637
|
+
}
|
|
2638
|
+
return "";
|
|
2639
|
+
}
|
|
2668
2640
|
function pickResponsesStreamUsage(payload) {
|
|
2669
2641
|
const direct = pickUsage(payload);
|
|
2670
2642
|
if (direct) {
|
|
@@ -2799,10 +2771,34 @@ function pickResponsesText(payload) {
|
|
|
2799
2771
|
}).join("");
|
|
2800
2772
|
}).join("");
|
|
2801
2773
|
}
|
|
2774
|
+
function pickResponsesReasoning(payload) {
|
|
2775
|
+
const direct = pickReasoningText(payload);
|
|
2776
|
+
if (direct) {
|
|
2777
|
+
return direct;
|
|
2778
|
+
}
|
|
2779
|
+
const output = payload.output;
|
|
2780
|
+
if (!Array.isArray(output)) {
|
|
2781
|
+
return "";
|
|
2782
|
+
}
|
|
2783
|
+
return output.map((item) => {
|
|
2784
|
+
if (!isRecord2(item)) {
|
|
2785
|
+
return "";
|
|
2786
|
+
}
|
|
2787
|
+
const itemReasoning = pickReasoningText(item);
|
|
2788
|
+
if (itemReasoning) {
|
|
2789
|
+
return itemReasoning;
|
|
2790
|
+
}
|
|
2791
|
+
const itemType = pickString(item.type) ?? "";
|
|
2792
|
+
if ((itemType.includes("reasoning") || itemType.includes("thinking")) && Array.isArray(item.content)) {
|
|
2793
|
+
return item.content.map((part) => isRecord2(part) ? pickTextLike(part) : "").join("");
|
|
2794
|
+
}
|
|
2795
|
+
return "";
|
|
2796
|
+
}).join("");
|
|
2797
|
+
}
|
|
2802
2798
|
function pickAssistantText(payload) {
|
|
2803
2799
|
const message = pickAssistantMessage(payload);
|
|
2804
2800
|
if (message) {
|
|
2805
|
-
const text =
|
|
2801
|
+
const text = pickTextLike(message.content);
|
|
2806
2802
|
if (text.length > 0) {
|
|
2807
2803
|
return text;
|
|
2808
2804
|
}
|
|
@@ -2819,8 +2815,17 @@ function pickAssistantText(payload) {
|
|
|
2819
2815
|
function pickReasoningText(value) {
|
|
2820
2816
|
return pickTextLike(value.reasoning) || pickTextLike(value.reasoning_content);
|
|
2821
2817
|
}
|
|
2822
|
-
function
|
|
2823
|
-
|
|
2818
|
+
function pushReasoningBlock(blocks, turnIndex, text) {
|
|
2819
|
+
const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
|
|
2820
|
+
if (!clean) {
|
|
2821
|
+
return;
|
|
2822
|
+
}
|
|
2823
|
+
blocks.push({ turnIndex, text: clean });
|
|
2824
|
+
}
|
|
2825
|
+
function joinReasoningBlocks(blocks) {
|
|
2826
|
+
return blocks.map((block) => block.text).filter(Boolean).join(`
|
|
2827
|
+
|
|
2828
|
+
`);
|
|
2824
2829
|
}
|
|
2825
2830
|
function pickTextLike(value) {
|
|
2826
2831
|
if (typeof value === "string") {
|
|
@@ -2895,83 +2900,81 @@ function createAnthropicCompatibleAdapter(options) {
|
|
|
2895
2900
|
if (hasMCPClients(request.mcpClients)) {
|
|
2896
2901
|
return streamWithMCPToolLoop(options, fetcher, path, request, callbacks);
|
|
2897
2902
|
}
|
|
2898
|
-
|
|
2899
|
-
const response = await fetcher(buildURL(options.baseURL, path), {
|
|
2900
|
-
method: "POST",
|
|
2901
|
-
headers: buildHeaders2(options),
|
|
2902
|
-
body: JSON.stringify(buildAnthropicRequestBody(options, request, {
|
|
2903
|
-
...options.defaultBody,
|
|
2904
|
-
...request.body,
|
|
2905
|
-
model: options.model,
|
|
2906
|
-
system: input.systemPrompt,
|
|
2907
|
-
messages: input.messages,
|
|
2908
|
-
temperature: request.temperature,
|
|
2909
|
-
stream: true
|
|
2910
|
-
})),
|
|
2911
|
-
signal: request.signal
|
|
2912
|
-
});
|
|
2913
|
-
if (!response.ok) {
|
|
2914
|
-
const message = await response.text();
|
|
2915
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2916
|
-
}
|
|
2917
|
-
callbacks.onStart?.();
|
|
2918
|
-
let text = "";
|
|
2919
|
-
let usage;
|
|
2920
|
-
let finishReason;
|
|
2921
|
-
await consumeSSE(response, (data) => {
|
|
2922
|
-
if (data === "[DONE]") {
|
|
2923
|
-
return;
|
|
2924
|
-
}
|
|
2925
|
-
const json = safeJSONParse(data);
|
|
2926
|
-
if (!isRecord2(json)) {
|
|
2927
|
-
return;
|
|
2928
|
-
}
|
|
2929
|
-
const delta = pickAnthropicDelta(json);
|
|
2930
|
-
const chunkUsage = pickUsage2(json);
|
|
2931
|
-
const chunkFinishReason = pickFinishReason2(json);
|
|
2932
|
-
usage = preferLatestUsage(usage, chunkUsage);
|
|
2933
|
-
if (chunkFinishReason) {
|
|
2934
|
-
finishReason = chunkFinishReason;
|
|
2935
|
-
}
|
|
2936
|
-
if (delta) {
|
|
2937
|
-
text += delta;
|
|
2938
|
-
callbacks.onToken?.(delta);
|
|
2939
|
-
}
|
|
2940
|
-
if (delta || chunkUsage || chunkFinishReason) {
|
|
2941
|
-
const chunk = {
|
|
2942
|
-
textDelta: delta,
|
|
2943
|
-
raw: json,
|
|
2944
|
-
usage: chunkUsage,
|
|
2945
|
-
finishReason: chunkFinishReason
|
|
2946
|
-
};
|
|
2947
|
-
callbacks.onChunk?.(chunk);
|
|
2948
|
-
}
|
|
2949
|
-
});
|
|
2950
|
-
const out = { text, usage, finishReason };
|
|
2951
|
-
callbacks.onComplete?.(out);
|
|
2952
|
-
return out;
|
|
2903
|
+
return streamPassThrough(options, fetcher, path, request, callbacks);
|
|
2953
2904
|
},
|
|
2954
2905
|
async embed() {
|
|
2955
2906
|
throw new Error("Anthropic does not provide a native embedding API. " + "Use the openai-compatible provider with Voyage AI (https://api.voyageai.com) — " + "Anthropic's recommended embedding solution, which uses the same request format.");
|
|
2956
2907
|
}
|
|
2957
2908
|
};
|
|
2958
2909
|
}
|
|
2959
|
-
async function
|
|
2910
|
+
async function streamPassThrough(options, fetcher, path, request, callbacks) {
|
|
2960
2911
|
const input = resolveAnthropicInput(request);
|
|
2961
|
-
const response = await
|
|
2912
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
2913
|
+
system: input.systemPrompt,
|
|
2914
|
+
messages: input.messages,
|
|
2915
|
+
stream: true
|
|
2916
|
+
});
|
|
2917
|
+
if (!response.ok) {
|
|
2918
|
+
const message = await response.text();
|
|
2919
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2920
|
+
}
|
|
2921
|
+
callbacks.onStart?.();
|
|
2922
|
+
let text = "";
|
|
2923
|
+
let usage;
|
|
2924
|
+
let finishReason;
|
|
2925
|
+
await consumeSSE(response, (data) => {
|
|
2926
|
+
if (data === "[DONE]") {
|
|
2927
|
+
return;
|
|
2928
|
+
}
|
|
2929
|
+
const json = safeJSONParse(data);
|
|
2930
|
+
if (!isRecord2(json)) {
|
|
2931
|
+
return;
|
|
2932
|
+
}
|
|
2933
|
+
const delta = pickAnthropicDelta(json);
|
|
2934
|
+
const chunkUsage = pickUsage2(json);
|
|
2935
|
+
const chunkFinishReason = pickFinishReason2(json);
|
|
2936
|
+
usage = preferLatestUsage(usage, chunkUsage);
|
|
2937
|
+
if (chunkFinishReason) {
|
|
2938
|
+
finishReason = chunkFinishReason;
|
|
2939
|
+
}
|
|
2940
|
+
if (delta) {
|
|
2941
|
+
text += delta;
|
|
2942
|
+
callbacks.onToken?.(delta);
|
|
2943
|
+
}
|
|
2944
|
+
if (delta || chunkUsage || chunkFinishReason) {
|
|
2945
|
+
callbacks.onChunk?.({
|
|
2946
|
+
textDelta: delta,
|
|
2947
|
+
raw: json,
|
|
2948
|
+
usage: chunkUsage,
|
|
2949
|
+
finishReason: chunkFinishReason
|
|
2950
|
+
});
|
|
2951
|
+
}
|
|
2952
|
+
});
|
|
2953
|
+
const out = { text, usage, finishReason };
|
|
2954
|
+
callbacks.onComplete?.(out);
|
|
2955
|
+
return out;
|
|
2956
|
+
}
|
|
2957
|
+
function sendAnthropicMessage(options, fetcher, path, request, body) {
|
|
2958
|
+
return fetcher(buildURL(options.baseURL, path), {
|
|
2962
2959
|
method: "POST",
|
|
2963
2960
|
headers: buildHeaders2(options),
|
|
2964
2961
|
body: JSON.stringify(buildAnthropicRequestBody(options, request, {
|
|
2965
2962
|
...options.defaultBody,
|
|
2966
2963
|
...request.body,
|
|
2967
2964
|
model: options.model,
|
|
2968
|
-
system: input.systemPrompt,
|
|
2969
|
-
messages: input.messages,
|
|
2970
2965
|
temperature: request.temperature,
|
|
2971
|
-
|
|
2966
|
+
...body
|
|
2972
2967
|
})),
|
|
2973
2968
|
signal: request.signal
|
|
2974
2969
|
});
|
|
2970
|
+
}
|
|
2971
|
+
async function completePassThrough(options, fetcher, path, request) {
|
|
2972
|
+
const input = resolveAnthropicInput(request);
|
|
2973
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
2974
|
+
system: input.systemPrompt,
|
|
2975
|
+
messages: input.messages,
|
|
2976
|
+
stream: false
|
|
2977
|
+
});
|
|
2975
2978
|
if (!response.ok) {
|
|
2976
2979
|
const message = await response.text();
|
|
2977
2980
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2999,24 +3002,16 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2999
3002
|
let lastPayload;
|
|
3000
3003
|
const toolCalls = [];
|
|
3001
3004
|
const toolExecutions = [];
|
|
3005
|
+
const reasoningBlocks = [];
|
|
3002
3006
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
3003
3007
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
3004
3008
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
3005
|
-
const response = await
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
model: options.model,
|
|
3012
|
-
system: input.systemPrompt,
|
|
3013
|
-
messages,
|
|
3014
|
-
temperature: request.temperature,
|
|
3015
|
-
tools,
|
|
3016
|
-
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3017
|
-
stream: false
|
|
3018
|
-
})),
|
|
3019
|
-
signal: request.signal
|
|
3009
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
3010
|
+
system: input.systemPrompt,
|
|
3011
|
+
messages,
|
|
3012
|
+
tools,
|
|
3013
|
+
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3014
|
+
stream: false
|
|
3020
3015
|
});
|
|
3021
3016
|
if (!response.ok) {
|
|
3022
3017
|
const message = await response.text();
|
|
@@ -3028,9 +3023,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
3028
3023
|
finishReason = pickFinishReason2(payload);
|
|
3029
3024
|
const content = Array.isArray(payload.content) ? payload.content : [];
|
|
3030
3025
|
const calledTools = pickAnthropicToolCalls(payload).filter((call) => call.type === "function");
|
|
3026
|
+
pushReasoningBlock2(reasoningBlocks, round, extractAnthropicReasoning(payload));
|
|
3031
3027
|
if (calledTools.length === 0) {
|
|
3032
3028
|
return {
|
|
3033
3029
|
text: extractAnthropicText(payload),
|
|
3030
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3031
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3034
3032
|
raw: payload,
|
|
3035
3033
|
usage: aggregatedUsage,
|
|
3036
3034
|
finishReason,
|
|
@@ -3066,6 +3064,8 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
3066
3064
|
}
|
|
3067
3065
|
return {
|
|
3068
3066
|
text: extractAnthropicText(lastPayload ?? {}),
|
|
3067
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3068
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3069
3069
|
raw: lastPayload,
|
|
3070
3070
|
usage: aggregatedUsage,
|
|
3071
3071
|
finishReason,
|
|
@@ -3082,31 +3082,24 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3082
3082
|
let lastPayload;
|
|
3083
3083
|
const toolCalls = [];
|
|
3084
3084
|
const toolExecutions = [];
|
|
3085
|
+
const reasoningBlocks = [];
|
|
3085
3086
|
callbacks.onStart?.();
|
|
3086
3087
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
3087
3088
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
3088
3089
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
3089
|
-
const response = await
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
model: options.model,
|
|
3096
|
-
system: input.systemPrompt,
|
|
3097
|
-
messages,
|
|
3098
|
-
temperature: request.temperature,
|
|
3099
|
-
tools,
|
|
3100
|
-
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3101
|
-
stream: true
|
|
3102
|
-
})),
|
|
3103
|
-
signal: request.signal
|
|
3090
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
3091
|
+
system: input.systemPrompt,
|
|
3092
|
+
messages,
|
|
3093
|
+
tools,
|
|
3094
|
+
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3095
|
+
stream: true
|
|
3104
3096
|
});
|
|
3105
3097
|
if (!response.ok) {
|
|
3106
3098
|
const message = await response.text();
|
|
3107
3099
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
3108
3100
|
}
|
|
3109
3101
|
let roundText = "";
|
|
3102
|
+
let roundReasoning = "";
|
|
3110
3103
|
let roundUsage;
|
|
3111
3104
|
let roundFinishReason;
|
|
3112
3105
|
const streamedToolCalls = new Map;
|
|
@@ -3120,6 +3113,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3120
3113
|
}
|
|
3121
3114
|
lastPayload = json;
|
|
3122
3115
|
const delta = pickAnthropicDelta(json);
|
|
3116
|
+
const reasoningDelta = pickAnthropicReasoningDelta(json);
|
|
3123
3117
|
const chunkUsage = pickUsage2(json);
|
|
3124
3118
|
const chunkFinishReason = pickFinishReason2(json);
|
|
3125
3119
|
collectAnthropicStreamToolCalls(json, streamedToolCalls);
|
|
@@ -3131,9 +3125,14 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3131
3125
|
roundText += delta;
|
|
3132
3126
|
callbacks.onToken?.(delta);
|
|
3133
3127
|
}
|
|
3134
|
-
if (
|
|
3128
|
+
if (reasoningDelta) {
|
|
3129
|
+
roundReasoning += reasoningDelta;
|
|
3130
|
+
}
|
|
3131
|
+
if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
|
|
3135
3132
|
const chunk = {
|
|
3136
3133
|
textDelta: delta,
|
|
3134
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
3135
|
+
turnIndex: round,
|
|
3137
3136
|
raw: json,
|
|
3138
3137
|
usage: chunkUsage,
|
|
3139
3138
|
finishReason: chunkFinishReason
|
|
@@ -3146,21 +3145,41 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3146
3145
|
finishReason = roundFinishReason;
|
|
3147
3146
|
}
|
|
3148
3147
|
const calledTools = buildAnthropicStreamToolCalls(streamedToolCalls);
|
|
3148
|
+
pushReasoningBlock2(reasoningBlocks, round, roundReasoning);
|
|
3149
|
+
request.onTurnTransition?.({
|
|
3150
|
+
turnIndex: round,
|
|
3151
|
+
kind: "reasoningComplete",
|
|
3152
|
+
reasoningText: roundReasoning
|
|
3153
|
+
});
|
|
3149
3154
|
if (calledTools.length === 0) {
|
|
3150
3155
|
const out2 = {
|
|
3151
3156
|
text: roundText,
|
|
3157
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3158
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3152
3159
|
raw: lastPayload,
|
|
3153
3160
|
usage: aggregatedUsage,
|
|
3154
3161
|
finishReason,
|
|
3155
3162
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
3156
3163
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
3157
3164
|
};
|
|
3165
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
3158
3166
|
callbacks.onComplete?.(out2);
|
|
3159
3167
|
return out2;
|
|
3160
3168
|
}
|
|
3161
3169
|
if (round > maxToolRounds) {
|
|
3162
3170
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
3163
3171
|
}
|
|
3172
|
+
request.onTurnTransition?.({
|
|
3173
|
+
turnIndex: round,
|
|
3174
|
+
kind: "toolCallsEmit",
|
|
3175
|
+
toolCalls: calledTools
|
|
3176
|
+
});
|
|
3177
|
+
callbacks.onChunk?.({
|
|
3178
|
+
textDelta: "",
|
|
3179
|
+
turnIndex: round,
|
|
3180
|
+
toolCalls: calledTools,
|
|
3181
|
+
finishReason: roundFinishReason
|
|
3182
|
+
});
|
|
3164
3183
|
const toolResultContent = [];
|
|
3165
3184
|
const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
|
|
3166
3185
|
round,
|
|
@@ -3170,6 +3189,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3170
3189
|
});
|
|
3171
3190
|
toolCalls.push(...outputs.map((entry) => entry.call));
|
|
3172
3191
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
3192
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
3173
3193
|
for (const entry of outputs) {
|
|
3174
3194
|
toolResultContent.push({
|
|
3175
3195
|
type: "tool_result",
|
|
@@ -3186,12 +3206,15 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3186
3206
|
}
|
|
3187
3207
|
const out = {
|
|
3188
3208
|
text: "",
|
|
3209
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3210
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3189
3211
|
raw: lastPayload,
|
|
3190
3212
|
usage: aggregatedUsage,
|
|
3191
3213
|
finishReason,
|
|
3192
3214
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
3193
3215
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
3194
3216
|
};
|
|
3217
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
3195
3218
|
callbacks.onComplete?.(out);
|
|
3196
3219
|
return out;
|
|
3197
3220
|
}
|
|
@@ -3312,10 +3335,26 @@ function extractAnthropicText(payload) {
|
|
|
3312
3335
|
return typeof text === "string" ? text : "";
|
|
3313
3336
|
}).join("");
|
|
3314
3337
|
}
|
|
3315
|
-
function
|
|
3338
|
+
function extractAnthropicReasoning(payload) {
|
|
3316
3339
|
const content = payload.content;
|
|
3317
3340
|
if (!Array.isArray(content)) {
|
|
3318
|
-
return
|
|
3341
|
+
return "";
|
|
3342
|
+
}
|
|
3343
|
+
return content.map((part) => {
|
|
3344
|
+
if (!isRecord2(part)) {
|
|
3345
|
+
return "";
|
|
3346
|
+
}
|
|
3347
|
+
const type = pickString(part.type) ?? "";
|
|
3348
|
+
if (type !== "thinking" && type !== "reasoning") {
|
|
3349
|
+
return "";
|
|
3350
|
+
}
|
|
3351
|
+
return pickString(part.thinking) ?? pickString(part.text) ?? pickString(part.reasoning) ?? "";
|
|
3352
|
+
}).join("");
|
|
3353
|
+
}
|
|
3354
|
+
function pickAnthropicToolCalls(payload) {
|
|
3355
|
+
const content = payload.content;
|
|
3356
|
+
if (!Array.isArray(content)) {
|
|
3357
|
+
return [];
|
|
3319
3358
|
}
|
|
3320
3359
|
const calls = [];
|
|
3321
3360
|
for (const part of content) {
|
|
@@ -3342,6 +3381,35 @@ function pickAnthropicDelta(payload) {
|
|
|
3342
3381
|
}
|
|
3343
3382
|
return "";
|
|
3344
3383
|
}
|
|
3384
|
+
function pickAnthropicReasoningDelta(payload) {
|
|
3385
|
+
const deltaObject = payload.delta;
|
|
3386
|
+
if (isRecord2(deltaObject)) {
|
|
3387
|
+
const type = pickString(deltaObject.type) ?? "";
|
|
3388
|
+
if (type === "thinking_delta" || type === "reasoning_delta") {
|
|
3389
|
+
return pickString(deltaObject.thinking) ?? pickString(deltaObject.text) ?? "";
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
const contentBlock = payload.content_block;
|
|
3393
|
+
if (isRecord2(contentBlock)) {
|
|
3394
|
+
const type = pickString(contentBlock.type) ?? "";
|
|
3395
|
+
if (type === "thinking" || type === "reasoning") {
|
|
3396
|
+
return pickString(contentBlock.thinking) ?? pickString(contentBlock.text) ?? "";
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
return "";
|
|
3400
|
+
}
|
|
3401
|
+
function pushReasoningBlock2(blocks, turnIndex, text) {
|
|
3402
|
+
const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
|
|
3403
|
+
if (!clean) {
|
|
3404
|
+
return;
|
|
3405
|
+
}
|
|
3406
|
+
blocks.push({ turnIndex, text: clean });
|
|
3407
|
+
}
|
|
3408
|
+
function joinReasoningBlocks2(blocks) {
|
|
3409
|
+
return blocks.map((block) => block.text).filter(Boolean).join(`
|
|
3410
|
+
|
|
3411
|
+
`);
|
|
3412
|
+
}
|
|
3345
3413
|
function collectAnthropicStreamToolCalls(payload, state) {
|
|
3346
3414
|
const eventType = pickString(payload.type);
|
|
3347
3415
|
if (!eventType) {
|
|
@@ -3568,39 +3636,10 @@ function buildProviderOptions(config) {
|
|
|
3568
3636
|
return {
|
|
3569
3637
|
model: config.model,
|
|
3570
3638
|
...transport,
|
|
3571
|
-
...config.options
|
|
3639
|
+
...config.options
|
|
3572
3640
|
};
|
|
3573
3641
|
}
|
|
3574
3642
|
|
|
3575
|
-
// src/utils/debug-colors.ts
|
|
3576
|
-
var ANSI = {
|
|
3577
|
-
reset: "\x1B[0m",
|
|
3578
|
-
bold: "\x1B[1m",
|
|
3579
|
-
cyan: "\x1B[36m",
|
|
3580
|
-
yellow: "\x1B[33m",
|
|
3581
|
-
green: "\x1B[32m",
|
|
3582
|
-
red: "\x1B[31m",
|
|
3583
|
-
dim: "\x1B[2m"
|
|
3584
|
-
};
|
|
3585
|
-
function color(config, text, tone) {
|
|
3586
|
-
if (!config.colors) {
|
|
3587
|
-
return text;
|
|
3588
|
-
}
|
|
3589
|
-
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
3590
|
-
}
|
|
3591
|
-
function dim(config, text) {
|
|
3592
|
-
if (!config.colors) {
|
|
3593
|
-
return text;
|
|
3594
|
-
}
|
|
3595
|
-
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
3596
|
-
}
|
|
3597
|
-
function title(config, text) {
|
|
3598
|
-
if (!config.colors) {
|
|
3599
|
-
return text;
|
|
3600
|
-
}
|
|
3601
|
-
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
3602
|
-
}
|
|
3603
|
-
|
|
3604
3643
|
// src/outdent.ts
|
|
3605
3644
|
var DEFAULT_OPTIONS = {
|
|
3606
3645
|
trimLeadingNewline: true,
|
|
@@ -3738,131 +3777,213 @@ function createOutdent(options = {}) {
|
|
|
3738
3777
|
return outdent;
|
|
3739
3778
|
}
|
|
3740
3779
|
|
|
3741
|
-
// src/generate-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3780
|
+
// src/generate-tool-timeout.ts
|
|
3781
|
+
function withToolTimeout(client, toolTimeoutMs) {
|
|
3782
|
+
return {
|
|
3783
|
+
id: client.id,
|
|
3784
|
+
listTools: client.listTools.bind(client),
|
|
3785
|
+
close: client.close?.bind(client),
|
|
3786
|
+
async callTool(params) {
|
|
3787
|
+
let timeoutId;
|
|
3788
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
3789
|
+
timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
|
|
3790
|
+
});
|
|
3791
|
+
try {
|
|
3792
|
+
return await Promise.race([client.callTool(params), timeoutPromise]);
|
|
3793
|
+
} finally {
|
|
3794
|
+
clearTimeout(timeoutId);
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
};
|
|
3798
|
+
}
|
|
3799
|
+
function applyToolTimeout(clients, toolTimeoutMs) {
|
|
3800
|
+
return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
|
|
3801
|
+
}
|
|
3802
|
+
// src/generate-output.ts
|
|
3748
3803
|
var RE_THINK_TAGS = /<\/?think\s*>/gi;
|
|
3749
|
-
function
|
|
3750
|
-
const
|
|
3751
|
-
|
|
3804
|
+
function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
|
|
3805
|
+
const sanitized = sanitizeThink(text);
|
|
3806
|
+
const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
|
|
3807
|
+
const reasoning = joinReasoningSegments([
|
|
3808
|
+
dedicatedReasoning,
|
|
3809
|
+
...sanitized.thinkBlocks.map((block) => block.content)
|
|
3810
|
+
]);
|
|
3811
|
+
return {
|
|
3812
|
+
text: visibleText,
|
|
3813
|
+
reasoning,
|
|
3814
|
+
reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
|
|
3815
|
+
thinkBlocks: sanitized.thinkBlocks,
|
|
3816
|
+
parseSource: composeParseSource(visibleText, reasoning)
|
|
3817
|
+
};
|
|
3752
3818
|
}
|
|
3753
|
-
function
|
|
3754
|
-
if (
|
|
3755
|
-
return
|
|
3756
|
-
prompt: value
|
|
3757
|
-
};
|
|
3758
|
-
}
|
|
3759
|
-
if (isPromptResolver(value)) {
|
|
3760
|
-
return normalizePromptPayload(value.resolvePrompt(_context));
|
|
3819
|
+
function normalizeReasoningBlocks(blocks) {
|
|
3820
|
+
if (!Array.isArray(blocks)) {
|
|
3821
|
+
return;
|
|
3761
3822
|
}
|
|
3762
|
-
|
|
3823
|
+
const normalized = blocks.map((block) => ({
|
|
3824
|
+
turnIndex: block.turnIndex,
|
|
3825
|
+
text: block.text.replace(RE_THINK_TAGS, "").trim()
|
|
3826
|
+
})).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
|
|
3827
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
3763
3828
|
}
|
|
3764
|
-
function
|
|
3765
|
-
const
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
|
|
3829
|
+
function appendReasoningBlock(blocks, transition) {
|
|
3830
|
+
const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
|
|
3831
|
+
if (!text) {
|
|
3832
|
+
return blocks;
|
|
3769
3833
|
}
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
|
|
3773
|
-
messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
|
|
3774
|
-
};
|
|
3834
|
+
const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
|
|
3835
|
+
return normalizeReasoningBlocks(next);
|
|
3775
3836
|
}
|
|
3776
|
-
function
|
|
3777
|
-
if (
|
|
3778
|
-
return
|
|
3837
|
+
function composeParseSource(text, reasoning) {
|
|
3838
|
+
if (typeof reasoning !== "string" || reasoning.length === 0) {
|
|
3839
|
+
return text;
|
|
3779
3840
|
}
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
|
|
3786
|
-
}))
|
|
3787
|
-
};
|
|
3841
|
+
const sanitized = reasoning.replace(RE_THINK_TAGS, "");
|
|
3842
|
+
if (sanitized.length === 0) {
|
|
3843
|
+
return text;
|
|
3844
|
+
}
|
|
3845
|
+
return `<think>${sanitized}</think>${text}`;
|
|
3788
3846
|
}
|
|
3789
|
-
function
|
|
3790
|
-
|
|
3791
|
-
|
|
3847
|
+
function aggregateUsage(attempts) {
|
|
3848
|
+
let usage;
|
|
3849
|
+
for (const attempt of attempts) {
|
|
3850
|
+
usage = mergeUsage2(usage, attempt.usage);
|
|
3792
3851
|
}
|
|
3793
|
-
return
|
|
3852
|
+
return usage;
|
|
3794
3853
|
}
|
|
3795
|
-
function
|
|
3796
|
-
|
|
3797
|
-
if (prompts.length === 0) {
|
|
3854
|
+
function mergeUsage2(base, next) {
|
|
3855
|
+
if (!base && !next) {
|
|
3798
3856
|
return;
|
|
3799
3857
|
}
|
|
3800
|
-
return
|
|
3858
|
+
return {
|
|
3859
|
+
inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
|
|
3860
|
+
outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
|
|
3861
|
+
totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
|
|
3862
|
+
cost: (base?.cost ?? 0) + (next?.cost ?? 0)
|
|
3863
|
+
};
|
|
3864
|
+
}
|
|
3865
|
+
function joinReasoningSegments(parts) {
|
|
3866
|
+
return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
|
|
3801
3867
|
|
|
3802
3868
|
`);
|
|
3803
3869
|
}
|
|
3804
|
-
function
|
|
3805
|
-
if (
|
|
3806
|
-
return
|
|
3807
|
-
enabled: option
|
|
3808
|
-
};
|
|
3870
|
+
function stripThinkBlocks(text, thinkBlocks) {
|
|
3871
|
+
if (thinkBlocks.length === 0) {
|
|
3872
|
+
return text;
|
|
3809
3873
|
}
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3874
|
+
let output = "";
|
|
3875
|
+
let cursor = 0;
|
|
3876
|
+
for (const block of thinkBlocks) {
|
|
3877
|
+
output += text.slice(cursor, block.start);
|
|
3878
|
+
cursor = block.end;
|
|
3814
3879
|
}
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
onData: option.onData,
|
|
3818
|
-
to: option.to
|
|
3819
|
-
};
|
|
3880
|
+
output += text.slice(cursor);
|
|
3881
|
+
return output;
|
|
3820
3882
|
}
|
|
3821
|
-
function
|
|
3822
|
-
|
|
3823
|
-
return
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
verbose: false,
|
|
3827
|
-
logger: (line) => console.log(line)
|
|
3828
|
-
};
|
|
3883
|
+
function toStreamDataFingerprint(value) {
|
|
3884
|
+
try {
|
|
3885
|
+
return JSON.stringify(value);
|
|
3886
|
+
} catch {
|
|
3887
|
+
return "__unserializable__";
|
|
3829
3888
|
}
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
// src/utils/debug-colors.ts
|
|
3892
|
+
var ANSI = {
|
|
3893
|
+
reset: "\x1B[0m",
|
|
3894
|
+
bold: "\x1B[1m",
|
|
3895
|
+
cyan: "\x1B[36m",
|
|
3896
|
+
yellow: "\x1B[33m",
|
|
3897
|
+
green: "\x1B[32m",
|
|
3898
|
+
red: "\x1B[31m",
|
|
3899
|
+
dim: "\x1B[2m"
|
|
3900
|
+
};
|
|
3901
|
+
function color(config, text, tone) {
|
|
3902
|
+
if (!config.colors) {
|
|
3903
|
+
return text;
|
|
3837
3904
|
}
|
|
3838
|
-
return {
|
|
3839
|
-
enabled: option.enabled ?? true,
|
|
3840
|
-
colors: option.colors ?? true,
|
|
3841
|
-
verbose: option.verbose ?? false,
|
|
3842
|
-
logger: option.logger ?? ((line) => console.log(line))
|
|
3843
|
-
};
|
|
3905
|
+
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
3844
3906
|
}
|
|
3845
|
-
function
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
async callTool(params) {
|
|
3851
|
-
let timeoutId;
|
|
3852
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
3853
|
-
timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
|
|
3854
|
-
});
|
|
3855
|
-
try {
|
|
3856
|
-
return await Promise.race([client.callTool(params), timeoutPromise]);
|
|
3857
|
-
} finally {
|
|
3858
|
-
clearTimeout(timeoutId);
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
};
|
|
3907
|
+
function dim(config, text) {
|
|
3908
|
+
if (!config.colors) {
|
|
3909
|
+
return text;
|
|
3910
|
+
}
|
|
3911
|
+
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
3862
3912
|
}
|
|
3863
|
-
function
|
|
3864
|
-
|
|
3913
|
+
function title(config, text) {
|
|
3914
|
+
if (!config.colors) {
|
|
3915
|
+
return text;
|
|
3916
|
+
}
|
|
3917
|
+
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
3918
|
+
}
|
|
3919
|
+
|
|
3920
|
+
// src/generate-debug.ts
|
|
3921
|
+
function emitDebugRequest(config, input) {
|
|
3922
|
+
const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
|
|
3923
|
+
const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
|
|
3924
|
+
const lines = [
|
|
3925
|
+
color(config, title(config, [
|
|
3926
|
+
`[${input.label}][request]`,
|
|
3927
|
+
`attempt=${input.attempt}`,
|
|
3928
|
+
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
3929
|
+
`selfHealAttempt=${input.selfHealAttempt}`
|
|
3930
|
+
].join(" ")), "cyan"),
|
|
3931
|
+
dim(config, [
|
|
3932
|
+
`provider=${input.provider ?? "unknown"}`,
|
|
3933
|
+
`model=${input.model ?? "unknown"}`,
|
|
3934
|
+
`stream=${input.stream}`
|
|
3935
|
+
].join(" ")),
|
|
3936
|
+
color(config, "prompt:", "yellow"),
|
|
3937
|
+
input.requestPayload.prompt ?? "(none)",
|
|
3938
|
+
color(config, "messages:", "yellow"),
|
|
3939
|
+
requestMessages,
|
|
3940
|
+
color(config, "systemPrompt:", "yellow"),
|
|
3941
|
+
input.requestPayload.systemPrompt ?? "(none)",
|
|
3942
|
+
color(config, "request.body:", "yellow"),
|
|
3943
|
+
requestBody
|
|
3944
|
+
];
|
|
3945
|
+
emitDebug(config, lines.join(`
|
|
3946
|
+
`));
|
|
3947
|
+
}
|
|
3948
|
+
function emitDebugResponse(config, input) {
|
|
3949
|
+
const text = input.text.length > 0 ? input.text : "(none)";
|
|
3950
|
+
const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
|
|
3951
|
+
const metadata = [
|
|
3952
|
+
`via=${input.via}`,
|
|
3953
|
+
`textChars=${input.text.length}`,
|
|
3954
|
+
`reasoningChars=${input.reasoning.length}`
|
|
3955
|
+
];
|
|
3956
|
+
if (config.verbose) {
|
|
3957
|
+
metadata.push(`parseSourceChars=${input.parseSource.length}`);
|
|
3958
|
+
}
|
|
3959
|
+
metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
|
|
3960
|
+
const lines = [
|
|
3961
|
+
color(config, title(config, [
|
|
3962
|
+
`[${input.label}][response]`,
|
|
3963
|
+
`attempt=${input.attempt}`,
|
|
3964
|
+
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
3965
|
+
`selfHealAttempt=${input.selfHealAttempt}`
|
|
3966
|
+
].join(" ")), "green"),
|
|
3967
|
+
dim(config, metadata.join(" ")),
|
|
3968
|
+
color(config, "text:", "yellow"),
|
|
3969
|
+
text,
|
|
3970
|
+
color(config, "reasoning:", "yellow"),
|
|
3971
|
+
reasoning
|
|
3972
|
+
];
|
|
3973
|
+
if (config.verbose) {
|
|
3974
|
+
lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
|
|
3975
|
+
}
|
|
3976
|
+
emitDebug(config, lines.join(`
|
|
3977
|
+
`));
|
|
3865
3978
|
}
|
|
3979
|
+
function emitDebug(config, message) {
|
|
3980
|
+
if (!config.enabled) {
|
|
3981
|
+
return;
|
|
3982
|
+
}
|
|
3983
|
+
config.logger(message);
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
// src/generate-model-call.ts
|
|
3866
3987
|
async function callModel(adapter, options) {
|
|
3867
3988
|
const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
|
|
3868
3989
|
const requestPayload = {
|
|
@@ -3882,6 +4003,7 @@ async function callModel(adapter, options) {
|
|
|
3882
4003
|
transformToolCallParams: options.request?.transformToolCallParams,
|
|
3883
4004
|
unknownToolError: options.request?.unknownToolError,
|
|
3884
4005
|
toolDebug: options.request?.toolDebug,
|
|
4006
|
+
onTurnTransition: options.stream.onTurnTransition,
|
|
3885
4007
|
body: options.request?.body,
|
|
3886
4008
|
signal: requestSignal
|
|
3887
4009
|
};
|
|
@@ -3909,13 +4031,21 @@ async function callModel(adapter, options) {
|
|
|
3909
4031
|
let latestFinishReason;
|
|
3910
4032
|
let streamedProviderText = "";
|
|
3911
4033
|
let streamedDedicatedReasoning = "";
|
|
4034
|
+
let currentTurnIndex;
|
|
4035
|
+
let currentToolCalls;
|
|
4036
|
+
let streamedReasoningBlocks;
|
|
3912
4037
|
let lastSnapshotFingerprint;
|
|
3913
4038
|
let previousSnapshotText = "";
|
|
3914
4039
|
let previousSnapshotReasoning = "";
|
|
3915
4040
|
const emitStreamingData = (done, usage2, finishReason2) => {
|
|
3916
|
-
const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
|
|
4041
|
+
const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
|
|
3917
4042
|
const snapshot = options.buildSnapshot(normalized2);
|
|
3918
|
-
const fingerprint = toStreamDataFingerprint(
|
|
4043
|
+
const fingerprint = toStreamDataFingerprint({
|
|
4044
|
+
snapshot,
|
|
4045
|
+
done,
|
|
4046
|
+
turnIndex: currentTurnIndex,
|
|
4047
|
+
toolCalls: currentToolCalls
|
|
4048
|
+
});
|
|
3919
4049
|
if (!done && fingerprint === lastSnapshotFingerprint) {
|
|
3920
4050
|
return;
|
|
3921
4051
|
}
|
|
@@ -3931,7 +4061,9 @@ async function callModel(adapter, options) {
|
|
|
3931
4061
|
snapshot,
|
|
3932
4062
|
done,
|
|
3933
4063
|
usage: usage2,
|
|
3934
|
-
finishReason: finishReason2
|
|
4064
|
+
finishReason: finishReason2,
|
|
4065
|
+
turnIndex: currentTurnIndex,
|
|
4066
|
+
toolCalls: currentToolCalls
|
|
3935
4067
|
});
|
|
3936
4068
|
if (options.stream.to === "stdout" && delta.text) {
|
|
3937
4069
|
process.stdout.write(delta.text);
|
|
@@ -3966,8 +4098,21 @@ async function callModel(adapter, options) {
|
|
|
3966
4098
|
streamedDedicatedReasoning += delta;
|
|
3967
4099
|
emitStreamingData(false);
|
|
3968
4100
|
};
|
|
3969
|
-
const
|
|
4101
|
+
const streamRequestPayload = {
|
|
4102
|
+
...requestPayload,
|
|
4103
|
+
onTurnTransition: (transition) => {
|
|
4104
|
+
if (transition.kind === "reasoningComplete") {
|
|
4105
|
+
streamedReasoningBlocks = appendReasoningBlock(streamedReasoningBlocks, transition);
|
|
4106
|
+
}
|
|
4107
|
+
options.stream.onTurnTransition?.(transition);
|
|
4108
|
+
}
|
|
4109
|
+
};
|
|
4110
|
+
const response2 = await adapter.stream(streamRequestPayload, {
|
|
3970
4111
|
onChunk: (chunk) => {
|
|
4112
|
+
if (chunk.turnIndex !== undefined) {
|
|
4113
|
+
currentTurnIndex = chunk.turnIndex;
|
|
4114
|
+
}
|
|
4115
|
+
currentToolCalls = chunk.toolCalls;
|
|
3971
4116
|
if (chunk.textDelta) {
|
|
3972
4117
|
handleTextDelta(chunk.textDelta);
|
|
3973
4118
|
}
|
|
@@ -3980,11 +4125,15 @@ async function callModel(adapter, options) {
|
|
|
3980
4125
|
if (chunk.finishReason) {
|
|
3981
4126
|
latestFinishReason = chunk.finishReason;
|
|
3982
4127
|
}
|
|
4128
|
+
if (!chunk.textDelta && !chunk.reasoningDelta && (chunk.turnIndex !== undefined || chunk.toolCalls)) {
|
|
4129
|
+
emitStreamingData(false, chunk.usage, chunk.finishReason);
|
|
4130
|
+
}
|
|
3983
4131
|
}
|
|
3984
4132
|
});
|
|
3985
4133
|
streamedProviderText = typeof response2.text === "string" ? response2.text : streamedProviderText;
|
|
3986
4134
|
streamedDedicatedReasoning = typeof response2.reasoning === "string" ? response2.reasoning : streamedDedicatedReasoning;
|
|
3987
|
-
|
|
4135
|
+
streamedReasoningBlocks = response2.reasoningBlocks ?? streamedReasoningBlocks;
|
|
4136
|
+
const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
|
|
3988
4137
|
const usage = preferLatestUsage(latestUsage, response2.usage);
|
|
3989
4138
|
const finishReason = response2.finishReason ?? latestFinishReason;
|
|
3990
4139
|
emitStreamingData(true, usage, finishReason);
|
|
@@ -4016,11 +4165,12 @@ async function callModel(adapter, options) {
|
|
|
4016
4165
|
parseSource: finalNormalized.parseSource,
|
|
4017
4166
|
via: "stream",
|
|
4018
4167
|
usage,
|
|
4019
|
-
finishReason
|
|
4168
|
+
finishReason,
|
|
4169
|
+
reasoningBlocks: finalNormalized.reasoningBlocks
|
|
4020
4170
|
};
|
|
4021
4171
|
}
|
|
4022
4172
|
const response = await adapter.complete(requestPayload);
|
|
4023
|
-
const normalized = normalizeModelOutput(response.text, response.reasoning);
|
|
4173
|
+
const normalized = normalizeModelOutput(response.text, response.reasoning, response.reasoningBlocks);
|
|
4024
4174
|
options.observe?.(options.buildEvent({
|
|
4025
4175
|
stage: "llm.response",
|
|
4026
4176
|
message: "Completion response received.",
|
|
@@ -4049,51 +4199,119 @@ async function callModel(adapter, options) {
|
|
|
4049
4199
|
parseSource: normalized.parseSource,
|
|
4050
4200
|
via: "complete",
|
|
4051
4201
|
usage: response.usage,
|
|
4052
|
-
finishReason: response.finishReason
|
|
4202
|
+
finishReason: response.finishReason,
|
|
4203
|
+
reasoningBlocks: normalized.reasoningBlocks
|
|
4053
4204
|
};
|
|
4054
4205
|
}
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4206
|
+
|
|
4207
|
+
// src/generate-shared.ts
|
|
4208
|
+
var sharedOutdent = createOutdent({
|
|
4209
|
+
trimLeadingNewline: true,
|
|
4210
|
+
trimTrailingNewline: true,
|
|
4211
|
+
newline: `
|
|
4212
|
+
`
|
|
4213
|
+
});
|
|
4214
|
+
function resolvePrompt(prompt, context) {
|
|
4215
|
+
const resolved = typeof prompt === "function" ? prompt(context) : prompt;
|
|
4216
|
+
return normalizePromptValue(resolved, context);
|
|
4217
|
+
}
|
|
4218
|
+
function normalizePromptValue(value, _context) {
|
|
4219
|
+
if (typeof value === "string") {
|
|
4220
|
+
return {
|
|
4221
|
+
prompt: value
|
|
4222
|
+
};
|
|
4223
|
+
}
|
|
4224
|
+
if (isPromptResolver(value)) {
|
|
4225
|
+
return normalizePromptPayload(value.resolvePrompt(_context));
|
|
4226
|
+
}
|
|
4227
|
+
return normalizePromptPayload(value);
|
|
4228
|
+
}
|
|
4229
|
+
function normalizePromptPayload(value) {
|
|
4230
|
+
const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
|
|
4231
|
+
const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
|
|
4232
|
+
if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
|
|
4233
|
+
throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
|
|
4234
|
+
}
|
|
4062
4235
|
return {
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
parseSource: composeParseSource(visibleText, reasoning)
|
|
4236
|
+
prompt,
|
|
4237
|
+
systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
|
|
4238
|
+
messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
|
|
4067
4239
|
};
|
|
4068
4240
|
}
|
|
4069
|
-
function
|
|
4070
|
-
if (
|
|
4071
|
-
return
|
|
4241
|
+
function applyPromptOutdent(payload, enabled) {
|
|
4242
|
+
if (!enabled) {
|
|
4243
|
+
return payload;
|
|
4072
4244
|
}
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4245
|
+
return {
|
|
4246
|
+
prompt: typeof payload.prompt === "string" ? sharedOutdent.string(payload.prompt) : undefined,
|
|
4247
|
+
systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
|
|
4248
|
+
messages: payload.messages?.map((message) => ({
|
|
4249
|
+
...message,
|
|
4250
|
+
content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
|
|
4251
|
+
}))
|
|
4252
|
+
};
|
|
4253
|
+
}
|
|
4254
|
+
function applyOutdentToOptionalPrompt(value, enabled) {
|
|
4255
|
+
if (!enabled || typeof value !== "string") {
|
|
4256
|
+
return value;
|
|
4076
4257
|
}
|
|
4077
|
-
return
|
|
4258
|
+
return sharedOutdent.string(value);
|
|
4078
4259
|
}
|
|
4079
|
-
function
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4260
|
+
function mergeSystemPrompts(primary, secondary) {
|
|
4261
|
+
const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
4262
|
+
if (prompts.length === 0) {
|
|
4263
|
+
return;
|
|
4264
|
+
}
|
|
4265
|
+
return prompts.join(`
|
|
4266
|
+
|
|
4267
|
+
`);
|
|
4268
|
+
}
|
|
4269
|
+
function normalizeStreamConfig(option) {
|
|
4270
|
+
if (typeof option === "boolean") {
|
|
4271
|
+
return {
|
|
4272
|
+
enabled: option
|
|
4273
|
+
};
|
|
4274
|
+
}
|
|
4275
|
+
if (!option) {
|
|
4276
|
+
return {
|
|
4277
|
+
enabled: false
|
|
4278
|
+
};
|
|
4083
4279
|
}
|
|
4084
|
-
return
|
|
4280
|
+
return {
|
|
4281
|
+
enabled: option.enabled ?? true,
|
|
4282
|
+
onData: option.onData,
|
|
4283
|
+
onTurnTransition: option.onTurnTransition,
|
|
4284
|
+
to: option.to
|
|
4285
|
+
};
|
|
4085
4286
|
}
|
|
4086
|
-
function
|
|
4087
|
-
if (
|
|
4088
|
-
return
|
|
4287
|
+
function normalizeDebugConfig(option) {
|
|
4288
|
+
if (typeof option === "boolean") {
|
|
4289
|
+
return {
|
|
4290
|
+
enabled: option,
|
|
4291
|
+
colors: true,
|
|
4292
|
+
verbose: false,
|
|
4293
|
+
logger: defaultDebugLogger
|
|
4294
|
+
};
|
|
4295
|
+
}
|
|
4296
|
+
if (!option) {
|
|
4297
|
+
return {
|
|
4298
|
+
enabled: false,
|
|
4299
|
+
colors: true,
|
|
4300
|
+
verbose: false,
|
|
4301
|
+
logger: defaultDebugLogger
|
|
4302
|
+
};
|
|
4089
4303
|
}
|
|
4090
4304
|
return {
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4305
|
+
enabled: option.enabled ?? true,
|
|
4306
|
+
colors: option.colors ?? true,
|
|
4307
|
+
verbose: option.verbose ?? false,
|
|
4308
|
+
logger: option.logger ?? defaultDebugLogger
|
|
4095
4309
|
};
|
|
4096
4310
|
}
|
|
4311
|
+
function defaultDebugLogger(line) {
|
|
4312
|
+
const { log } = globalThis.console;
|
|
4313
|
+
log(line);
|
|
4314
|
+
}
|
|
4097
4315
|
function isPromptResolver(value) {
|
|
4098
4316
|
return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
|
|
4099
4317
|
}
|
|
@@ -4107,95 +4325,6 @@ function isLLMMessage(value) {
|
|
|
4107
4325
|
}
|
|
4108
4326
|
return "content" in candidate;
|
|
4109
4327
|
}
|
|
4110
|
-
function joinReasoningSegments(parts) {
|
|
4111
|
-
return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
|
|
4112
|
-
|
|
4113
|
-
`);
|
|
4114
|
-
}
|
|
4115
|
-
function stripThinkBlocks(text, thinkBlocks) {
|
|
4116
|
-
if (thinkBlocks.length === 0) {
|
|
4117
|
-
return text;
|
|
4118
|
-
}
|
|
4119
|
-
let output = "";
|
|
4120
|
-
let cursor = 0;
|
|
4121
|
-
for (const block of thinkBlocks) {
|
|
4122
|
-
output += text.slice(cursor, block.start);
|
|
4123
|
-
cursor = block.end;
|
|
4124
|
-
}
|
|
4125
|
-
output += text.slice(cursor);
|
|
4126
|
-
return output;
|
|
4127
|
-
}
|
|
4128
|
-
function toStreamDataFingerprint(value) {
|
|
4129
|
-
try {
|
|
4130
|
-
return JSON.stringify(value);
|
|
4131
|
-
} catch {
|
|
4132
|
-
return "__unserializable__";
|
|
4133
|
-
}
|
|
4134
|
-
}
|
|
4135
|
-
function emitDebugRequest(config, input) {
|
|
4136
|
-
const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
|
|
4137
|
-
const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
|
|
4138
|
-
const lines = [
|
|
4139
|
-
color(config, title(config, [
|
|
4140
|
-
`[${input.label}][request]`,
|
|
4141
|
-
`attempt=${input.attempt}`,
|
|
4142
|
-
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
4143
|
-
`selfHealAttempt=${input.selfHealAttempt}`
|
|
4144
|
-
].join(" ")), "cyan"),
|
|
4145
|
-
dim(config, [
|
|
4146
|
-
`provider=${input.provider ?? "unknown"}`,
|
|
4147
|
-
`model=${input.model ?? "unknown"}`,
|
|
4148
|
-
`stream=${input.stream}`
|
|
4149
|
-
].join(" ")),
|
|
4150
|
-
color(config, "prompt:", "yellow"),
|
|
4151
|
-
input.requestPayload.prompt ?? "(none)",
|
|
4152
|
-
color(config, "messages:", "yellow"),
|
|
4153
|
-
requestMessages,
|
|
4154
|
-
color(config, "systemPrompt:", "yellow"),
|
|
4155
|
-
input.requestPayload.systemPrompt ?? "(none)",
|
|
4156
|
-
color(config, "request.body:", "yellow"),
|
|
4157
|
-
requestBody
|
|
4158
|
-
];
|
|
4159
|
-
emitDebug(config, lines.join(`
|
|
4160
|
-
`));
|
|
4161
|
-
}
|
|
4162
|
-
function emitDebugResponse(config, input) {
|
|
4163
|
-
const text = input.text.length > 0 ? input.text : "(none)";
|
|
4164
|
-
const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
|
|
4165
|
-
const metadata = [
|
|
4166
|
-
`via=${input.via}`,
|
|
4167
|
-
`textChars=${input.text.length}`,
|
|
4168
|
-
`reasoningChars=${input.reasoning.length}`
|
|
4169
|
-
];
|
|
4170
|
-
if (config.verbose) {
|
|
4171
|
-
metadata.push(`parseSourceChars=${input.parseSource.length}`);
|
|
4172
|
-
}
|
|
4173
|
-
metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
|
|
4174
|
-
const lines = [
|
|
4175
|
-
color(config, title(config, [
|
|
4176
|
-
`[${input.label}][response]`,
|
|
4177
|
-
`attempt=${input.attempt}`,
|
|
4178
|
-
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
4179
|
-
`selfHealAttempt=${input.selfHealAttempt}`
|
|
4180
|
-
].join(" ")), "green"),
|
|
4181
|
-
dim(config, metadata.join(" ")),
|
|
4182
|
-
color(config, "text:", "yellow"),
|
|
4183
|
-
text,
|
|
4184
|
-
color(config, "reasoning:", "yellow"),
|
|
4185
|
-
reasoning
|
|
4186
|
-
];
|
|
4187
|
-
if (config.verbose) {
|
|
4188
|
-
lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
|
|
4189
|
-
}
|
|
4190
|
-
emitDebug(config, lines.join(`
|
|
4191
|
-
`));
|
|
4192
|
-
}
|
|
4193
|
-
function emitDebug(config, message) {
|
|
4194
|
-
if (!config.enabled) {
|
|
4195
|
-
return;
|
|
4196
|
-
}
|
|
4197
|
-
config.logger(message);
|
|
4198
|
-
}
|
|
4199
4328
|
|
|
4200
4329
|
// src/generate.ts
|
|
4201
4330
|
async function generate(adapter, promptOrOptions, callOptions) {
|
|
@@ -4225,7 +4354,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4225
4354
|
}),
|
|
4226
4355
|
buildSnapshot: (model) => ({
|
|
4227
4356
|
text: model.text,
|
|
4228
|
-
reasoning: model.reasoning
|
|
4357
|
+
reasoning: model.reasoning,
|
|
4358
|
+
...model.reasoningBlocks ? { reasoningBlocks: model.reasoningBlocks } : {}
|
|
4229
4359
|
}),
|
|
4230
4360
|
debug: debugConfig,
|
|
4231
4361
|
debugLabel: "generate",
|
|
@@ -4240,7 +4370,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4240
4370
|
text: response.text,
|
|
4241
4371
|
reasoning: response.reasoning,
|
|
4242
4372
|
usage: response.usage,
|
|
4243
|
-
finishReason: response.finishReason
|
|
4373
|
+
finishReason: response.finishReason,
|
|
4374
|
+
...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {}
|
|
4244
4375
|
};
|
|
4245
4376
|
const attempts = [attempt];
|
|
4246
4377
|
normalized.observe?.({
|
|
@@ -4257,7 +4388,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4257
4388
|
reasoning: attempt.reasoning,
|
|
4258
4389
|
attempts,
|
|
4259
4390
|
usage: aggregateUsage(attempts),
|
|
4260
|
-
finishReason: attempt.finishReason
|
|
4391
|
+
finishReason: attempt.finishReason,
|
|
4392
|
+
...attempt.reasoningBlocks ? { reasoningBlocks: attempt.reasoningBlocks } : {}
|
|
4261
4393
|
};
|
|
4262
4394
|
}
|
|
4263
4395
|
function normalizeGenerateInput(promptOrOptions, callOptions) {
|
|
@@ -4268,7 +4400,7 @@ function normalizeGenerateInput(promptOrOptions, callOptions) {
|
|
|
4268
4400
|
throw new Error("Missing prompt in generate(adapter, prompt, options?) call.");
|
|
4269
4401
|
}
|
|
4270
4402
|
return {
|
|
4271
|
-
...callOptions
|
|
4403
|
+
...callOptions,
|
|
4272
4404
|
prompt: promptOrOptions
|
|
4273
4405
|
};
|
|
4274
4406
|
}
|
|
@@ -4294,9 +4426,6 @@ function prepareGeneratePromptPayload(payload, systemPrompt) {
|
|
|
4294
4426
|
};
|
|
4295
4427
|
}
|
|
4296
4428
|
|
|
4297
|
-
// src/structured.ts
|
|
4298
|
-
var import_jsonrepair3 = require("jsonrepair");
|
|
4299
|
-
|
|
4300
4429
|
// src/parse.ts
|
|
4301
4430
|
var import_jsonrepair2 = require("jsonrepair");
|
|
4302
4431
|
function parseLLMOutput(output, schema, options = {}) {
|
|
@@ -4596,6 +4725,80 @@ function formatZodIssues(issues) {
|
|
|
4596
4725
|
`);
|
|
4597
4726
|
}
|
|
4598
4727
|
|
|
4728
|
+
// src/structured-streaming.ts
|
|
4729
|
+
var import_jsonrepair3 = require("jsonrepair");
|
|
4730
|
+
function parseStreamingStructuredData(parseSource) {
|
|
4731
|
+
const sanitized = sanitizeThink(parseSource);
|
|
4732
|
+
const start = findFirstJsonRootStart(sanitized.visibleText);
|
|
4733
|
+
if (start < 0) {
|
|
4734
|
+
return null;
|
|
4735
|
+
}
|
|
4736
|
+
const candidate = sanitized.visibleText.slice(start).trim();
|
|
4737
|
+
if (!candidate) {
|
|
4738
|
+
return null;
|
|
4739
|
+
}
|
|
4740
|
+
try {
|
|
4741
|
+
const repaired = import_jsonrepair3.jsonrepair(candidate);
|
|
4742
|
+
const parsed = JSON.parse(repaired);
|
|
4743
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
4744
|
+
return null;
|
|
4745
|
+
}
|
|
4746
|
+
return parsed;
|
|
4747
|
+
} catch {
|
|
4748
|
+
return null;
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
function findFirstJsonRootStart(input) {
|
|
4752
|
+
const unquotedRootStart = findFirstUnquotedJsonRootStart(input);
|
|
4753
|
+
if (unquotedRootStart >= 0) {
|
|
4754
|
+
return unquotedRootStart;
|
|
4755
|
+
}
|
|
4756
|
+
return findFirstRawJsonRootStart(input);
|
|
4757
|
+
}
|
|
4758
|
+
function findFirstUnquotedJsonRootStart(input) {
|
|
4759
|
+
let inString = false;
|
|
4760
|
+
let escaped = false;
|
|
4761
|
+
for (let index = 0;index < input.length; index += 1) {
|
|
4762
|
+
const char = input[index];
|
|
4763
|
+
if (!char) {
|
|
4764
|
+
continue;
|
|
4765
|
+
}
|
|
4766
|
+
if (inString) {
|
|
4767
|
+
if (escaped) {
|
|
4768
|
+
escaped = false;
|
|
4769
|
+
continue;
|
|
4770
|
+
}
|
|
4771
|
+
if (char === "\\") {
|
|
4772
|
+
escaped = true;
|
|
4773
|
+
continue;
|
|
4774
|
+
}
|
|
4775
|
+
if (char === '"') {
|
|
4776
|
+
inString = false;
|
|
4777
|
+
}
|
|
4778
|
+
continue;
|
|
4779
|
+
}
|
|
4780
|
+
if (char === '"') {
|
|
4781
|
+
inString = true;
|
|
4782
|
+
continue;
|
|
4783
|
+
}
|
|
4784
|
+
if (char === "{" || char === "[") {
|
|
4785
|
+
return index;
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
return -1;
|
|
4789
|
+
}
|
|
4790
|
+
function findFirstRawJsonRootStart(input) {
|
|
4791
|
+
const objectStart = input.indexOf("{");
|
|
4792
|
+
const arrayStart = input.indexOf("[");
|
|
4793
|
+
if (objectStart < 0) {
|
|
4794
|
+
return arrayStart;
|
|
4795
|
+
}
|
|
4796
|
+
if (arrayStart < 0) {
|
|
4797
|
+
return objectStart;
|
|
4798
|
+
}
|
|
4799
|
+
return Math.min(objectStart, arrayStart);
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4599
4802
|
// src/structured.ts
|
|
4600
4803
|
class StructuredParseError extends Error {
|
|
4601
4804
|
name = "StructuredParseError";
|
|
@@ -4833,7 +5036,7 @@ function normalizeStructuredInput(schemaOrOptions, promptInput, callOptions) {
|
|
|
4833
5036
|
throw new Error("Missing prompt in structured(adapter, schema, prompt, options?) call.");
|
|
4834
5037
|
}
|
|
4835
5038
|
return {
|
|
4836
|
-
...callOptions
|
|
5039
|
+
...callOptions,
|
|
4837
5040
|
schema: schemaOrOptions,
|
|
4838
5041
|
prompt: promptInput
|
|
4839
5042
|
};
|
|
@@ -5077,6 +5280,7 @@ async function executeAttempt(adapter, input) {
|
|
|
5077
5280
|
success: parsed.success,
|
|
5078
5281
|
usage: response.usage,
|
|
5079
5282
|
finishReason: response.finishReason,
|
|
5283
|
+
...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {},
|
|
5080
5284
|
parsed
|
|
5081
5285
|
};
|
|
5082
5286
|
return {
|
|
@@ -5097,72 +5301,12 @@ async function callModel2(adapter, options) {
|
|
|
5097
5301
|
buildSnapshot: (normalized) => ({
|
|
5098
5302
|
text: normalized.text,
|
|
5099
5303
|
reasoning: normalized.reasoning,
|
|
5304
|
+
...normalized.reasoningBlocks ? { reasoningBlocks: normalized.reasoningBlocks } : {},
|
|
5100
5305
|
data: parseStreamingStructuredData(normalized.parseSource) ?? null
|
|
5101
5306
|
}),
|
|
5102
5307
|
debugLabel: "structured"
|
|
5103
5308
|
});
|
|
5104
5309
|
}
|
|
5105
|
-
function parseStreamingStructuredData(parseSource) {
|
|
5106
|
-
const sanitized = sanitizeThink(parseSource);
|
|
5107
|
-
const start = findFirstJsonRootStart(sanitized.visibleText);
|
|
5108
|
-
if (start < 0) {
|
|
5109
|
-
return null;
|
|
5110
|
-
}
|
|
5111
|
-
const candidate = sanitized.visibleText.slice(start).trim();
|
|
5112
|
-
if (!candidate) {
|
|
5113
|
-
return null;
|
|
5114
|
-
}
|
|
5115
|
-
try {
|
|
5116
|
-
const repaired = import_jsonrepair3.jsonrepair(candidate);
|
|
5117
|
-
const parsed = JSON.parse(repaired);
|
|
5118
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
5119
|
-
return null;
|
|
5120
|
-
}
|
|
5121
|
-
return parsed;
|
|
5122
|
-
} catch {
|
|
5123
|
-
return null;
|
|
5124
|
-
}
|
|
5125
|
-
}
|
|
5126
|
-
function findFirstJsonRootStart(input) {
|
|
5127
|
-
let inString = false;
|
|
5128
|
-
let escaped = false;
|
|
5129
|
-
for (let index = 0;index < input.length; index += 1) {
|
|
5130
|
-
const char = input[index];
|
|
5131
|
-
if (!char) {
|
|
5132
|
-
continue;
|
|
5133
|
-
}
|
|
5134
|
-
if (inString) {
|
|
5135
|
-
if (escaped) {
|
|
5136
|
-
escaped = false;
|
|
5137
|
-
continue;
|
|
5138
|
-
}
|
|
5139
|
-
if (char === "\\") {
|
|
5140
|
-
escaped = true;
|
|
5141
|
-
continue;
|
|
5142
|
-
}
|
|
5143
|
-
if (char === '"') {
|
|
5144
|
-
inString = false;
|
|
5145
|
-
}
|
|
5146
|
-
continue;
|
|
5147
|
-
}
|
|
5148
|
-
if (char === '"') {
|
|
5149
|
-
inString = true;
|
|
5150
|
-
continue;
|
|
5151
|
-
}
|
|
5152
|
-
if (char === "{" || char === "[") {
|
|
5153
|
-
return index;
|
|
5154
|
-
}
|
|
5155
|
-
}
|
|
5156
|
-
const objectStart = input.indexOf("{");
|
|
5157
|
-
const arrayStart = input.indexOf("[");
|
|
5158
|
-
if (objectStart < 0) {
|
|
5159
|
-
return arrayStart;
|
|
5160
|
-
}
|
|
5161
|
-
if (arrayStart < 0) {
|
|
5162
|
-
return objectStart;
|
|
5163
|
-
}
|
|
5164
|
-
return Math.min(objectStart, arrayStart);
|
|
5165
|
-
}
|
|
5166
5310
|
function parseWithObserve(output, schema, parseOptions, context) {
|
|
5167
5311
|
const userParseTrace = parseOptions.onTrace;
|
|
5168
5312
|
return parseLLMOutput(output, schema, {
|
|
@@ -5197,7 +5341,8 @@ function buildSuccessResult(data, attempts) {
|
|
|
5197
5341
|
json: final?.json ?? null,
|
|
5198
5342
|
attempts,
|
|
5199
5343
|
usage: aggregateUsage(attempts),
|
|
5200
|
-
finishReason: final?.finishReason
|
|
5344
|
+
finishReason: final?.finishReason,
|
|
5345
|
+
...final?.reasoningBlocks ? { reasoningBlocks: final.reasoningBlocks } : {}
|
|
5201
5346
|
};
|
|
5202
5347
|
}
|
|
5203
5348
|
function toStructuredError(attempt) {
|
|
@@ -5264,12 +5409,12 @@ function mergeStructuredOptions(defaults, overrides) {
|
|
|
5264
5409
|
...defaults,
|
|
5265
5410
|
...overrides,
|
|
5266
5411
|
parse: {
|
|
5267
|
-
...defaults?.parse
|
|
5268
|
-
...overrides?.parse
|
|
5412
|
+
...defaults?.parse,
|
|
5413
|
+
...overrides?.parse
|
|
5269
5414
|
},
|
|
5270
5415
|
request: {
|
|
5271
|
-
...defaults?.request
|
|
5272
|
-
...overrides?.request
|
|
5416
|
+
...defaults?.request,
|
|
5417
|
+
...overrides?.request
|
|
5273
5418
|
},
|
|
5274
5419
|
stream: mergeObjectLike(defaults?.stream, overrides?.stream),
|
|
5275
5420
|
selfHeal: mergeObjectLike(defaults?.selfHeal, overrides?.selfHeal),
|
|
@@ -5285,8 +5430,8 @@ function mergeGenerateOptions(defaults, overrides) {
|
|
|
5285
5430
|
outdent: overrides?.outdent ?? defaults?.outdent,
|
|
5286
5431
|
systemPrompt: overrides?.systemPrompt ?? defaults?.systemPrompt,
|
|
5287
5432
|
request: {
|
|
5288
|
-
...defaults?.request
|
|
5289
|
-
...overrides?.request
|
|
5433
|
+
...defaults?.request,
|
|
5434
|
+
...overrides?.request
|
|
5290
5435
|
},
|
|
5291
5436
|
stream: mergeObjectLike(defaults?.stream, overrides?.stream),
|
|
5292
5437
|
debug: mergeObjectLike(defaults?.debug, overrides?.debug),
|
|
@@ -5660,11 +5805,11 @@ function inferSchemaExample(schema) {
|
|
|
5660
5805
|
}
|
|
5661
5806
|
function getObjectShape(schema) {
|
|
5662
5807
|
const unwrapped = unwrap2(schema).schema;
|
|
5663
|
-
const typeName = unwrapped.
|
|
5808
|
+
const typeName = unwrapped.def?.type;
|
|
5664
5809
|
if (typeName !== "object") {
|
|
5665
5810
|
return null;
|
|
5666
5811
|
}
|
|
5667
|
-
const rawShape = unwrapped.
|
|
5812
|
+
const rawShape = unwrapped.def?.shape;
|
|
5668
5813
|
if (typeof rawShape === "function") {
|
|
5669
5814
|
return rawShape();
|
|
5670
5815
|
}
|
|
@@ -5672,11 +5817,11 @@ function getObjectShape(schema) {
|
|
|
5672
5817
|
}
|
|
5673
5818
|
function readDefaultValue(schema) {
|
|
5674
5819
|
let current = schema;
|
|
5675
|
-
while (current?.
|
|
5676
|
-
const typeName = current.
|
|
5820
|
+
while (current?.def?.type) {
|
|
5821
|
+
const typeName = current.def.type;
|
|
5677
5822
|
if (typeName === "default") {
|
|
5678
5823
|
try {
|
|
5679
|
-
const raw = current.
|
|
5824
|
+
const raw = current.def.defaultValue;
|
|
5680
5825
|
if (typeof raw === "function") {
|
|
5681
5826
|
return raw();
|
|
5682
5827
|
}
|
|
@@ -5686,11 +5831,11 @@ function readDefaultValue(schema) {
|
|
|
5686
5831
|
}
|
|
5687
5832
|
}
|
|
5688
5833
|
if (typeName === "optional" || typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
|
|
5689
|
-
current = current.
|
|
5834
|
+
current = current.def.innerType ?? current;
|
|
5690
5835
|
continue;
|
|
5691
5836
|
}
|
|
5692
5837
|
if (typeName === "pipe") {
|
|
5693
|
-
current = current.
|
|
5838
|
+
current = current.def.in ?? current;
|
|
5694
5839
|
continue;
|
|
5695
5840
|
}
|
|
5696
5841
|
return;
|
|
@@ -5699,22 +5844,22 @@ function readDefaultValue(schema) {
|
|
|
5699
5844
|
}
|
|
5700
5845
|
function readSchemaDescription2(schema) {
|
|
5701
5846
|
let current = schema;
|
|
5702
|
-
while (current?.
|
|
5847
|
+
while (current?.def?.type) {
|
|
5703
5848
|
const desc = current.description;
|
|
5704
5849
|
if (typeof desc === "string" && desc.trim().length > 0) {
|
|
5705
5850
|
return desc.trim();
|
|
5706
5851
|
}
|
|
5707
|
-
const typeName = current.
|
|
5852
|
+
const typeName = current.def.type;
|
|
5708
5853
|
if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
|
|
5709
|
-
current = current.
|
|
5854
|
+
current = current.def.innerType ?? current;
|
|
5710
5855
|
continue;
|
|
5711
5856
|
}
|
|
5712
5857
|
if (typeName === "catch" || typeName === "readonly") {
|
|
5713
|
-
current = current.
|
|
5858
|
+
current = current.def.innerType ?? current;
|
|
5714
5859
|
continue;
|
|
5715
5860
|
}
|
|
5716
5861
|
if (typeName === "pipe") {
|
|
5717
|
-
current = current.
|
|
5862
|
+
current = current.def.in ?? current;
|
|
5718
5863
|
continue;
|
|
5719
5864
|
}
|
|
5720
5865
|
break;
|
|
@@ -5724,19 +5869,19 @@ function readSchemaDescription2(schema) {
|
|
|
5724
5869
|
function unwrap2(schema) {
|
|
5725
5870
|
let current = schema;
|
|
5726
5871
|
let optional = false;
|
|
5727
|
-
while (current?.
|
|
5728
|
-
const typeName = current.
|
|
5872
|
+
while (current?.def?.type) {
|
|
5873
|
+
const typeName = current.def.type;
|
|
5729
5874
|
if (typeName === "optional" || typeName === "default") {
|
|
5730
5875
|
optional = true;
|
|
5731
|
-
current = current.
|
|
5876
|
+
current = current.def.innerType ?? current;
|
|
5732
5877
|
continue;
|
|
5733
5878
|
}
|
|
5734
5879
|
if (typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
|
|
5735
|
-
current = current.
|
|
5880
|
+
current = current.def.innerType ?? current;
|
|
5736
5881
|
continue;
|
|
5737
5882
|
}
|
|
5738
5883
|
if (typeName === "pipe") {
|
|
5739
|
-
current = current.
|
|
5884
|
+
current = current.def.in ?? current;
|
|
5740
5885
|
continue;
|
|
5741
5886
|
}
|
|
5742
5887
|
break;
|