extrait 0.7.0 → 0.7.2
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 +2 -0
- 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 +6 -10
- package/dist/generate-tool-timeout.d.ts +3 -0
- package/dist/index.cjs +1016 -1121
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1016 -1121
- package/dist/prompt.d.ts +14 -0
- package/dist/providers/mcp-runtime-debug.d.ts +3 -0
- package/dist/providers/mcp-runtime.d.ts +1 -1
- package/dist/structured-streaming.d.ts +1 -0
- 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 -468
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -58,6 +58,7 @@ __export(exports_src, {
|
|
|
58
58
|
extractMarkdownCodeBlocks: () => extractMarkdownCodeBlocks,
|
|
59
59
|
extractJsonCandidates: () => extractJsonCandidates,
|
|
60
60
|
extractFirstMarkdownCode: () => extractFirstMarkdownCode,
|
|
61
|
+
dedent: () => dedent,
|
|
61
62
|
createProviderRegistry: () => createProviderRegistry,
|
|
62
63
|
createOpenAICompatibleAdapter: () => createOpenAICompatibleAdapter,
|
|
63
64
|
createModelAdapter: () => createModelAdapter,
|
|
@@ -91,9 +92,6 @@ __export(exports_src, {
|
|
|
91
92
|
});
|
|
92
93
|
module.exports = __toCommonJS(exports_src);
|
|
93
94
|
|
|
94
|
-
// src/extract.ts
|
|
95
|
-
var import_jsonrepair = require("jsonrepair");
|
|
96
|
-
|
|
97
95
|
// src/utils/common.ts
|
|
98
96
|
function toErrorMessage(error) {
|
|
99
97
|
if (error instanceof Error) {
|
|
@@ -311,184 +309,77 @@ function isOnlyWhitespace(value) {
|
|
|
311
309
|
return true;
|
|
312
310
|
}
|
|
313
311
|
|
|
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);
|
|
312
|
+
// src/extract-parse-hint.ts
|
|
313
|
+
var import_jsonrepair = require("jsonrepair");
|
|
314
|
+
function buildParseHint(content, allowRepair, hintMaxLength) {
|
|
315
|
+
if (content.length > hintMaxLength) {
|
|
316
|
+
return null;
|
|
362
317
|
}
|
|
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);
|
|
318
|
+
try {
|
|
379
319
|
return {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
320
|
+
success: true,
|
|
321
|
+
parsed: JSON.parse(content),
|
|
322
|
+
repaired: null,
|
|
323
|
+
usedRepair: false,
|
|
324
|
+
stage: "parse",
|
|
325
|
+
error: ""
|
|
383
326
|
};
|
|
384
|
-
})
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
327
|
+
} catch (directError) {
|
|
328
|
+
if (!allowRepair) {
|
|
329
|
+
return {
|
|
330
|
+
success: false,
|
|
331
|
+
parsed: null,
|
|
332
|
+
repaired: null,
|
|
333
|
+
usedRepair: false,
|
|
334
|
+
stage: "parse",
|
|
335
|
+
error: toErrorMessage(directError)
|
|
336
|
+
};
|
|
392
337
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
338
|
+
let repaired;
|
|
339
|
+
try {
|
|
340
|
+
repaired = import_jsonrepair.jsonrepair(content);
|
|
341
|
+
} catch (repairError) {
|
|
342
|
+
return {
|
|
343
|
+
success: false,
|
|
344
|
+
parsed: null,
|
|
345
|
+
repaired: null,
|
|
346
|
+
usedRepair: true,
|
|
347
|
+
stage: "repair",
|
|
348
|
+
error: toErrorMessage(repairError)
|
|
349
|
+
};
|
|
404
350
|
}
|
|
405
|
-
|
|
406
|
-
return
|
|
351
|
+
try {
|
|
352
|
+
return {
|
|
353
|
+
success: true,
|
|
354
|
+
parsed: JSON.parse(repaired),
|
|
355
|
+
repaired,
|
|
356
|
+
usedRepair: true,
|
|
357
|
+
stage: "parse",
|
|
358
|
+
error: ""
|
|
359
|
+
};
|
|
360
|
+
} catch (parseError) {
|
|
361
|
+
return {
|
|
362
|
+
success: false,
|
|
363
|
+
parsed: null,
|
|
364
|
+
repaired,
|
|
365
|
+
usedRepair: true,
|
|
366
|
+
stage: "parse",
|
|
367
|
+
error: toErrorMessage(parseError || directError)
|
|
368
|
+
};
|
|
407
369
|
}
|
|
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
|
-
});
|
|
370
|
+
}
|
|
421
371
|
}
|
|
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
|
-
});
|
|
372
|
+
function parseHintBonus(hint) {
|
|
373
|
+
if (hint.success) {
|
|
374
|
+
return hint.usedRepair ? 70 : 120;
|
|
489
375
|
}
|
|
490
|
-
return
|
|
376
|
+
return hint.usedRepair ? -20 : -10;
|
|
491
377
|
}
|
|
378
|
+
|
|
379
|
+
// src/extract-shape.ts
|
|
380
|
+
var RE_EMPTY_OBJECT = /^\{\s*\}$/;
|
|
381
|
+
var RE_EMPTY_ARRAY = /^\[\s*\]$/;
|
|
382
|
+
var RE_BOUNDARY_CHAR = /[\s,.;:!?`"'()[\]{}<>]/;
|
|
492
383
|
function looksLikeJsonEnvelope(content, acceptArrays) {
|
|
493
384
|
const trimmed = content.trim();
|
|
494
385
|
if (trimmed.startsWith("{")) {
|
|
@@ -597,95 +488,6 @@ function passesShapeFilter(candidate) {
|
|
|
597
488
|
}
|
|
598
489
|
return candidate.shapeScore >= 35;
|
|
599
490
|
}
|
|
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
491
|
function hasBalancedJsonDelimiters(input) {
|
|
690
492
|
const stack = [];
|
|
691
493
|
let inString = false;
|
|
@@ -771,6 +573,207 @@ function dedupeCandidates(candidates) {
|
|
|
771
573
|
function clamp(value, min, max) {
|
|
772
574
|
return Math.max(min, Math.min(max, Math.floor(value)));
|
|
773
575
|
}
|
|
576
|
+
function normalizeInteger(value, fallback) {
|
|
577
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
578
|
+
return fallback;
|
|
579
|
+
}
|
|
580
|
+
return Math.max(0, Math.floor(value));
|
|
581
|
+
}
|
|
582
|
+
function resolveExtractionHeuristics(input, defaults) {
|
|
583
|
+
const merged = {
|
|
584
|
+
...defaults,
|
|
585
|
+
...input
|
|
586
|
+
};
|
|
587
|
+
const firstPassMin = normalizeInteger(merged.firstPassMin, defaults.firstPassMin);
|
|
588
|
+
const firstPassCap = Math.max(firstPassMin, normalizeInteger(merged.firstPassCap, defaults.firstPassCap));
|
|
589
|
+
const secondPassMin = normalizeInteger(merged.secondPassMin, defaults.secondPassMin);
|
|
590
|
+
const secondPassCap = Math.max(secondPassMin, normalizeInteger(merged.secondPassCap, defaults.secondPassCap));
|
|
591
|
+
return {
|
|
592
|
+
firstPassMin,
|
|
593
|
+
firstPassCap,
|
|
594
|
+
firstPassMultiplier: normalizeInteger(merged.firstPassMultiplier, defaults.firstPassMultiplier),
|
|
595
|
+
secondPassMin,
|
|
596
|
+
secondPassCap,
|
|
597
|
+
secondPassMultiplier: normalizeInteger(merged.secondPassMultiplier, defaults.secondPassMultiplier),
|
|
598
|
+
hintMaxLength: normalizeInteger(merged.hintMaxLength, defaults.hintMaxLength)
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/extract.ts
|
|
603
|
+
var DEFAULT_EXTRACTION_HEURISTICS = {
|
|
604
|
+
firstPassMin: 12,
|
|
605
|
+
firstPassCap: 30,
|
|
606
|
+
firstPassMultiplier: 6,
|
|
607
|
+
secondPassMin: 4,
|
|
608
|
+
secondPassCap: 8,
|
|
609
|
+
secondPassMultiplier: 2,
|
|
610
|
+
hintMaxLength: 50000
|
|
611
|
+
};
|
|
612
|
+
function extractJsonCandidates(input, options = {}) {
|
|
613
|
+
const maxCandidates = options.maxCandidates ?? 5;
|
|
614
|
+
const acceptArrays = options.acceptArrays ?? true;
|
|
615
|
+
const allowRepairHints = options.allowRepairHints ?? true;
|
|
616
|
+
const heuristics = resolveExtractionHeuristics(options.heuristics, DEFAULT_EXTRACTION_HEURISTICS);
|
|
617
|
+
const extractionInput = input;
|
|
618
|
+
const candidates = [];
|
|
619
|
+
candidates.push(...extractFromMarkdown(extractionInput, acceptArrays));
|
|
620
|
+
candidates.push(...scanBalancedSegments(extractionInput, acceptArrays));
|
|
621
|
+
if (candidates.length === 0 && extractionInput.trim()) {
|
|
622
|
+
const content = extractionInput.trim();
|
|
623
|
+
candidates.push({
|
|
624
|
+
id: "raw:fallback",
|
|
625
|
+
source: "raw",
|
|
626
|
+
content,
|
|
627
|
+
start: 0,
|
|
628
|
+
end: extractionInput.length,
|
|
629
|
+
score: 10 + Math.floor(lengthScore(content.length) / 3)
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
const prefiltered = prefilterByJsonShape(candidates, acceptArrays);
|
|
633
|
+
const firstPassLimit = clamp(maxCandidates * heuristics.firstPassMultiplier, heuristics.firstPassMin, heuristics.firstPassCap);
|
|
634
|
+
const firstPass = prefiltered.slice(0, firstPassLimit);
|
|
635
|
+
const secondPassLimit = Math.min(firstPass.length, clamp(maxCandidates * heuristics.secondPassMultiplier, heuristics.secondPassMin, heuristics.secondPassCap));
|
|
636
|
+
for (let i = 0;i < secondPassLimit; i += 1) {
|
|
637
|
+
const candidate = firstPass[i];
|
|
638
|
+
if (!candidate) {
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
const parseHint = buildParseHint(candidate.content, allowRepairHints, heuristics.hintMaxLength);
|
|
642
|
+
if (!parseHint) {
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
candidate.parseHint = parseHint;
|
|
646
|
+
candidate.score += parseHintBonus(parseHint);
|
|
647
|
+
}
|
|
648
|
+
const sorted = sortCandidates(firstPass);
|
|
649
|
+
const deduped = dedupeCandidates(sorted);
|
|
650
|
+
return deduped.slice(0, maxCandidates).map((candidate, index) => ({
|
|
651
|
+
id: `${candidate.source}:${index}`,
|
|
652
|
+
source: candidate.source,
|
|
653
|
+
content: candidate.content,
|
|
654
|
+
language: candidate.language,
|
|
655
|
+
parseHint: candidate.parseHint,
|
|
656
|
+
start: candidate.start,
|
|
657
|
+
end: candidate.end,
|
|
658
|
+
score: candidate.score
|
|
659
|
+
}));
|
|
660
|
+
}
|
|
661
|
+
function prefilterByJsonShape(candidates, acceptArrays) {
|
|
662
|
+
const shaped = candidates.map((candidate) => {
|
|
663
|
+
const shapeScore = jsonShapeScore(candidate.content, acceptArrays);
|
|
664
|
+
return {
|
|
665
|
+
...candidate,
|
|
666
|
+
shapeScore,
|
|
667
|
+
score: candidate.score + shapeScore
|
|
668
|
+
};
|
|
669
|
+
});
|
|
670
|
+
const sorted = sortCandidates(shaped);
|
|
671
|
+
const deduped = dedupeCandidates(sorted);
|
|
672
|
+
const filtered = deduped.filter((candidate) => passesShapeFilter(candidate));
|
|
673
|
+
if (filtered.length > 0) {
|
|
674
|
+
const fallback = deduped.find((candidate) => !passesShapeFilter(candidate));
|
|
675
|
+
if (fallback) {
|
|
676
|
+
filtered.push(fallback);
|
|
677
|
+
}
|
|
678
|
+
return sortCandidates(filtered);
|
|
679
|
+
}
|
|
680
|
+
return deduped.slice(0, Math.min(1, deduped.length));
|
|
681
|
+
}
|
|
682
|
+
function extractFromMarkdown(input, acceptArrays) {
|
|
683
|
+
const blocks = extractMarkdownCodeBlocks(input);
|
|
684
|
+
return blocks.flatMap((block, index) => {
|
|
685
|
+
const language = block.language || null;
|
|
686
|
+
const content = block.code.trim();
|
|
687
|
+
if (!content) {
|
|
688
|
+
return [];
|
|
689
|
+
}
|
|
690
|
+
if (!looksLikeJsonEnvelope(content, acceptArrays)) {
|
|
691
|
+
return [];
|
|
692
|
+
}
|
|
693
|
+
const langBonus = languageBonus(language);
|
|
694
|
+
return [
|
|
695
|
+
{
|
|
696
|
+
id: `fenced:${index}`,
|
|
697
|
+
source: "fenced",
|
|
698
|
+
language,
|
|
699
|
+
content,
|
|
700
|
+
start: block.start,
|
|
701
|
+
end: block.end,
|
|
702
|
+
score: 260 + langBonus + lengthScore(content.length)
|
|
703
|
+
}
|
|
704
|
+
];
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
function scanBalancedSegments(input, acceptArrays) {
|
|
708
|
+
const results = [];
|
|
709
|
+
const stack = [];
|
|
710
|
+
let inString = false;
|
|
711
|
+
let quote = null;
|
|
712
|
+
let escaped = false;
|
|
713
|
+
for (let i = 0;i < input.length; i += 1) {
|
|
714
|
+
const char = input[i];
|
|
715
|
+
if (!char) {
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (inString) {
|
|
719
|
+
if (escaped) {
|
|
720
|
+
escaped = false;
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
if (char === "\\") {
|
|
724
|
+
escaped = true;
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (char === quote) {
|
|
728
|
+
inString = false;
|
|
729
|
+
quote = null;
|
|
730
|
+
}
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
const allowSingleQuoted = stack.length > 0;
|
|
734
|
+
if (char === '"' || allowSingleQuoted && (char === "'" || char === "`")) {
|
|
735
|
+
inString = true;
|
|
736
|
+
quote = char;
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
if (char === "{" || char === "[") {
|
|
740
|
+
stack.push({ char, index: i });
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
if (char !== "}" && char !== "]") {
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
const expectedOpen = char === "}" ? "{" : "[";
|
|
747
|
+
while (stack.length > 0 && stack[stack.length - 1]?.char !== expectedOpen) {
|
|
748
|
+
stack.pop();
|
|
749
|
+
}
|
|
750
|
+
const opened = stack.pop();
|
|
751
|
+
if (!opened) {
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
if (stack.length > 0) {
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
if (!acceptArrays && opened.char === "[") {
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
const content = input.slice(opened.index, i + 1).trim();
|
|
761
|
+
if (!content) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
const rootBonus = opened.char === "{" ? 40 : 20;
|
|
765
|
+
const boundaryBonus = boundaryScore(input, opened.index, i + 1);
|
|
766
|
+
results.push({
|
|
767
|
+
id: `scan:${results.length}`,
|
|
768
|
+
source: "scan",
|
|
769
|
+
content,
|
|
770
|
+
start: opened.index,
|
|
771
|
+
end: i + 1,
|
|
772
|
+
score: 120 + rootBonus + boundaryBonus + lengthScore(content.length)
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
return results;
|
|
776
|
+
}
|
|
774
777
|
// src/schema.ts
|
|
775
778
|
var RE_SIMPLE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
776
779
|
var RE_WHITESPACE = /\s+/g;
|
|
@@ -1219,17 +1222,107 @@ function findSSEBoundary(buffer) {
|
|
|
1219
1222
|
return Math.max(crlfIndex, lfIndex);
|
|
1220
1223
|
}
|
|
1221
1224
|
|
|
1222
|
-
// src/providers/mcp-runtime.ts
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1225
|
+
// src/providers/mcp-runtime-debug.ts
|
|
1226
|
+
function formatToolExecutionDebugLine(execution) {
|
|
1227
|
+
const status = execution.error ? "error" : "ok";
|
|
1228
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1229
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1230
|
+
const duration = typeof execution.durationMs === "number" ? ` ${execution.durationMs}ms` : "";
|
|
1231
|
+
const base = `[tool:mcp:${status}] ${scope} ${toolRef}#${execution.callId}${duration}`;
|
|
1232
|
+
if (execution.error) {
|
|
1233
|
+
return `${base} -> ${execution.error}`;
|
|
1234
|
+
}
|
|
1235
|
+
return base;
|
|
1236
|
+
}
|
|
1237
|
+
function emitToolExecution(request, execution) {
|
|
1238
|
+
request.onToolExecution?.(execution);
|
|
1239
|
+
const debug = resolveToolDebugOptions(request.toolDebug);
|
|
1240
|
+
if (!debug.enabled) {
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
debug.logger(formatToolExecutionDebugLine(execution));
|
|
1244
|
+
if (debug.includeRequest) {
|
|
1245
|
+
debug.logger(formatToolExecutionRequestDebugLine(execution, debug));
|
|
1246
|
+
}
|
|
1247
|
+
if (debug.includeResult && (!execution.error || debug.includeResultOnError)) {
|
|
1248
|
+
debug.logger(formatToolExecutionResultDebugLine(execution, debug));
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
function resolveToolDebugOptions(value) {
|
|
1252
|
+
if (value === true) {
|
|
1253
|
+
return {
|
|
1254
|
+
enabled: true,
|
|
1255
|
+
logger: defaultToolDebugLogger,
|
|
1256
|
+
includeRequest: true,
|
|
1257
|
+
includeResult: true,
|
|
1258
|
+
includeResultOnError: true,
|
|
1259
|
+
pretty: false
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
if (value === undefined || value === false) {
|
|
1263
|
+
return {
|
|
1264
|
+
enabled: false,
|
|
1265
|
+
logger: () => {
|
|
1266
|
+
return;
|
|
1267
|
+
},
|
|
1268
|
+
includeRequest: false,
|
|
1269
|
+
includeResult: false,
|
|
1270
|
+
includeResultOnError: false,
|
|
1271
|
+
pretty: false
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
return {
|
|
1275
|
+
enabled: value.enabled ?? true,
|
|
1276
|
+
logger: value.logger ?? defaultToolDebugLogger,
|
|
1277
|
+
includeRequest: value.includeRequest ?? true,
|
|
1278
|
+
includeResult: value.includeResult ?? true,
|
|
1279
|
+
includeResultOnError: value.includeResultOnError ?? true,
|
|
1280
|
+
pretty: value.pretty ?? false
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
function defaultToolDebugLogger(line) {
|
|
1284
|
+
const { log } = globalThis.console;
|
|
1285
|
+
log(line);
|
|
1286
|
+
}
|
|
1287
|
+
function formatToolExecutionRequestDebugLine(execution, debug) {
|
|
1288
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1289
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1290
|
+
const payload = formatDebugPayload(execution.arguments, debug.pretty);
|
|
1291
|
+
return `[tool:mcp:request] ${scope} ${toolRef}#${execution.callId} arguments=${payload}`;
|
|
1292
|
+
}
|
|
1293
|
+
function formatToolExecutionResultDebugLine(execution, debug) {
|
|
1294
|
+
const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
|
|
1295
|
+
const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
|
|
1296
|
+
if (execution.error) {
|
|
1297
|
+
const payload2 = formatDebugPayload({ error: execution.error }, debug.pretty);
|
|
1298
|
+
return `[tool:mcp:result:error] ${scope} ${toolRef}#${execution.callId} output=${payload2}`;
|
|
1299
|
+
}
|
|
1300
|
+
const payload = formatDebugPayload(execution.output, debug.pretty);
|
|
1301
|
+
return `[tool:mcp:result:ok] ${scope} ${toolRef}#${execution.callId} output=${payload}`;
|
|
1302
|
+
}
|
|
1303
|
+
function formatDebugPayload(value, pretty) {
|
|
1304
|
+
if (value === undefined) {
|
|
1305
|
+
return "undefined";
|
|
1306
|
+
}
|
|
1307
|
+
try {
|
|
1308
|
+
const serialized = JSON.stringify(value, null, pretty ? 2 : 0);
|
|
1309
|
+
return serialized ?? "undefined";
|
|
1310
|
+
} catch {
|
|
1311
|
+
return String(value);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// src/providers/mcp-runtime.ts
|
|
1316
|
+
var DEFAULT_MAX_TOOL_ROUNDS = 100;
|
|
1317
|
+
async function resolveMCPToolset(clients) {
|
|
1318
|
+
if (!Array.isArray(clients) || clients.length === 0) {
|
|
1319
|
+
return {
|
|
1320
|
+
tools: [],
|
|
1321
|
+
byName: new Map
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
const listed = [];
|
|
1325
|
+
for (const client of clients) {
|
|
1233
1326
|
let cursor;
|
|
1234
1327
|
do {
|
|
1235
1328
|
const page = await client.listTools(cursor ? { cursor } : undefined);
|
|
@@ -1411,90 +1504,6 @@ function stringifyToolOutput(value) {
|
|
|
1411
1504
|
}
|
|
1412
1505
|
return JSON.stringify(value ?? null);
|
|
1413
1506
|
}
|
|
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
1507
|
function countNameCollisions(names) {
|
|
1499
1508
|
const out = new Map;
|
|
1500
1509
|
for (const name of names) {
|
|
@@ -1691,103 +1700,89 @@ function createOpenAICompatibleAdapter(options) {
|
|
|
1691
1700
|
if (usesMCP) {
|
|
1692
1701
|
return streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks);
|
|
1693
1702
|
}
|
|
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;
|
|
1703
|
+
return streamWithChatCompletionsPassThrough(options, fetcher, path, request, callbacks);
|
|
1760
1704
|
},
|
|
1761
1705
|
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
|
-
|
|
1706
|
+
return embedOpenAI(options, fetcher, embeddingPath, request);
|
|
1707
|
+
}
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
async function streamWithChatCompletionsPassThrough(options, fetcher, path, request, callbacks) {
|
|
1711
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1712
|
+
messages: buildMessages(request),
|
|
1713
|
+
stream: true
|
|
1714
|
+
}));
|
|
1715
|
+
if (!response.ok) {
|
|
1716
|
+
const message = await response.text();
|
|
1717
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1718
|
+
}
|
|
1719
|
+
callbacks.onStart?.();
|
|
1720
|
+
let text = "";
|
|
1721
|
+
let reasoning = "";
|
|
1722
|
+
let usage;
|
|
1723
|
+
let finishReason;
|
|
1724
|
+
await consumeSSE(response, (data) => {
|
|
1725
|
+
if (data === "[DONE]") {
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1728
|
+
const json = safeJSONParse(data);
|
|
1729
|
+
if (!isRecord2(json)) {
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
const delta = pickAssistantDelta(json);
|
|
1733
|
+
const reasoningDelta = pickAssistantReasoningDelta(json);
|
|
1734
|
+
const chunkUsage = pickUsage(json);
|
|
1735
|
+
const chunkFinishReason = pickFinishReason(json);
|
|
1736
|
+
usage = preferLatestUsage(usage, chunkUsage);
|
|
1737
|
+
if (chunkFinishReason) {
|
|
1738
|
+
finishReason = chunkFinishReason;
|
|
1739
|
+
}
|
|
1740
|
+
if (delta) {
|
|
1741
|
+
text += delta;
|
|
1742
|
+
callbacks.onToken?.(delta);
|
|
1790
1743
|
}
|
|
1744
|
+
if (reasoningDelta) {
|
|
1745
|
+
reasoning += reasoningDelta;
|
|
1746
|
+
}
|
|
1747
|
+
emitOpenAIStreamChunk(callbacks, undefined, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
1748
|
+
});
|
|
1749
|
+
const out = {
|
|
1750
|
+
text,
|
|
1751
|
+
reasoning: reasoning.length > 0 ? reasoning : undefined,
|
|
1752
|
+
usage,
|
|
1753
|
+
finishReason
|
|
1754
|
+
};
|
|
1755
|
+
callbacks.onComplete?.(out);
|
|
1756
|
+
return out;
|
|
1757
|
+
}
|
|
1758
|
+
async function embedOpenAI(options, fetcher, path, request) {
|
|
1759
|
+
const body = cleanUndefined({
|
|
1760
|
+
...options.defaultBody,
|
|
1761
|
+
...request.body,
|
|
1762
|
+
model: request.model ?? options.model,
|
|
1763
|
+
input: request.input,
|
|
1764
|
+
dimensions: request.dimensions,
|
|
1765
|
+
encoding_format: "float"
|
|
1766
|
+
});
|
|
1767
|
+
const response = await fetcher(buildURL(options.baseURL, path), {
|
|
1768
|
+
method: "POST",
|
|
1769
|
+
headers: buildHeaders(options),
|
|
1770
|
+
body: JSON.stringify(body)
|
|
1771
|
+
});
|
|
1772
|
+
if (!response.ok) {
|
|
1773
|
+
const message = await response.text();
|
|
1774
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1775
|
+
}
|
|
1776
|
+
const json = await response.json();
|
|
1777
|
+
const data = json.data;
|
|
1778
|
+
if (!Array.isArray(data)) {
|
|
1779
|
+
throw new Error("Unexpected embedding response: missing data array");
|
|
1780
|
+
}
|
|
1781
|
+
return {
|
|
1782
|
+
embeddings: data.map((d) => isRecord2(d) && Array.isArray(d.embedding) ? d.embedding : []),
|
|
1783
|
+
model: pickString(json.model) ?? body.model,
|
|
1784
|
+
usage: pickUsage(json),
|
|
1785
|
+
raw: json
|
|
1791
1786
|
};
|
|
1792
1787
|
}
|
|
1793
1788
|
async function completeOpenAIRequest(options, fetcher, chatPath, responsesPath, request) {
|
|
@@ -1804,22 +1799,80 @@ async function completeOpenAIRequest(options, fetcher, chatPath, responsesPath,
|
|
|
1804
1799
|
}
|
|
1805
1800
|
return completeWithChatCompletionsPassThrough(options, fetcher, chatPath, request);
|
|
1806
1801
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
1802
|
+
function buildChatCompletionsBody(options, request, overrides) {
|
|
1803
|
+
return buildOpenAIRequestBody(options, request, "max_tokens", overrides);
|
|
1804
|
+
}
|
|
1805
|
+
function buildResponsesBody(options, request, overrides) {
|
|
1806
|
+
return buildOpenAIRequestBody(options, request, "max_output_tokens", overrides);
|
|
1807
|
+
}
|
|
1808
|
+
function buildOpenAIRequestBody(options, request, maxTokenKey, overrides) {
|
|
1809
|
+
return cleanUndefined({
|
|
1810
|
+
...options.defaultBody,
|
|
1811
|
+
...request.body,
|
|
1812
|
+
model: options.model,
|
|
1813
|
+
temperature: request.temperature,
|
|
1814
|
+
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1815
|
+
[maxTokenKey]: request.maxTokens,
|
|
1816
|
+
...overrides
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
function sendOpenAIRequest(options, fetcher, path, request, body) {
|
|
1820
|
+
return fetcher(buildURL(options.baseURL, path), {
|
|
1809
1821
|
method: "POST",
|
|
1810
1822
|
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
|
-
})),
|
|
1823
|
+
body: JSON.stringify(body),
|
|
1821
1824
|
signal: request.signal
|
|
1822
1825
|
});
|
|
1826
|
+
}
|
|
1827
|
+
async function sendOpenAIJsonRequest(options, fetcher, path, request, body) {
|
|
1828
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, body);
|
|
1829
|
+
if (!response.ok) {
|
|
1830
|
+
const message = await response.text();
|
|
1831
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1832
|
+
}
|
|
1833
|
+
return await response.json();
|
|
1834
|
+
}
|
|
1835
|
+
function createResponsesMCPState(request) {
|
|
1836
|
+
return {
|
|
1837
|
+
input: buildResponsesInput(request),
|
|
1838
|
+
previousResponseId: pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined),
|
|
1839
|
+
aggregatedUsage: undefined,
|
|
1840
|
+
finishReason: undefined,
|
|
1841
|
+
lastPayload: undefined,
|
|
1842
|
+
executedToolCalls: [],
|
|
1843
|
+
toolExecutions: [],
|
|
1844
|
+
reasoningBlocks: []
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
function buildResponsesMCPResult(state, text, raw) {
|
|
1848
|
+
return {
|
|
1849
|
+
text,
|
|
1850
|
+
reasoning: joinReasoningBlocks(state.reasoningBlocks) || undefined,
|
|
1851
|
+
reasoningBlocks: state.reasoningBlocks.length > 0 ? state.reasoningBlocks : undefined,
|
|
1852
|
+
raw,
|
|
1853
|
+
usage: state.aggregatedUsage,
|
|
1854
|
+
finishReason: state.finishReason,
|
|
1855
|
+
toolCalls: state.executedToolCalls.length > 0 ? state.executedToolCalls : undefined,
|
|
1856
|
+
toolExecutions: state.toolExecutions.length > 0 ? state.toolExecutions : undefined
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
function emitOpenAIStreamChunk(callbacks, round, raw, delta, reasoningDelta, usage, finishReason) {
|
|
1860
|
+
if (delta || reasoningDelta || usage || finishReason) {
|
|
1861
|
+
callbacks.onChunk?.({
|
|
1862
|
+
textDelta: delta,
|
|
1863
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
1864
|
+
...round !== undefined ? { turnIndex: round } : {},
|
|
1865
|
+
raw,
|
|
1866
|
+
usage,
|
|
1867
|
+
finishReason
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
async function completeWithChatCompletionsPassThrough(options, fetcher, path, request) {
|
|
1872
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1873
|
+
messages: buildMessages(request),
|
|
1874
|
+
stream: false
|
|
1875
|
+
}));
|
|
1823
1876
|
if (!response.ok) {
|
|
1824
1877
|
const message = await response.text();
|
|
1825
1878
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -1871,28 +1924,12 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1871
1924
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1872
1925
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1873
1926
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
1874
|
-
const
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
model: options.model,
|
|
1881
|
-
messages,
|
|
1882
|
-
temperature: request.temperature,
|
|
1883
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1884
|
-
max_tokens: request.maxTokens,
|
|
1885
|
-
tools: transportTools,
|
|
1886
|
-
tool_choice: request.toolChoice,
|
|
1887
|
-
parallel_tool_calls: request.parallelToolCalls
|
|
1888
|
-
})),
|
|
1889
|
-
signal: request.signal
|
|
1890
|
-
});
|
|
1891
|
-
if (!response.ok) {
|
|
1892
|
-
const message = await response.text();
|
|
1893
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1894
|
-
}
|
|
1895
|
-
const payload = await response.json();
|
|
1927
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
1928
|
+
messages,
|
|
1929
|
+
tools: transportTools,
|
|
1930
|
+
tool_choice: request.toolChoice,
|
|
1931
|
+
parallel_tool_calls: request.parallelToolCalls
|
|
1932
|
+
}));
|
|
1896
1933
|
lastPayload = payload;
|
|
1897
1934
|
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
1898
1935
|
finishReason = pickFinishReason(payload);
|
|
@@ -1947,26 +1984,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1947
1984
|
}
|
|
1948
1985
|
async function completeWithResponsesAPIPassThrough(options, fetcher, path, request) {
|
|
1949
1986
|
const body = isRecord2(request.body) ? request.body : undefined;
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
...options.defaultBody,
|
|
1955
|
-
...request.body,
|
|
1956
|
-
model: options.model,
|
|
1957
|
-
input: buildResponsesInput(request),
|
|
1958
|
-
previous_response_id: pickString(body?.previous_response_id),
|
|
1959
|
-
temperature: request.temperature,
|
|
1960
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
1961
|
-
max_output_tokens: request.maxTokens
|
|
1962
|
-
})),
|
|
1963
|
-
signal: request.signal
|
|
1964
|
-
});
|
|
1965
|
-
if (!response.ok) {
|
|
1966
|
-
const message = await response.text();
|
|
1967
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1968
|
-
}
|
|
1969
|
-
const payload = await response.json();
|
|
1987
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
1988
|
+
input: buildResponsesInput(request),
|
|
1989
|
+
previous_response_id: pickString(body?.previous_response_id)
|
|
1990
|
+
}));
|
|
1970
1991
|
const toolCalls = pickResponsesToolCalls(payload);
|
|
1971
1992
|
return {
|
|
1972
1993
|
text: pickResponsesText(payload) || pickAssistantText(payload),
|
|
@@ -1978,58 +1999,26 @@ async function completeWithResponsesAPIPassThrough(options, fetcher, path, reque
|
|
|
1978
1999
|
}
|
|
1979
2000
|
async function completeWithResponsesAPIWithMCP(options, fetcher, path, request) {
|
|
1980
2001
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
1981
|
-
|
|
1982
|
-
let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
|
|
1983
|
-
let aggregatedUsage;
|
|
1984
|
-
let finishReason;
|
|
1985
|
-
let lastPayload;
|
|
1986
|
-
const executedToolCalls = [];
|
|
1987
|
-
const toolExecutions = [];
|
|
1988
|
-
const reasoningBlocks = [];
|
|
2002
|
+
const state = createResponsesMCPState(request);
|
|
1989
2003
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1990
2004
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1991
2005
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
1992
|
-
const
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
max_output_tokens: request.maxTokens,
|
|
2004
|
-
tools: transportTools,
|
|
2005
|
-
tool_choice: request.toolChoice,
|
|
2006
|
-
parallel_tool_calls: request.parallelToolCalls
|
|
2007
|
-
})),
|
|
2008
|
-
signal: request.signal
|
|
2009
|
-
});
|
|
2010
|
-
if (!response.ok) {
|
|
2011
|
-
const message = await response.text();
|
|
2012
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2013
|
-
}
|
|
2014
|
-
const payload = await response.json();
|
|
2015
|
-
lastPayload = payload;
|
|
2016
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
2017
|
-
finishReason = pickResponsesFinishReason(payload) ?? finishReason;
|
|
2018
|
-
pushReasoningBlock(reasoningBlocks, round, pickResponsesReasoning(payload));
|
|
2006
|
+
const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
2007
|
+
input: state.input,
|
|
2008
|
+
previous_response_id: state.previousResponseId,
|
|
2009
|
+
tools: transportTools,
|
|
2010
|
+
tool_choice: request.toolChoice,
|
|
2011
|
+
parallel_tool_calls: request.parallelToolCalls
|
|
2012
|
+
}));
|
|
2013
|
+
state.lastPayload = payload;
|
|
2014
|
+
state.aggregatedUsage = mergeUsage(state.aggregatedUsage, pickUsage(payload));
|
|
2015
|
+
state.finishReason = pickResponsesFinishReason(payload) ?? state.finishReason;
|
|
2016
|
+
pushReasoningBlock(state.reasoningBlocks, round, pickResponsesReasoning(payload));
|
|
2019
2017
|
const providerToolCalls = pickResponsesToolCalls(payload);
|
|
2020
2018
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
2021
2019
|
if (functionCalls.length === 0) {
|
|
2022
2020
|
const text = pickResponsesText(payload) || pickAssistantText(payload);
|
|
2023
|
-
return
|
|
2024
|
-
text,
|
|
2025
|
-
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2026
|
-
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2027
|
-
raw: payload,
|
|
2028
|
-
usage: aggregatedUsage,
|
|
2029
|
-
finishReason,
|
|
2030
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2031
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2032
|
-
};
|
|
2021
|
+
return buildResponsesMCPResult(state, text, payload);
|
|
2033
2022
|
}
|
|
2034
2023
|
if (round > maxToolRounds) {
|
|
2035
2024
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
@@ -2040,25 +2029,16 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
2040
2029
|
provider: "openai-compatible",
|
|
2041
2030
|
model: options.model
|
|
2042
2031
|
});
|
|
2043
|
-
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2044
|
-
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2045
|
-
input = outputs.map((entry) => ({
|
|
2032
|
+
state.executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2033
|
+
state.toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2034
|
+
state.input = outputs.map((entry) => ({
|
|
2046
2035
|
type: "function_call_output",
|
|
2047
2036
|
call_id: entry.call.id,
|
|
2048
2037
|
output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
|
|
2049
2038
|
}));
|
|
2050
|
-
previousResponseId = pickString(payload.id);
|
|
2039
|
+
state.previousResponseId = pickString(payload.id);
|
|
2051
2040
|
}
|
|
2052
|
-
return {
|
|
2053
|
-
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
2054
|
-
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2055
|
-
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2056
|
-
raw: lastPayload,
|
|
2057
|
-
usage: aggregatedUsage,
|
|
2058
|
-
finishReason,
|
|
2059
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2060
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2061
|
-
};
|
|
2041
|
+
return buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
|
|
2062
2042
|
}
|
|
2063
2043
|
async function streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks) {
|
|
2064
2044
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
@@ -2074,24 +2054,13 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2074
2054
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2075
2055
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2076
2056
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
2077
|
-
const response = await
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
messages,
|
|
2085
|
-
temperature: request.temperature,
|
|
2086
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2087
|
-
max_tokens: request.maxTokens,
|
|
2088
|
-
tools: transportTools,
|
|
2089
|
-
tool_choice: request.toolChoice,
|
|
2090
|
-
parallel_tool_calls: request.parallelToolCalls,
|
|
2091
|
-
stream: true
|
|
2092
|
-
})),
|
|
2093
|
-
signal: request.signal
|
|
2094
|
-
});
|
|
2057
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
|
|
2058
|
+
messages,
|
|
2059
|
+
tools: transportTools,
|
|
2060
|
+
tool_choice: request.toolChoice,
|
|
2061
|
+
parallel_tool_calls: request.parallelToolCalls,
|
|
2062
|
+
stream: true
|
|
2063
|
+
}));
|
|
2095
2064
|
if (!response.ok) {
|
|
2096
2065
|
const message = await response.text();
|
|
2097
2066
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2128,17 +2097,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2128
2097
|
roundReasoning += reasoningDelta;
|
|
2129
2098
|
reasoningFieldName ??= pickAssistantReasoningDeltaFieldName(json);
|
|
2130
2099
|
}
|
|
2131
|
-
|
|
2132
|
-
const chunk = {
|
|
2133
|
-
textDelta: delta,
|
|
2134
|
-
reasoningDelta: reasoningDelta || undefined,
|
|
2135
|
-
turnIndex: round,
|
|
2136
|
-
raw: json,
|
|
2137
|
-
usage: chunkUsage,
|
|
2138
|
-
finishReason: chunkFinishReason
|
|
2139
|
-
};
|
|
2140
|
-
callbacks.onChunk?.(chunk);
|
|
2141
|
-
}
|
|
2100
|
+
emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
2142
2101
|
});
|
|
2143
2102
|
aggregatedUsage = mergeUsage(aggregatedUsage, roundUsage);
|
|
2144
2103
|
if (roundFinishReason) {
|
|
@@ -2217,22 +2176,11 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2217
2176
|
}
|
|
2218
2177
|
async function streamWithResponsesAPIPassThrough(options, fetcher, path, request, callbacks) {
|
|
2219
2178
|
const body = isRecord2(request.body) ? request.body : undefined;
|
|
2220
|
-
const response = await
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
...request.body,
|
|
2226
|
-
model: options.model,
|
|
2227
|
-
input: buildResponsesInput(request),
|
|
2228
|
-
previous_response_id: pickString(body?.previous_response_id),
|
|
2229
|
-
temperature: request.temperature,
|
|
2230
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2231
|
-
max_output_tokens: request.maxTokens,
|
|
2232
|
-
stream: true
|
|
2233
|
-
})),
|
|
2234
|
-
signal: request.signal
|
|
2235
|
-
});
|
|
2179
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
2180
|
+
input: buildResponsesInput(request),
|
|
2181
|
+
previous_response_id: pickString(body?.previous_response_id),
|
|
2182
|
+
stream: true
|
|
2183
|
+
}));
|
|
2236
2184
|
if (!response.ok) {
|
|
2237
2185
|
const message = await response.text();
|
|
2238
2186
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2265,15 +2213,7 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
|
|
|
2265
2213
|
text += delta;
|
|
2266
2214
|
callbacks.onToken?.(delta);
|
|
2267
2215
|
}
|
|
2268
|
-
|
|
2269
|
-
const chunk = {
|
|
2270
|
-
textDelta: delta,
|
|
2271
|
-
raw: json,
|
|
2272
|
-
usage: chunkUsage,
|
|
2273
|
-
finishReason: chunkFinishReason
|
|
2274
|
-
};
|
|
2275
|
-
callbacks.onChunk?.(chunk);
|
|
2276
|
-
}
|
|
2216
|
+
emitOpenAIStreamChunk(callbacks, undefined, json, delta, "", chunkUsage, chunkFinishReason);
|
|
2277
2217
|
});
|
|
2278
2218
|
const finalPayload = lastPayload ?? {};
|
|
2279
2219
|
const out = {
|
|
@@ -2287,37 +2227,19 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
|
|
|
2287
2227
|
}
|
|
2288
2228
|
async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, callbacks) {
|
|
2289
2229
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
2290
|
-
|
|
2291
|
-
let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
|
|
2292
|
-
let aggregatedUsage;
|
|
2293
|
-
let finishReason;
|
|
2294
|
-
let lastPayload;
|
|
2295
|
-
const executedToolCalls = [];
|
|
2296
|
-
const toolExecutions = [];
|
|
2297
|
-
const reasoningBlocks = [];
|
|
2230
|
+
const state = createResponsesMCPState(request);
|
|
2298
2231
|
callbacks.onStart?.();
|
|
2299
2232
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2300
2233
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2301
2234
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
2302
|
-
const response = await
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
previous_response_id: previousResponseId,
|
|
2311
|
-
temperature: request.temperature,
|
|
2312
|
-
reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
|
|
2313
|
-
max_output_tokens: request.maxTokens,
|
|
2314
|
-
tools: transportTools,
|
|
2315
|
-
tool_choice: request.toolChoice,
|
|
2316
|
-
parallel_tool_calls: request.parallelToolCalls,
|
|
2317
|
-
stream: true
|
|
2318
|
-
})),
|
|
2319
|
-
signal: request.signal
|
|
2320
|
-
});
|
|
2235
|
+
const response = await sendOpenAIRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
|
|
2236
|
+
input: state.input,
|
|
2237
|
+
previous_response_id: state.previousResponseId,
|
|
2238
|
+
tools: transportTools,
|
|
2239
|
+
tool_choice: request.toolChoice,
|
|
2240
|
+
parallel_tool_calls: request.parallelToolCalls,
|
|
2241
|
+
stream: true
|
|
2242
|
+
}));
|
|
2321
2243
|
if (!response.ok) {
|
|
2322
2244
|
const message = await response.text();
|
|
2323
2245
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -2339,7 +2261,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2339
2261
|
const payload = pickResponsesStreamPayload(json);
|
|
2340
2262
|
if (payload) {
|
|
2341
2263
|
roundPayload = payload;
|
|
2342
|
-
lastPayload = payload;
|
|
2264
|
+
state.lastPayload = payload;
|
|
2343
2265
|
}
|
|
2344
2266
|
const delta = pickResponsesStreamTextDelta(json);
|
|
2345
2267
|
const reasoningDelta = pickResponsesStreamReasoningDelta(json);
|
|
@@ -2357,24 +2279,14 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2357
2279
|
if (reasoningDelta) {
|
|
2358
2280
|
roundReasoning += reasoningDelta;
|
|
2359
2281
|
}
|
|
2360
|
-
|
|
2361
|
-
const chunk = {
|
|
2362
|
-
textDelta: delta,
|
|
2363
|
-
reasoningDelta: reasoningDelta || undefined,
|
|
2364
|
-
turnIndex: round,
|
|
2365
|
-
raw: json,
|
|
2366
|
-
usage: chunkUsage,
|
|
2367
|
-
finishReason: chunkFinishReason
|
|
2368
|
-
};
|
|
2369
|
-
callbacks.onChunk?.(chunk);
|
|
2370
|
-
}
|
|
2282
|
+
emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
|
|
2371
2283
|
});
|
|
2372
2284
|
const resolvedRoundUsage = preferLatestUsage(roundUsage, roundPayload ? pickUsage(roundPayload) : undefined);
|
|
2373
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, resolvedRoundUsage);
|
|
2285
|
+
state.aggregatedUsage = mergeUsage(state.aggregatedUsage, resolvedRoundUsage);
|
|
2374
2286
|
if (roundFinishReason) {
|
|
2375
|
-
finishReason = roundFinishReason;
|
|
2287
|
+
state.finishReason = roundFinishReason;
|
|
2376
2288
|
} else if (roundPayload) {
|
|
2377
|
-
finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
|
|
2289
|
+
state.finishReason = pickResponsesFinishReason(roundPayload) ?? state.finishReason;
|
|
2378
2290
|
}
|
|
2379
2291
|
const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
|
|
2380
2292
|
if (roundPayload && roundReasoning.length === 0) {
|
|
@@ -2383,7 +2295,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2383
2295
|
const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
|
|
2384
2296
|
const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
|
|
2385
2297
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
2386
|
-
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
2298
|
+
pushReasoningBlock(state.reasoningBlocks, round, roundReasoning);
|
|
2387
2299
|
request.onTurnTransition?.({
|
|
2388
2300
|
turnIndex: round,
|
|
2389
2301
|
kind: "reasoningComplete",
|
|
@@ -2391,16 +2303,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2391
2303
|
});
|
|
2392
2304
|
if (functionCalls.length === 0) {
|
|
2393
2305
|
const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
|
|
2394
|
-
const out2 =
|
|
2395
|
-
text: finalText,
|
|
2396
|
-
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2397
|
-
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2398
|
-
raw: roundPayload ?? lastPayload,
|
|
2399
|
-
usage: aggregatedUsage,
|
|
2400
|
-
finishReason,
|
|
2401
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2402
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2403
|
-
};
|
|
2306
|
+
const out2 = buildResponsesMCPResult(state, finalText, roundPayload ?? state.lastPayload);
|
|
2404
2307
|
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2405
2308
|
callbacks.onComplete?.(out2);
|
|
2406
2309
|
return out2;
|
|
@@ -2425,26 +2328,17 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2425
2328
|
provider: "openai-compatible",
|
|
2426
2329
|
model: options.model
|
|
2427
2330
|
});
|
|
2428
|
-
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2429
|
-
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2331
|
+
state.executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2332
|
+
state.toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2430
2333
|
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2431
|
-
input = outputs.map((entry) => ({
|
|
2334
|
+
state.input = outputs.map((entry) => ({
|
|
2432
2335
|
type: "function_call_output",
|
|
2433
2336
|
call_id: entry.call.id,
|
|
2434
2337
|
output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
|
|
2435
2338
|
}));
|
|
2436
|
-
previousResponseId = pickString(roundPayload?.id);
|
|
2339
|
+
state.previousResponseId = pickString(roundPayload?.id);
|
|
2437
2340
|
}
|
|
2438
|
-
const out = {
|
|
2439
|
-
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
2440
|
-
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2441
|
-
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2442
|
-
raw: lastPayload,
|
|
2443
|
-
usage: aggregatedUsage,
|
|
2444
|
-
finishReason,
|
|
2445
|
-
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2446
|
-
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2447
|
-
};
|
|
2341
|
+
const out = buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
|
|
2448
2342
|
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2449
2343
|
callbacks.onComplete?.(out);
|
|
2450
2344
|
return out;
|
|
@@ -2597,7 +2491,7 @@ function pickAssistantDelta(payload) {
|
|
|
2597
2491
|
if (!isRecord2(delta)) {
|
|
2598
2492
|
return "";
|
|
2599
2493
|
}
|
|
2600
|
-
return
|
|
2494
|
+
return pickTextLike(delta.content);
|
|
2601
2495
|
}
|
|
2602
2496
|
function pickAssistantReasoning(payload) {
|
|
2603
2497
|
const message = pickAssistantMessage(payload);
|
|
@@ -2905,7 +2799,7 @@ function pickResponsesReasoning(payload) {
|
|
|
2905
2799
|
function pickAssistantText(payload) {
|
|
2906
2800
|
const message = pickAssistantMessage(payload);
|
|
2907
2801
|
if (message) {
|
|
2908
|
-
const text =
|
|
2802
|
+
const text = pickTextLike(message.content);
|
|
2909
2803
|
if (text.length > 0) {
|
|
2910
2804
|
return text;
|
|
2911
2805
|
}
|
|
@@ -2934,9 +2828,6 @@ function joinReasoningBlocks(blocks) {
|
|
|
2934
2828
|
|
|
2935
2829
|
`);
|
|
2936
2830
|
}
|
|
2937
|
-
function pickTextFromOpenAIContent(value) {
|
|
2938
|
-
return pickTextLike(value);
|
|
2939
|
-
}
|
|
2940
2831
|
function pickTextLike(value) {
|
|
2941
2832
|
if (typeof value === "string") {
|
|
2942
2833
|
return value;
|
|
@@ -3010,83 +2901,81 @@ function createAnthropicCompatibleAdapter(options) {
|
|
|
3010
2901
|
if (hasMCPClients(request.mcpClients)) {
|
|
3011
2902
|
return streamWithMCPToolLoop(options, fetcher, path, request, callbacks);
|
|
3012
2903
|
}
|
|
3013
|
-
|
|
3014
|
-
const response = await fetcher(buildURL(options.baseURL, path), {
|
|
3015
|
-
method: "POST",
|
|
3016
|
-
headers: buildHeaders2(options),
|
|
3017
|
-
body: JSON.stringify(buildAnthropicRequestBody(options, request, {
|
|
3018
|
-
...options.defaultBody,
|
|
3019
|
-
...request.body,
|
|
3020
|
-
model: options.model,
|
|
3021
|
-
system: input.systemPrompt,
|
|
3022
|
-
messages: input.messages,
|
|
3023
|
-
temperature: request.temperature,
|
|
3024
|
-
stream: true
|
|
3025
|
-
})),
|
|
3026
|
-
signal: request.signal
|
|
3027
|
-
});
|
|
3028
|
-
if (!response.ok) {
|
|
3029
|
-
const message = await response.text();
|
|
3030
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
3031
|
-
}
|
|
3032
|
-
callbacks.onStart?.();
|
|
3033
|
-
let text = "";
|
|
3034
|
-
let usage;
|
|
3035
|
-
let finishReason;
|
|
3036
|
-
await consumeSSE(response, (data) => {
|
|
3037
|
-
if (data === "[DONE]") {
|
|
3038
|
-
return;
|
|
3039
|
-
}
|
|
3040
|
-
const json = safeJSONParse(data);
|
|
3041
|
-
if (!isRecord2(json)) {
|
|
3042
|
-
return;
|
|
3043
|
-
}
|
|
3044
|
-
const delta = pickAnthropicDelta(json);
|
|
3045
|
-
const chunkUsage = pickUsage2(json);
|
|
3046
|
-
const chunkFinishReason = pickFinishReason2(json);
|
|
3047
|
-
usage = preferLatestUsage(usage, chunkUsage);
|
|
3048
|
-
if (chunkFinishReason) {
|
|
3049
|
-
finishReason = chunkFinishReason;
|
|
3050
|
-
}
|
|
3051
|
-
if (delta) {
|
|
3052
|
-
text += delta;
|
|
3053
|
-
callbacks.onToken?.(delta);
|
|
3054
|
-
}
|
|
3055
|
-
if (delta || chunkUsage || chunkFinishReason) {
|
|
3056
|
-
const chunk = {
|
|
3057
|
-
textDelta: delta,
|
|
3058
|
-
raw: json,
|
|
3059
|
-
usage: chunkUsage,
|
|
3060
|
-
finishReason: chunkFinishReason
|
|
3061
|
-
};
|
|
3062
|
-
callbacks.onChunk?.(chunk);
|
|
3063
|
-
}
|
|
3064
|
-
});
|
|
3065
|
-
const out = { text, usage, finishReason };
|
|
3066
|
-
callbacks.onComplete?.(out);
|
|
3067
|
-
return out;
|
|
2904
|
+
return streamPassThrough(options, fetcher, path, request, callbacks);
|
|
3068
2905
|
},
|
|
3069
2906
|
async embed() {
|
|
3070
2907
|
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.");
|
|
3071
2908
|
}
|
|
3072
2909
|
};
|
|
3073
2910
|
}
|
|
3074
|
-
async function
|
|
2911
|
+
async function streamPassThrough(options, fetcher, path, request, callbacks) {
|
|
3075
2912
|
const input = resolveAnthropicInput(request);
|
|
3076
|
-
const response = await
|
|
2913
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
2914
|
+
system: input.systemPrompt,
|
|
2915
|
+
messages: input.messages,
|
|
2916
|
+
stream: true
|
|
2917
|
+
});
|
|
2918
|
+
if (!response.ok) {
|
|
2919
|
+
const message = await response.text();
|
|
2920
|
+
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2921
|
+
}
|
|
2922
|
+
callbacks.onStart?.();
|
|
2923
|
+
let text = "";
|
|
2924
|
+
let usage;
|
|
2925
|
+
let finishReason;
|
|
2926
|
+
await consumeSSE(response, (data) => {
|
|
2927
|
+
if (data === "[DONE]") {
|
|
2928
|
+
return;
|
|
2929
|
+
}
|
|
2930
|
+
const json = safeJSONParse(data);
|
|
2931
|
+
if (!isRecord2(json)) {
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
2934
|
+
const delta = pickAnthropicDelta(json);
|
|
2935
|
+
const chunkUsage = pickUsage2(json);
|
|
2936
|
+
const chunkFinishReason = pickFinishReason2(json);
|
|
2937
|
+
usage = preferLatestUsage(usage, chunkUsage);
|
|
2938
|
+
if (chunkFinishReason) {
|
|
2939
|
+
finishReason = chunkFinishReason;
|
|
2940
|
+
}
|
|
2941
|
+
if (delta) {
|
|
2942
|
+
text += delta;
|
|
2943
|
+
callbacks.onToken?.(delta);
|
|
2944
|
+
}
|
|
2945
|
+
if (delta || chunkUsage || chunkFinishReason) {
|
|
2946
|
+
callbacks.onChunk?.({
|
|
2947
|
+
textDelta: delta,
|
|
2948
|
+
raw: json,
|
|
2949
|
+
usage: chunkUsage,
|
|
2950
|
+
finishReason: chunkFinishReason
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
});
|
|
2954
|
+
const out = { text, usage, finishReason };
|
|
2955
|
+
callbacks.onComplete?.(out);
|
|
2956
|
+
return out;
|
|
2957
|
+
}
|
|
2958
|
+
function sendAnthropicMessage(options, fetcher, path, request, body) {
|
|
2959
|
+
return fetcher(buildURL(options.baseURL, path), {
|
|
3077
2960
|
method: "POST",
|
|
3078
2961
|
headers: buildHeaders2(options),
|
|
3079
2962
|
body: JSON.stringify(buildAnthropicRequestBody(options, request, {
|
|
3080
2963
|
...options.defaultBody,
|
|
3081
2964
|
...request.body,
|
|
3082
2965
|
model: options.model,
|
|
3083
|
-
system: input.systemPrompt,
|
|
3084
|
-
messages: input.messages,
|
|
3085
2966
|
temperature: request.temperature,
|
|
3086
|
-
|
|
2967
|
+
...body
|
|
3087
2968
|
})),
|
|
3088
2969
|
signal: request.signal
|
|
3089
2970
|
});
|
|
2971
|
+
}
|
|
2972
|
+
async function completePassThrough(options, fetcher, path, request) {
|
|
2973
|
+
const input = resolveAnthropicInput(request);
|
|
2974
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
2975
|
+
system: input.systemPrompt,
|
|
2976
|
+
messages: input.messages,
|
|
2977
|
+
stream: false
|
|
2978
|
+
});
|
|
3090
2979
|
if (!response.ok) {
|
|
3091
2980
|
const message = await response.text();
|
|
3092
2981
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
@@ -3118,21 +3007,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
3118
3007
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
3119
3008
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
3120
3009
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
3121
|
-
const response = await
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
model: options.model,
|
|
3128
|
-
system: input.systemPrompt,
|
|
3129
|
-
messages,
|
|
3130
|
-
temperature: request.temperature,
|
|
3131
|
-
tools,
|
|
3132
|
-
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3133
|
-
stream: false
|
|
3134
|
-
})),
|
|
3135
|
-
signal: request.signal
|
|
3010
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
3011
|
+
system: input.systemPrompt,
|
|
3012
|
+
messages,
|
|
3013
|
+
tools,
|
|
3014
|
+
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3015
|
+
stream: false
|
|
3136
3016
|
});
|
|
3137
3017
|
if (!response.ok) {
|
|
3138
3018
|
const message = await response.text();
|
|
@@ -3208,21 +3088,12 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3208
3088
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
3209
3089
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
3210
3090
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
3211
|
-
const response = await
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
model: options.model,
|
|
3218
|
-
system: input.systemPrompt,
|
|
3219
|
-
messages,
|
|
3220
|
-
temperature: request.temperature,
|
|
3221
|
-
tools,
|
|
3222
|
-
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3223
|
-
stream: true
|
|
3224
|
-
})),
|
|
3225
|
-
signal: request.signal
|
|
3091
|
+
const response = await sendAnthropicMessage(options, fetcher, path, request, {
|
|
3092
|
+
system: input.systemPrompt,
|
|
3093
|
+
messages,
|
|
3094
|
+
tools,
|
|
3095
|
+
tool_choice: toAnthropicToolChoice(request.toolChoice),
|
|
3096
|
+
stream: true
|
|
3226
3097
|
});
|
|
3227
3098
|
if (!response.ok) {
|
|
3228
3099
|
const message = await response.text();
|
|
@@ -3766,39 +3637,10 @@ function buildProviderOptions(config) {
|
|
|
3766
3637
|
return {
|
|
3767
3638
|
model: config.model,
|
|
3768
3639
|
...transport,
|
|
3769
|
-
...config.options
|
|
3640
|
+
...config.options
|
|
3770
3641
|
};
|
|
3771
3642
|
}
|
|
3772
3643
|
|
|
3773
|
-
// src/utils/debug-colors.ts
|
|
3774
|
-
var ANSI = {
|
|
3775
|
-
reset: "\x1B[0m",
|
|
3776
|
-
bold: "\x1B[1m",
|
|
3777
|
-
cyan: "\x1B[36m",
|
|
3778
|
-
yellow: "\x1B[33m",
|
|
3779
|
-
green: "\x1B[32m",
|
|
3780
|
-
red: "\x1B[31m",
|
|
3781
|
-
dim: "\x1B[2m"
|
|
3782
|
-
};
|
|
3783
|
-
function color(config, text, tone) {
|
|
3784
|
-
if (!config.colors) {
|
|
3785
|
-
return text;
|
|
3786
|
-
}
|
|
3787
|
-
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
3788
|
-
}
|
|
3789
|
-
function dim(config, text) {
|
|
3790
|
-
if (!config.colors) {
|
|
3791
|
-
return text;
|
|
3792
|
-
}
|
|
3793
|
-
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
3794
|
-
}
|
|
3795
|
-
function title(config, text) {
|
|
3796
|
-
if (!config.colors) {
|
|
3797
|
-
return text;
|
|
3798
|
-
}
|
|
3799
|
-
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
3800
|
-
}
|
|
3801
|
-
|
|
3802
3644
|
// src/outdent.ts
|
|
3803
3645
|
var DEFAULT_OPTIONS = {
|
|
3804
3646
|
trimLeadingNewline: true,
|
|
@@ -3936,132 +3778,213 @@ function createOutdent(options = {}) {
|
|
|
3936
3778
|
return outdent;
|
|
3937
3779
|
}
|
|
3938
3780
|
|
|
3939
|
-
// src/generate-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3781
|
+
// src/generate-tool-timeout.ts
|
|
3782
|
+
function withToolTimeout(client, toolTimeoutMs) {
|
|
3783
|
+
return {
|
|
3784
|
+
id: client.id,
|
|
3785
|
+
listTools: client.listTools.bind(client),
|
|
3786
|
+
close: client.close?.bind(client),
|
|
3787
|
+
async callTool(params) {
|
|
3788
|
+
let timeoutId;
|
|
3789
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
3790
|
+
timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
|
|
3791
|
+
});
|
|
3792
|
+
try {
|
|
3793
|
+
return await Promise.race([client.callTool(params), timeoutPromise]);
|
|
3794
|
+
} finally {
|
|
3795
|
+
clearTimeout(timeoutId);
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
function applyToolTimeout(clients, toolTimeoutMs) {
|
|
3801
|
+
return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
|
|
3802
|
+
}
|
|
3803
|
+
// src/generate-output.ts
|
|
3946
3804
|
var RE_THINK_TAGS = /<\/?think\s*>/gi;
|
|
3947
|
-
function
|
|
3948
|
-
const
|
|
3949
|
-
|
|
3805
|
+
function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
|
|
3806
|
+
const sanitized = sanitizeThink(text);
|
|
3807
|
+
const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
|
|
3808
|
+
const reasoning = joinReasoningSegments([
|
|
3809
|
+
dedicatedReasoning,
|
|
3810
|
+
...sanitized.thinkBlocks.map((block) => block.content)
|
|
3811
|
+
]);
|
|
3812
|
+
return {
|
|
3813
|
+
text: visibleText,
|
|
3814
|
+
reasoning,
|
|
3815
|
+
reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
|
|
3816
|
+
thinkBlocks: sanitized.thinkBlocks,
|
|
3817
|
+
parseSource: composeParseSource(visibleText, reasoning)
|
|
3818
|
+
};
|
|
3950
3819
|
}
|
|
3951
|
-
function
|
|
3952
|
-
if (
|
|
3953
|
-
return
|
|
3954
|
-
prompt: value
|
|
3955
|
-
};
|
|
3956
|
-
}
|
|
3957
|
-
if (isPromptResolver(value)) {
|
|
3958
|
-
return normalizePromptPayload(value.resolvePrompt(_context));
|
|
3820
|
+
function normalizeReasoningBlocks(blocks) {
|
|
3821
|
+
if (!Array.isArray(blocks)) {
|
|
3822
|
+
return;
|
|
3959
3823
|
}
|
|
3960
|
-
|
|
3824
|
+
const normalized = blocks.map((block) => ({
|
|
3825
|
+
turnIndex: block.turnIndex,
|
|
3826
|
+
text: block.text.replace(RE_THINK_TAGS, "").trim()
|
|
3827
|
+
})).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
|
|
3828
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
3961
3829
|
}
|
|
3962
|
-
function
|
|
3963
|
-
const
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
|
|
3830
|
+
function appendReasoningBlock(blocks, transition) {
|
|
3831
|
+
const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
|
|
3832
|
+
if (!text) {
|
|
3833
|
+
return blocks;
|
|
3967
3834
|
}
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
|
|
3971
|
-
messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
|
|
3972
|
-
};
|
|
3835
|
+
const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
|
|
3836
|
+
return normalizeReasoningBlocks(next);
|
|
3973
3837
|
}
|
|
3974
|
-
function
|
|
3975
|
-
if (
|
|
3976
|
-
return
|
|
3838
|
+
function composeParseSource(text, reasoning) {
|
|
3839
|
+
if (typeof reasoning !== "string" || reasoning.length === 0) {
|
|
3840
|
+
return text;
|
|
3977
3841
|
}
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
|
|
3984
|
-
}))
|
|
3985
|
-
};
|
|
3842
|
+
const sanitized = reasoning.replace(RE_THINK_TAGS, "");
|
|
3843
|
+
if (sanitized.length === 0) {
|
|
3844
|
+
return text;
|
|
3845
|
+
}
|
|
3846
|
+
return `<think>${sanitized}</think>${text}`;
|
|
3986
3847
|
}
|
|
3987
|
-
function
|
|
3988
|
-
|
|
3989
|
-
|
|
3848
|
+
function aggregateUsage(attempts) {
|
|
3849
|
+
let usage;
|
|
3850
|
+
for (const attempt of attempts) {
|
|
3851
|
+
usage = mergeUsage2(usage, attempt.usage);
|
|
3990
3852
|
}
|
|
3991
|
-
return
|
|
3853
|
+
return usage;
|
|
3992
3854
|
}
|
|
3993
|
-
function
|
|
3994
|
-
|
|
3995
|
-
if (prompts.length === 0) {
|
|
3855
|
+
function mergeUsage2(base, next) {
|
|
3856
|
+
if (!base && !next) {
|
|
3996
3857
|
return;
|
|
3997
3858
|
}
|
|
3998
|
-
return
|
|
3859
|
+
return {
|
|
3860
|
+
inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
|
|
3861
|
+
outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
|
|
3862
|
+
totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
|
|
3863
|
+
cost: (base?.cost ?? 0) + (next?.cost ?? 0)
|
|
3864
|
+
};
|
|
3865
|
+
}
|
|
3866
|
+
function joinReasoningSegments(parts) {
|
|
3867
|
+
return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
|
|
3999
3868
|
|
|
4000
3869
|
`);
|
|
4001
3870
|
}
|
|
4002
|
-
function
|
|
4003
|
-
if (
|
|
4004
|
-
return
|
|
4005
|
-
enabled: option
|
|
4006
|
-
};
|
|
3871
|
+
function stripThinkBlocks(text, thinkBlocks) {
|
|
3872
|
+
if (thinkBlocks.length === 0) {
|
|
3873
|
+
return text;
|
|
4007
3874
|
}
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
3875
|
+
let output = "";
|
|
3876
|
+
let cursor = 0;
|
|
3877
|
+
for (const block of thinkBlocks) {
|
|
3878
|
+
output += text.slice(cursor, block.start);
|
|
3879
|
+
cursor = block.end;
|
|
4012
3880
|
}
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
onData: option.onData,
|
|
4016
|
-
onTurnTransition: option.onTurnTransition,
|
|
4017
|
-
to: option.to
|
|
4018
|
-
};
|
|
3881
|
+
output += text.slice(cursor);
|
|
3882
|
+
return output;
|
|
4019
3883
|
}
|
|
4020
|
-
function
|
|
4021
|
-
|
|
4022
|
-
return
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
verbose: false,
|
|
4026
|
-
logger: (line) => console.log(line)
|
|
4027
|
-
};
|
|
3884
|
+
function toStreamDataFingerprint(value) {
|
|
3885
|
+
try {
|
|
3886
|
+
return JSON.stringify(value);
|
|
3887
|
+
} catch {
|
|
3888
|
+
return "__unserializable__";
|
|
4028
3889
|
}
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3892
|
+
// src/utils/debug-colors.ts
|
|
3893
|
+
var ANSI = {
|
|
3894
|
+
reset: "\x1B[0m",
|
|
3895
|
+
bold: "\x1B[1m",
|
|
3896
|
+
cyan: "\x1B[36m",
|
|
3897
|
+
yellow: "\x1B[33m",
|
|
3898
|
+
green: "\x1B[32m",
|
|
3899
|
+
red: "\x1B[31m",
|
|
3900
|
+
dim: "\x1B[2m"
|
|
3901
|
+
};
|
|
3902
|
+
function color(config, text, tone) {
|
|
3903
|
+
if (!config.colors) {
|
|
3904
|
+
return text;
|
|
4036
3905
|
}
|
|
4037
|
-
return {
|
|
4038
|
-
enabled: option.enabled ?? true,
|
|
4039
|
-
colors: option.colors ?? true,
|
|
4040
|
-
verbose: option.verbose ?? false,
|
|
4041
|
-
logger: option.logger ?? ((line) => console.log(line))
|
|
4042
|
-
};
|
|
3906
|
+
return `${ANSI[tone]}${text}${ANSI.reset}`;
|
|
4043
3907
|
}
|
|
4044
|
-
function
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
async callTool(params) {
|
|
4050
|
-
let timeoutId;
|
|
4051
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
4052
|
-
timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
|
|
4053
|
-
});
|
|
4054
|
-
try {
|
|
4055
|
-
return await Promise.race([client.callTool(params), timeoutPromise]);
|
|
4056
|
-
} finally {
|
|
4057
|
-
clearTimeout(timeoutId);
|
|
4058
|
-
}
|
|
4059
|
-
}
|
|
4060
|
-
};
|
|
3908
|
+
function dim(config, text) {
|
|
3909
|
+
if (!config.colors) {
|
|
3910
|
+
return text;
|
|
3911
|
+
}
|
|
3912
|
+
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
4061
3913
|
}
|
|
4062
|
-
function
|
|
4063
|
-
|
|
3914
|
+
function title(config, text) {
|
|
3915
|
+
if (!config.colors) {
|
|
3916
|
+
return text;
|
|
3917
|
+
}
|
|
3918
|
+
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
4064
3919
|
}
|
|
3920
|
+
|
|
3921
|
+
// src/generate-debug.ts
|
|
3922
|
+
function emitDebugRequest(config, input) {
|
|
3923
|
+
const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
|
|
3924
|
+
const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
|
|
3925
|
+
const lines = [
|
|
3926
|
+
color(config, title(config, [
|
|
3927
|
+
`[${input.label}][request]`,
|
|
3928
|
+
`attempt=${input.attempt}`,
|
|
3929
|
+
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
3930
|
+
`selfHealAttempt=${input.selfHealAttempt}`
|
|
3931
|
+
].join(" ")), "cyan"),
|
|
3932
|
+
dim(config, [
|
|
3933
|
+
`provider=${input.provider ?? "unknown"}`,
|
|
3934
|
+
`model=${input.model ?? "unknown"}`,
|
|
3935
|
+
`stream=${input.stream}`
|
|
3936
|
+
].join(" ")),
|
|
3937
|
+
color(config, "prompt:", "yellow"),
|
|
3938
|
+
input.requestPayload.prompt ?? "(none)",
|
|
3939
|
+
color(config, "messages:", "yellow"),
|
|
3940
|
+
requestMessages,
|
|
3941
|
+
color(config, "systemPrompt:", "yellow"),
|
|
3942
|
+
input.requestPayload.systemPrompt ?? "(none)",
|
|
3943
|
+
color(config, "request.body:", "yellow"),
|
|
3944
|
+
requestBody
|
|
3945
|
+
];
|
|
3946
|
+
emitDebug(config, lines.join(`
|
|
3947
|
+
`));
|
|
3948
|
+
}
|
|
3949
|
+
function emitDebugResponse(config, input) {
|
|
3950
|
+
const text = input.text.length > 0 ? input.text : "(none)";
|
|
3951
|
+
const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
|
|
3952
|
+
const metadata = [
|
|
3953
|
+
`via=${input.via}`,
|
|
3954
|
+
`textChars=${input.text.length}`,
|
|
3955
|
+
`reasoningChars=${input.reasoning.length}`
|
|
3956
|
+
];
|
|
3957
|
+
if (config.verbose) {
|
|
3958
|
+
metadata.push(`parseSourceChars=${input.parseSource.length}`);
|
|
3959
|
+
}
|
|
3960
|
+
metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
|
|
3961
|
+
const lines = [
|
|
3962
|
+
color(config, title(config, [
|
|
3963
|
+
`[${input.label}][response]`,
|
|
3964
|
+
`attempt=${input.attempt}`,
|
|
3965
|
+
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
3966
|
+
`selfHealAttempt=${input.selfHealAttempt}`
|
|
3967
|
+
].join(" ")), "green"),
|
|
3968
|
+
dim(config, metadata.join(" ")),
|
|
3969
|
+
color(config, "text:", "yellow"),
|
|
3970
|
+
text,
|
|
3971
|
+
color(config, "reasoning:", "yellow"),
|
|
3972
|
+
reasoning
|
|
3973
|
+
];
|
|
3974
|
+
if (config.verbose) {
|
|
3975
|
+
lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
|
|
3976
|
+
}
|
|
3977
|
+
emitDebug(config, lines.join(`
|
|
3978
|
+
`));
|
|
3979
|
+
}
|
|
3980
|
+
function emitDebug(config, message) {
|
|
3981
|
+
if (!config.enabled) {
|
|
3982
|
+
return;
|
|
3983
|
+
}
|
|
3984
|
+
config.logger(message);
|
|
3985
|
+
}
|
|
3986
|
+
|
|
3987
|
+
// src/generate-model-call.ts
|
|
4065
3988
|
async function callModel(adapter, options) {
|
|
4066
3989
|
const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
|
|
4067
3990
|
const requestPayload = {
|
|
@@ -4281,67 +4204,115 @@ async function callModel(adapter, options) {
|
|
|
4281
4204
|
reasoningBlocks: normalized.reasoningBlocks
|
|
4282
4205
|
};
|
|
4283
4206
|
}
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4207
|
+
|
|
4208
|
+
// src/generate-shared.ts
|
|
4209
|
+
var sharedOutdent = createOutdent({
|
|
4210
|
+
trimLeadingNewline: true,
|
|
4211
|
+
trimTrailingNewline: true,
|
|
4212
|
+
newline: `
|
|
4213
|
+
`
|
|
4214
|
+
});
|
|
4215
|
+
function resolvePrompt(prompt, context) {
|
|
4216
|
+
const resolved = typeof prompt === "function" ? prompt(context) : prompt;
|
|
4217
|
+
return normalizePromptValue(resolved, context);
|
|
4218
|
+
}
|
|
4219
|
+
function normalizePromptValue(value, _context) {
|
|
4220
|
+
if (typeof value === "string") {
|
|
4221
|
+
return {
|
|
4222
|
+
prompt: value
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
if (isPromptResolver(value)) {
|
|
4226
|
+
return normalizePromptPayload(value.resolvePrompt(_context));
|
|
4227
|
+
}
|
|
4228
|
+
return normalizePromptPayload(value);
|
|
4229
|
+
}
|
|
4230
|
+
function normalizePromptPayload(value) {
|
|
4231
|
+
const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
|
|
4232
|
+
const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
|
|
4233
|
+
if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
|
|
4234
|
+
throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
|
|
4235
|
+
}
|
|
4291
4236
|
return {
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
thinkBlocks: sanitized.thinkBlocks,
|
|
4296
|
-
parseSource: composeParseSource(visibleText, reasoning)
|
|
4237
|
+
prompt,
|
|
4238
|
+
systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
|
|
4239
|
+
messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
|
|
4297
4240
|
};
|
|
4298
4241
|
}
|
|
4299
|
-
function
|
|
4300
|
-
if (!
|
|
4301
|
-
return;
|
|
4242
|
+
function applyPromptOutdent(payload, enabled) {
|
|
4243
|
+
if (!enabled) {
|
|
4244
|
+
return payload;
|
|
4302
4245
|
}
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4246
|
+
return {
|
|
4247
|
+
prompt: typeof payload.prompt === "string" ? sharedOutdent.string(payload.prompt) : undefined,
|
|
4248
|
+
systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
|
|
4249
|
+
messages: payload.messages?.map((message) => ({
|
|
4250
|
+
...message,
|
|
4251
|
+
content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
|
|
4252
|
+
}))
|
|
4253
|
+
};
|
|
4308
4254
|
}
|
|
4309
|
-
function
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
return blocks;
|
|
4255
|
+
function applyOutdentToOptionalPrompt(value, enabled) {
|
|
4256
|
+
if (!enabled || typeof value !== "string") {
|
|
4257
|
+
return value;
|
|
4313
4258
|
}
|
|
4314
|
-
|
|
4315
|
-
return normalizeReasoningBlocks(next);
|
|
4259
|
+
return sharedOutdent.string(value);
|
|
4316
4260
|
}
|
|
4317
|
-
function
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
const sanitized = reasoning.replace(RE_THINK_TAGS, "");
|
|
4322
|
-
if (sanitized.length === 0) {
|
|
4323
|
-
return text;
|
|
4261
|
+
function mergeSystemPrompts(primary, secondary) {
|
|
4262
|
+
const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
4263
|
+
if (prompts.length === 0) {
|
|
4264
|
+
return;
|
|
4324
4265
|
}
|
|
4325
|
-
return
|
|
4266
|
+
return prompts.join(`
|
|
4267
|
+
|
|
4268
|
+
`);
|
|
4326
4269
|
}
|
|
4327
|
-
function
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4270
|
+
function normalizeStreamConfig(option) {
|
|
4271
|
+
if (typeof option === "boolean") {
|
|
4272
|
+
return {
|
|
4273
|
+
enabled: option
|
|
4274
|
+
};
|
|
4275
|
+
}
|
|
4276
|
+
if (!option) {
|
|
4277
|
+
return {
|
|
4278
|
+
enabled: false
|
|
4279
|
+
};
|
|
4331
4280
|
}
|
|
4332
|
-
return
|
|
4281
|
+
return {
|
|
4282
|
+
enabled: option.enabled ?? true,
|
|
4283
|
+
onData: option.onData,
|
|
4284
|
+
onTurnTransition: option.onTurnTransition,
|
|
4285
|
+
to: option.to
|
|
4286
|
+
};
|
|
4333
4287
|
}
|
|
4334
|
-
function
|
|
4335
|
-
if (
|
|
4336
|
-
return
|
|
4288
|
+
function normalizeDebugConfig(option) {
|
|
4289
|
+
if (typeof option === "boolean") {
|
|
4290
|
+
return {
|
|
4291
|
+
enabled: option,
|
|
4292
|
+
colors: true,
|
|
4293
|
+
verbose: false,
|
|
4294
|
+
logger: defaultDebugLogger
|
|
4295
|
+
};
|
|
4296
|
+
}
|
|
4297
|
+
if (!option) {
|
|
4298
|
+
return {
|
|
4299
|
+
enabled: false,
|
|
4300
|
+
colors: true,
|
|
4301
|
+
verbose: false,
|
|
4302
|
+
logger: defaultDebugLogger
|
|
4303
|
+
};
|
|
4337
4304
|
}
|
|
4338
4305
|
return {
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4306
|
+
enabled: option.enabled ?? true,
|
|
4307
|
+
colors: option.colors ?? true,
|
|
4308
|
+
verbose: option.verbose ?? false,
|
|
4309
|
+
logger: option.logger ?? defaultDebugLogger
|
|
4343
4310
|
};
|
|
4344
4311
|
}
|
|
4312
|
+
function defaultDebugLogger(line) {
|
|
4313
|
+
const { log } = globalThis.console;
|
|
4314
|
+
log(line);
|
|
4315
|
+
}
|
|
4345
4316
|
function isPromptResolver(value) {
|
|
4346
4317
|
return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
|
|
4347
4318
|
}
|
|
@@ -4355,95 +4326,6 @@ function isLLMMessage(value) {
|
|
|
4355
4326
|
}
|
|
4356
4327
|
return "content" in candidate;
|
|
4357
4328
|
}
|
|
4358
|
-
function joinReasoningSegments(parts) {
|
|
4359
|
-
return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
|
|
4360
|
-
|
|
4361
|
-
`);
|
|
4362
|
-
}
|
|
4363
|
-
function stripThinkBlocks(text, thinkBlocks) {
|
|
4364
|
-
if (thinkBlocks.length === 0) {
|
|
4365
|
-
return text;
|
|
4366
|
-
}
|
|
4367
|
-
let output = "";
|
|
4368
|
-
let cursor = 0;
|
|
4369
|
-
for (const block of thinkBlocks) {
|
|
4370
|
-
output += text.slice(cursor, block.start);
|
|
4371
|
-
cursor = block.end;
|
|
4372
|
-
}
|
|
4373
|
-
output += text.slice(cursor);
|
|
4374
|
-
return output;
|
|
4375
|
-
}
|
|
4376
|
-
function toStreamDataFingerprint(value) {
|
|
4377
|
-
try {
|
|
4378
|
-
return JSON.stringify(value);
|
|
4379
|
-
} catch {
|
|
4380
|
-
return "__unserializable__";
|
|
4381
|
-
}
|
|
4382
|
-
}
|
|
4383
|
-
function emitDebugRequest(config, input) {
|
|
4384
|
-
const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
|
|
4385
|
-
const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
|
|
4386
|
-
const lines = [
|
|
4387
|
-
color(config, title(config, [
|
|
4388
|
-
`[${input.label}][request]`,
|
|
4389
|
-
`attempt=${input.attempt}`,
|
|
4390
|
-
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
4391
|
-
`selfHealAttempt=${input.selfHealAttempt}`
|
|
4392
|
-
].join(" ")), "cyan"),
|
|
4393
|
-
dim(config, [
|
|
4394
|
-
`provider=${input.provider ?? "unknown"}`,
|
|
4395
|
-
`model=${input.model ?? "unknown"}`,
|
|
4396
|
-
`stream=${input.stream}`
|
|
4397
|
-
].join(" ")),
|
|
4398
|
-
color(config, "prompt:", "yellow"),
|
|
4399
|
-
input.requestPayload.prompt ?? "(none)",
|
|
4400
|
-
color(config, "messages:", "yellow"),
|
|
4401
|
-
requestMessages,
|
|
4402
|
-
color(config, "systemPrompt:", "yellow"),
|
|
4403
|
-
input.requestPayload.systemPrompt ?? "(none)",
|
|
4404
|
-
color(config, "request.body:", "yellow"),
|
|
4405
|
-
requestBody
|
|
4406
|
-
];
|
|
4407
|
-
emitDebug(config, lines.join(`
|
|
4408
|
-
`));
|
|
4409
|
-
}
|
|
4410
|
-
function emitDebugResponse(config, input) {
|
|
4411
|
-
const text = input.text.length > 0 ? input.text : "(none)";
|
|
4412
|
-
const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
|
|
4413
|
-
const metadata = [
|
|
4414
|
-
`via=${input.via}`,
|
|
4415
|
-
`textChars=${input.text.length}`,
|
|
4416
|
-
`reasoningChars=${input.reasoning.length}`
|
|
4417
|
-
];
|
|
4418
|
-
if (config.verbose) {
|
|
4419
|
-
metadata.push(`parseSourceChars=${input.parseSource.length}`);
|
|
4420
|
-
}
|
|
4421
|
-
metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
|
|
4422
|
-
const lines = [
|
|
4423
|
-
color(config, title(config, [
|
|
4424
|
-
`[${input.label}][response]`,
|
|
4425
|
-
`attempt=${input.attempt}`,
|
|
4426
|
-
`selfHealEnabled=${input.selfHealEnabled}`,
|
|
4427
|
-
`selfHealAttempt=${input.selfHealAttempt}`
|
|
4428
|
-
].join(" ")), "green"),
|
|
4429
|
-
dim(config, metadata.join(" ")),
|
|
4430
|
-
color(config, "text:", "yellow"),
|
|
4431
|
-
text,
|
|
4432
|
-
color(config, "reasoning:", "yellow"),
|
|
4433
|
-
reasoning
|
|
4434
|
-
];
|
|
4435
|
-
if (config.verbose) {
|
|
4436
|
-
lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
|
|
4437
|
-
}
|
|
4438
|
-
emitDebug(config, lines.join(`
|
|
4439
|
-
`));
|
|
4440
|
-
}
|
|
4441
|
-
function emitDebug(config, message) {
|
|
4442
|
-
if (!config.enabled) {
|
|
4443
|
-
return;
|
|
4444
|
-
}
|
|
4445
|
-
config.logger(message);
|
|
4446
|
-
}
|
|
4447
4329
|
|
|
4448
4330
|
// src/generate.ts
|
|
4449
4331
|
async function generate(adapter, promptOrOptions, callOptions) {
|
|
@@ -4519,7 +4401,7 @@ function normalizeGenerateInput(promptOrOptions, callOptions) {
|
|
|
4519
4401
|
throw new Error("Missing prompt in generate(adapter, prompt, options?) call.");
|
|
4520
4402
|
}
|
|
4521
4403
|
return {
|
|
4522
|
-
...callOptions
|
|
4404
|
+
...callOptions,
|
|
4523
4405
|
prompt: promptOrOptions
|
|
4524
4406
|
};
|
|
4525
4407
|
}
|
|
@@ -4545,9 +4427,6 @@ function prepareGeneratePromptPayload(payload, systemPrompt) {
|
|
|
4545
4427
|
};
|
|
4546
4428
|
}
|
|
4547
4429
|
|
|
4548
|
-
// src/structured.ts
|
|
4549
|
-
var import_jsonrepair3 = require("jsonrepair");
|
|
4550
|
-
|
|
4551
4430
|
// src/parse.ts
|
|
4552
4431
|
var import_jsonrepair2 = require("jsonrepair");
|
|
4553
4432
|
function parseLLMOutput(output, schema, options = {}) {
|
|
@@ -4847,6 +4726,80 @@ function formatZodIssues(issues) {
|
|
|
4847
4726
|
`);
|
|
4848
4727
|
}
|
|
4849
4728
|
|
|
4729
|
+
// src/structured-streaming.ts
|
|
4730
|
+
var import_jsonrepair3 = require("jsonrepair");
|
|
4731
|
+
function parseStreamingStructuredData(parseSource) {
|
|
4732
|
+
const sanitized = sanitizeThink(parseSource);
|
|
4733
|
+
const start = findFirstJsonRootStart(sanitized.visibleText);
|
|
4734
|
+
if (start < 0) {
|
|
4735
|
+
return null;
|
|
4736
|
+
}
|
|
4737
|
+
const candidate = sanitized.visibleText.slice(start).trim();
|
|
4738
|
+
if (!candidate) {
|
|
4739
|
+
return null;
|
|
4740
|
+
}
|
|
4741
|
+
try {
|
|
4742
|
+
const repaired = import_jsonrepair3.jsonrepair(candidate);
|
|
4743
|
+
const parsed = JSON.parse(repaired);
|
|
4744
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
4745
|
+
return null;
|
|
4746
|
+
}
|
|
4747
|
+
return parsed;
|
|
4748
|
+
} catch {
|
|
4749
|
+
return null;
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
function findFirstJsonRootStart(input) {
|
|
4753
|
+
const unquotedRootStart = findFirstUnquotedJsonRootStart(input);
|
|
4754
|
+
if (unquotedRootStart >= 0) {
|
|
4755
|
+
return unquotedRootStart;
|
|
4756
|
+
}
|
|
4757
|
+
return findFirstRawJsonRootStart(input);
|
|
4758
|
+
}
|
|
4759
|
+
function findFirstUnquotedJsonRootStart(input) {
|
|
4760
|
+
let inString = false;
|
|
4761
|
+
let escaped = false;
|
|
4762
|
+
for (let index = 0;index < input.length; index += 1) {
|
|
4763
|
+
const char = input[index];
|
|
4764
|
+
if (!char) {
|
|
4765
|
+
continue;
|
|
4766
|
+
}
|
|
4767
|
+
if (inString) {
|
|
4768
|
+
if (escaped) {
|
|
4769
|
+
escaped = false;
|
|
4770
|
+
continue;
|
|
4771
|
+
}
|
|
4772
|
+
if (char === "\\") {
|
|
4773
|
+
escaped = true;
|
|
4774
|
+
continue;
|
|
4775
|
+
}
|
|
4776
|
+
if (char === '"') {
|
|
4777
|
+
inString = false;
|
|
4778
|
+
}
|
|
4779
|
+
continue;
|
|
4780
|
+
}
|
|
4781
|
+
if (char === '"') {
|
|
4782
|
+
inString = true;
|
|
4783
|
+
continue;
|
|
4784
|
+
}
|
|
4785
|
+
if (char === "{" || char === "[") {
|
|
4786
|
+
return index;
|
|
4787
|
+
}
|
|
4788
|
+
}
|
|
4789
|
+
return -1;
|
|
4790
|
+
}
|
|
4791
|
+
function findFirstRawJsonRootStart(input) {
|
|
4792
|
+
const objectStart = input.indexOf("{");
|
|
4793
|
+
const arrayStart = input.indexOf("[");
|
|
4794
|
+
if (objectStart < 0) {
|
|
4795
|
+
return arrayStart;
|
|
4796
|
+
}
|
|
4797
|
+
if (arrayStart < 0) {
|
|
4798
|
+
return objectStart;
|
|
4799
|
+
}
|
|
4800
|
+
return Math.min(objectStart, arrayStart);
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4850
4803
|
// src/structured.ts
|
|
4851
4804
|
class StructuredParseError extends Error {
|
|
4852
4805
|
name = "StructuredParseError";
|
|
@@ -5084,7 +5037,7 @@ function normalizeStructuredInput(schemaOrOptions, promptInput, callOptions) {
|
|
|
5084
5037
|
throw new Error("Missing prompt in structured(adapter, schema, prompt, options?) call.");
|
|
5085
5038
|
}
|
|
5086
5039
|
return {
|
|
5087
|
-
...callOptions
|
|
5040
|
+
...callOptions,
|
|
5088
5041
|
schema: schemaOrOptions,
|
|
5089
5042
|
prompt: promptInput
|
|
5090
5043
|
};
|
|
@@ -5355,67 +5308,6 @@ async function callModel2(adapter, options) {
|
|
|
5355
5308
|
debugLabel: "structured"
|
|
5356
5309
|
});
|
|
5357
5310
|
}
|
|
5358
|
-
function parseStreamingStructuredData(parseSource) {
|
|
5359
|
-
const sanitized = sanitizeThink(parseSource);
|
|
5360
|
-
const start = findFirstJsonRootStart(sanitized.visibleText);
|
|
5361
|
-
if (start < 0) {
|
|
5362
|
-
return null;
|
|
5363
|
-
}
|
|
5364
|
-
const candidate = sanitized.visibleText.slice(start).trim();
|
|
5365
|
-
if (!candidate) {
|
|
5366
|
-
return null;
|
|
5367
|
-
}
|
|
5368
|
-
try {
|
|
5369
|
-
const repaired = import_jsonrepair3.jsonrepair(candidate);
|
|
5370
|
-
const parsed = JSON.parse(repaired);
|
|
5371
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
5372
|
-
return null;
|
|
5373
|
-
}
|
|
5374
|
-
return parsed;
|
|
5375
|
-
} catch {
|
|
5376
|
-
return null;
|
|
5377
|
-
}
|
|
5378
|
-
}
|
|
5379
|
-
function findFirstJsonRootStart(input) {
|
|
5380
|
-
let inString = false;
|
|
5381
|
-
let escaped = false;
|
|
5382
|
-
for (let index = 0;index < input.length; index += 1) {
|
|
5383
|
-
const char = input[index];
|
|
5384
|
-
if (!char) {
|
|
5385
|
-
continue;
|
|
5386
|
-
}
|
|
5387
|
-
if (inString) {
|
|
5388
|
-
if (escaped) {
|
|
5389
|
-
escaped = false;
|
|
5390
|
-
continue;
|
|
5391
|
-
}
|
|
5392
|
-
if (char === "\\") {
|
|
5393
|
-
escaped = true;
|
|
5394
|
-
continue;
|
|
5395
|
-
}
|
|
5396
|
-
if (char === '"') {
|
|
5397
|
-
inString = false;
|
|
5398
|
-
}
|
|
5399
|
-
continue;
|
|
5400
|
-
}
|
|
5401
|
-
if (char === '"') {
|
|
5402
|
-
inString = true;
|
|
5403
|
-
continue;
|
|
5404
|
-
}
|
|
5405
|
-
if (char === "{" || char === "[") {
|
|
5406
|
-
return index;
|
|
5407
|
-
}
|
|
5408
|
-
}
|
|
5409
|
-
const objectStart = input.indexOf("{");
|
|
5410
|
-
const arrayStart = input.indexOf("[");
|
|
5411
|
-
if (objectStart < 0) {
|
|
5412
|
-
return arrayStart;
|
|
5413
|
-
}
|
|
5414
|
-
if (arrayStart < 0) {
|
|
5415
|
-
return objectStart;
|
|
5416
|
-
}
|
|
5417
|
-
return Math.min(objectStart, arrayStart);
|
|
5418
|
-
}
|
|
5419
5311
|
function parseWithObserve(output, schema, parseOptions, context) {
|
|
5420
5312
|
const userParseTrace = parseOptions.onTrace;
|
|
5421
5313
|
return parseLLMOutput(output, schema, {
|
|
@@ -5518,12 +5410,12 @@ function mergeStructuredOptions(defaults, overrides) {
|
|
|
5518
5410
|
...defaults,
|
|
5519
5411
|
...overrides,
|
|
5520
5412
|
parse: {
|
|
5521
|
-
...defaults?.parse
|
|
5522
|
-
...overrides?.parse
|
|
5413
|
+
...defaults?.parse,
|
|
5414
|
+
...overrides?.parse
|
|
5523
5415
|
},
|
|
5524
5416
|
request: {
|
|
5525
|
-
...defaults?.request
|
|
5526
|
-
...overrides?.request
|
|
5417
|
+
...defaults?.request,
|
|
5418
|
+
...overrides?.request
|
|
5527
5419
|
},
|
|
5528
5420
|
stream: mergeObjectLike(defaults?.stream, overrides?.stream),
|
|
5529
5421
|
selfHeal: mergeObjectLike(defaults?.selfHeal, overrides?.selfHeal),
|
|
@@ -5539,8 +5431,8 @@ function mergeGenerateOptions(defaults, overrides) {
|
|
|
5539
5431
|
outdent: overrides?.outdent ?? defaults?.outdent,
|
|
5540
5432
|
systemPrompt: overrides?.systemPrompt ?? defaults?.systemPrompt,
|
|
5541
5433
|
request: {
|
|
5542
|
-
...defaults?.request
|
|
5543
|
-
...overrides?.request
|
|
5434
|
+
...defaults?.request,
|
|
5435
|
+
...overrides?.request
|
|
5544
5436
|
},
|
|
5545
5437
|
stream: mergeObjectLike(defaults?.stream, overrides?.stream),
|
|
5546
5438
|
debug: mergeObjectLike(defaults?.debug, overrides?.debug),
|
|
@@ -5733,7 +5625,7 @@ function toPromptString(value) {
|
|
|
5733
5625
|
return String(value);
|
|
5734
5626
|
}
|
|
5735
5627
|
}
|
|
5736
|
-
var
|
|
5628
|
+
var outdent = createOutdent({
|
|
5737
5629
|
trimLeadingNewline: true,
|
|
5738
5630
|
trimTrailingNewline: true,
|
|
5739
5631
|
newline: `
|
|
@@ -5766,7 +5658,7 @@ function stripOuterBlankLines(text) {
|
|
|
5766
5658
|
`);
|
|
5767
5659
|
}
|
|
5768
5660
|
function renderPromptTemplate(strings, values) {
|
|
5769
|
-
return stripOuterBlankLines(
|
|
5661
|
+
return stripOuterBlankLines(outdent(strings, ...values.map(toPromptString)));
|
|
5770
5662
|
}
|
|
5771
5663
|
function isTemplateStringsArray(value) {
|
|
5772
5664
|
return Array.isArray(value) && "raw" in value;
|
|
@@ -5814,6 +5706,9 @@ class PromptMessageBuilderImpl {
|
|
|
5814
5706
|
function createPromptMessageBuilder() {
|
|
5815
5707
|
return new PromptMessageBuilderImpl;
|
|
5816
5708
|
}
|
|
5709
|
+
function dedent(strings, ...values) {
|
|
5710
|
+
return renderPromptTemplate(strings, values);
|
|
5711
|
+
}
|
|
5817
5712
|
function prompt(input, ...values) {
|
|
5818
5713
|
if (isTemplateStringsArray(input)) {
|
|
5819
5714
|
return renderPromptTemplate(input, values);
|