extrait 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/generate-shared.d.ts +8 -2
- package/dist/index.cjs +384 -73
- package/dist/index.d.ts +1 -1
- package/dist/index.js +384 -73
- package/dist/parse.d.ts +1 -1
- package/dist/providers/mcp-runtime.d.ts +1 -1
- package/dist/structured.d.ts +3 -3
- package/dist/types.d.ts +32 -5
- package/package.json +8 -14
package/dist/index.js
CHANGED
|
@@ -700,36 +700,36 @@ function unwrap(schema) {
|
|
|
700
700
|
let optional = false;
|
|
701
701
|
let nullable = false;
|
|
702
702
|
while (true) {
|
|
703
|
-
const typeName = current?.
|
|
703
|
+
const typeName = current?.def?.type;
|
|
704
704
|
if (!typeName) {
|
|
705
705
|
break;
|
|
706
706
|
}
|
|
707
707
|
if (typeName === "optional") {
|
|
708
708
|
optional = true;
|
|
709
|
-
current = current.
|
|
709
|
+
current = current.def?.innerType ?? current;
|
|
710
710
|
continue;
|
|
711
711
|
}
|
|
712
712
|
if (typeName === "default") {
|
|
713
713
|
optional = true;
|
|
714
|
-
current = current.
|
|
714
|
+
current = current.def?.innerType ?? current;
|
|
715
715
|
continue;
|
|
716
716
|
}
|
|
717
717
|
if (typeName === "nullable") {
|
|
718
718
|
nullable = true;
|
|
719
|
-
current = current.
|
|
719
|
+
current = current.def?.innerType ?? current;
|
|
720
720
|
continue;
|
|
721
721
|
}
|
|
722
722
|
if (typeName === "pipe") {
|
|
723
|
-
const outType = current.
|
|
723
|
+
const outType = current.def?.out?.def?.type;
|
|
724
724
|
if (outType === "transform") {
|
|
725
|
-
current = current.
|
|
725
|
+
current = current.def?.in ?? current;
|
|
726
726
|
} else {
|
|
727
|
-
current = current.
|
|
727
|
+
current = current.def?.out ?? current;
|
|
728
728
|
}
|
|
729
729
|
continue;
|
|
730
730
|
}
|
|
731
731
|
if (typeName === "catch" || typeName === "readonly") {
|
|
732
|
-
current = current.
|
|
732
|
+
current = current.def?.innerType ?? current;
|
|
733
733
|
continue;
|
|
734
734
|
}
|
|
735
735
|
break;
|
|
@@ -745,7 +745,7 @@ function formatCore(schema, depth, seen) {
|
|
|
745
745
|
return "unknown";
|
|
746
746
|
}
|
|
747
747
|
seen.add(schema);
|
|
748
|
-
const typeName = schema?.
|
|
748
|
+
const typeName = schema?.def?.type;
|
|
749
749
|
switch (typeName) {
|
|
750
750
|
case "string":
|
|
751
751
|
return "string";
|
|
@@ -770,44 +770,44 @@ function formatCore(schema, depth, seen) {
|
|
|
770
770
|
case "void":
|
|
771
771
|
return "void";
|
|
772
772
|
case "literal": {
|
|
773
|
-
const value = schema.
|
|
773
|
+
const value = schema.def?.values?.[0];
|
|
774
774
|
return JSON.stringify(value);
|
|
775
775
|
}
|
|
776
776
|
case "enum": {
|
|
777
|
-
const entries = schema.
|
|
777
|
+
const entries = schema.def?.entries;
|
|
778
778
|
const values = Object.values(entries ?? {});
|
|
779
779
|
const unique = [...new Set(values.filter((v) => typeof v !== "string" || Number.isNaN(Number(v))))];
|
|
780
780
|
return unique.map((v) => JSON.stringify(v)).join(" | ") || "string";
|
|
781
781
|
}
|
|
782
782
|
case "array": {
|
|
783
|
-
const inner = formatType(schema.
|
|
783
|
+
const inner = formatType(schema.def?.element ?? schema, depth, seen);
|
|
784
784
|
return requiresParentheses(inner) ? `(${inner})[]` : `${inner}[]`;
|
|
785
785
|
}
|
|
786
786
|
case "tuple": {
|
|
787
|
-
const items = (schema.
|
|
787
|
+
const items = (schema.def?.items ?? []).map((item) => formatType(item, depth, seen));
|
|
788
788
|
return `[${items.join(", ")}]`;
|
|
789
789
|
}
|
|
790
790
|
case "union": {
|
|
791
|
-
const options = (schema.
|
|
791
|
+
const options = (schema.def?.options ?? []).map((option) => formatType(option, depth, seen));
|
|
792
792
|
return options.join(" | ") || "unknown";
|
|
793
793
|
}
|
|
794
794
|
case "intersection": {
|
|
795
|
-
const left = formatType(schema.
|
|
796
|
-
const right = formatType(schema.
|
|
795
|
+
const left = formatType(schema.def?.left ?? schema, depth, seen);
|
|
796
|
+
const right = formatType(schema.def?.right ?? schema, depth, seen);
|
|
797
797
|
return `${left} & ${right}`;
|
|
798
798
|
}
|
|
799
799
|
case "record": {
|
|
800
|
-
const keyType = formatType(schema.
|
|
801
|
-
const valueType = formatType(schema.
|
|
800
|
+
const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
|
|
801
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
802
802
|
return `Record<${keyType}, ${valueType}>`;
|
|
803
803
|
}
|
|
804
804
|
case "map": {
|
|
805
|
-
const keyType = formatType(schema.
|
|
806
|
-
const valueType = formatType(schema.
|
|
805
|
+
const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
|
|
806
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
807
807
|
return `Map<${keyType}, ${valueType}>`;
|
|
808
808
|
}
|
|
809
809
|
case "set": {
|
|
810
|
-
const valueType = formatType(schema.
|
|
810
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
811
811
|
return `Set<${valueType}>`;
|
|
812
812
|
}
|
|
813
813
|
case "object":
|
|
@@ -821,7 +821,7 @@ function formatCore(schema, depth, seen) {
|
|
|
821
821
|
function formatObject(schema, depth, seen) {
|
|
822
822
|
const indent = " ".repeat(depth);
|
|
823
823
|
const innerIndent = " ".repeat(depth + 1);
|
|
824
|
-
const rawShape = schema.
|
|
824
|
+
const rawShape = schema.def?.shape;
|
|
825
825
|
const shape = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
|
|
826
826
|
const entries = Object.entries(shape);
|
|
827
827
|
if (entries.length === 0) {
|
|
@@ -847,27 +847,27 @@ function requiresParentheses(typeText) {
|
|
|
847
847
|
return typeText.includes(" | ") || typeText.includes(" & ");
|
|
848
848
|
}
|
|
849
849
|
function isIntegerNumber(schema) {
|
|
850
|
-
const checks = schema.
|
|
850
|
+
const checks = schema.def?.checks ?? [];
|
|
851
851
|
return checks.some((check) => check.isInt === true);
|
|
852
852
|
}
|
|
853
853
|
function readSchemaDescription(schema) {
|
|
854
854
|
let current = schema;
|
|
855
|
-
while (current?.
|
|
855
|
+
while (current?.def?.type) {
|
|
856
856
|
const desc = current.description;
|
|
857
857
|
if (typeof desc === "string" && desc.trim().length > 0) {
|
|
858
858
|
return sanitizeDescription(desc);
|
|
859
859
|
}
|
|
860
|
-
const typeName = current.
|
|
860
|
+
const typeName = current.def.type;
|
|
861
861
|
if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
|
|
862
|
-
current = current.
|
|
862
|
+
current = current.def.innerType ?? current;
|
|
863
863
|
continue;
|
|
864
864
|
}
|
|
865
865
|
if (typeName === "pipe") {
|
|
866
|
-
current = current.
|
|
866
|
+
current = current.def.in ?? current;
|
|
867
867
|
continue;
|
|
868
868
|
}
|
|
869
869
|
if (typeName === "catch" || typeName === "readonly") {
|
|
870
|
-
current = current.
|
|
870
|
+
current = current.def.innerType ?? current;
|
|
871
871
|
continue;
|
|
872
872
|
}
|
|
873
873
|
break;
|
|
@@ -1130,7 +1130,7 @@ function findSSEBoundary(buffer) {
|
|
|
1130
1130
|
}
|
|
1131
1131
|
|
|
1132
1132
|
// src/providers/mcp-runtime.ts
|
|
1133
|
-
var DEFAULT_MAX_TOOL_ROUNDS =
|
|
1133
|
+
var DEFAULT_MAX_TOOL_ROUNDS = 100;
|
|
1134
1134
|
async function resolveMCPToolset(clients) {
|
|
1135
1135
|
if (!Array.isArray(clients) || clients.length === 0) {
|
|
1136
1136
|
return {
|
|
@@ -1463,7 +1463,15 @@ function normalizeBaseURL(baseURL) {
|
|
|
1463
1463
|
return baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
|
|
1464
1464
|
}
|
|
1465
1465
|
function buildURL(baseURL, path) {
|
|
1466
|
-
|
|
1466
|
+
try {
|
|
1467
|
+
return new URL(path).toString();
|
|
1468
|
+
} catch {}
|
|
1469
|
+
const base = new URL(normalizeBaseURL(baseURL));
|
|
1470
|
+
const resolvedPath = new URL(path, "http://provider-path.local");
|
|
1471
|
+
base.pathname = mergePathnames(base.pathname, resolvedPath.pathname);
|
|
1472
|
+
base.search = resolvedPath.search;
|
|
1473
|
+
base.hash = resolvedPath.hash;
|
|
1474
|
+
return base.toString();
|
|
1467
1475
|
}
|
|
1468
1476
|
function safeJSONParse(input) {
|
|
1469
1477
|
try {
|
|
@@ -1538,6 +1546,36 @@ function addOptional(a, b) {
|
|
|
1538
1546
|
}
|
|
1539
1547
|
return (a ?? 0) + (b ?? 0);
|
|
1540
1548
|
}
|
|
1549
|
+
function mergePathnames(basePathname, pathPathname) {
|
|
1550
|
+
const baseSegments = splitPathSegments(basePathname);
|
|
1551
|
+
const pathSegments = splitPathSegments(pathPathname);
|
|
1552
|
+
const overlap = findPathOverlap(baseSegments, pathSegments);
|
|
1553
|
+
const mergedSegments = [...baseSegments, ...pathSegments.slice(overlap)];
|
|
1554
|
+
if (mergedSegments.length === 0) {
|
|
1555
|
+
return "/";
|
|
1556
|
+
}
|
|
1557
|
+
const mergedPathname = `/${mergedSegments.join("/")}`;
|
|
1558
|
+
return pathPathname.endsWith("/") && pathPathname !== "/" ? `${mergedPathname}/` : mergedPathname;
|
|
1559
|
+
}
|
|
1560
|
+
function splitPathSegments(pathname) {
|
|
1561
|
+
return pathname.split("/").filter((segment) => segment.length > 0);
|
|
1562
|
+
}
|
|
1563
|
+
function findPathOverlap(baseSegments, pathSegments) {
|
|
1564
|
+
const maxOverlap = Math.min(baseSegments.length, pathSegments.length);
|
|
1565
|
+
for (let size = maxOverlap;size > 0; size -= 1) {
|
|
1566
|
+
let matches = true;
|
|
1567
|
+
for (let index = 0;index < size; index += 1) {
|
|
1568
|
+
if (baseSegments[baseSegments.length - size + index] !== pathSegments[index]) {
|
|
1569
|
+
matches = false;
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
if (matches) {
|
|
1574
|
+
return size;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
return 0;
|
|
1578
|
+
}
|
|
1541
1579
|
|
|
1542
1580
|
// src/providers/openai-compatible.ts
|
|
1543
1581
|
function createOpenAICompatibleAdapter(options) {
|
|
@@ -1696,7 +1734,7 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
|
|
|
1696
1734
|
const message = await response.text();
|
|
1697
1735
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1698
1736
|
}
|
|
1699
|
-
const payload = await response
|
|
1737
|
+
const payload = await parseOpenAICompatibleJSONResponse(response, "Failed to parse OpenAI-compatible chat completion response");
|
|
1700
1738
|
const assistantMessage = pickAssistantMessage(payload);
|
|
1701
1739
|
if (!assistantMessage) {
|
|
1702
1740
|
throw new Error("No assistant message in OpenAI-compatible response.");
|
|
@@ -1712,6 +1750,25 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
|
|
|
1712
1750
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined
|
|
1713
1751
|
};
|
|
1714
1752
|
}
|
|
1753
|
+
async function parseOpenAICompatibleJSONResponse(response, context) {
|
|
1754
|
+
const rawBody = await response.text();
|
|
1755
|
+
try {
|
|
1756
|
+
return JSON.parse(rawBody);
|
|
1757
|
+
} catch (error) {
|
|
1758
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1759
|
+
throw new Error(`${context} (HTTP ${response.status}): ${message}. Raw body: ${formatResponseBodyForError(rawBody)}`);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
function formatResponseBodyForError(rawBody, maxLength = 2000) {
|
|
1763
|
+
const normalized = rawBody.trim();
|
|
1764
|
+
if (normalized.length === 0) {
|
|
1765
|
+
return "[empty body]";
|
|
1766
|
+
}
|
|
1767
|
+
if (normalized.length <= maxLength) {
|
|
1768
|
+
return normalized;
|
|
1769
|
+
}
|
|
1770
|
+
return `${normalized.slice(0, maxLength)}...[truncated ${normalized.length - maxLength} chars]`;
|
|
1771
|
+
}
|
|
1715
1772
|
async function completeWithChatCompletionsWithMCP(options, fetcher, path, request) {
|
|
1716
1773
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
1717
1774
|
let messages = buildMessages(request);
|
|
@@ -1720,6 +1777,7 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1720
1777
|
let lastPayload;
|
|
1721
1778
|
const toolCalls = [];
|
|
1722
1779
|
const toolExecutions = [];
|
|
1780
|
+
const reasoningBlocks = [];
|
|
1723
1781
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1724
1782
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1725
1783
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
@@ -1750,14 +1808,17 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1750
1808
|
finishReason = pickFinishReason(payload);
|
|
1751
1809
|
const assistantMessage = pickAssistantMessage(payload);
|
|
1752
1810
|
const calledTools = pickChatToolCalls(payload);
|
|
1811
|
+
const roundReasoning = pickAssistantReasoning(payload);
|
|
1812
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
1753
1813
|
if (!assistantMessage) {
|
|
1754
1814
|
throw new Error("No assistant message in OpenAI-compatible response.");
|
|
1755
1815
|
}
|
|
1756
1816
|
if (calledTools.length === 0) {
|
|
1757
|
-
const reasoning =
|
|
1817
|
+
const reasoning = joinReasoningBlocks(reasoningBlocks) || undefined;
|
|
1758
1818
|
return {
|
|
1759
1819
|
text: pickAssistantText(payload),
|
|
1760
|
-
reasoning
|
|
1820
|
+
reasoning,
|
|
1821
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1761
1822
|
raw: payload,
|
|
1762
1823
|
usage: aggregatedUsage,
|
|
1763
1824
|
finishReason,
|
|
@@ -1785,10 +1846,8 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1785
1846
|
}
|
|
1786
1847
|
return {
|
|
1787
1848
|
text: pickAssistantText(lastPayload ?? {}),
|
|
1788
|
-
reasoning: (
|
|
1789
|
-
|
|
1790
|
-
return value.length > 0 ? value : undefined;
|
|
1791
|
-
})(),
|
|
1849
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
1850
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1792
1851
|
raw: lastPayload,
|
|
1793
1852
|
usage: aggregatedUsage,
|
|
1794
1853
|
finishReason,
|
|
@@ -1836,6 +1895,7 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
1836
1895
|
let lastPayload;
|
|
1837
1896
|
const executedToolCalls = [];
|
|
1838
1897
|
const toolExecutions = [];
|
|
1898
|
+
const reasoningBlocks = [];
|
|
1839
1899
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1840
1900
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1841
1901
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
@@ -1865,12 +1925,15 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
1865
1925
|
lastPayload = payload;
|
|
1866
1926
|
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
1867
1927
|
finishReason = pickResponsesFinishReason(payload) ?? finishReason;
|
|
1928
|
+
pushReasoningBlock(reasoningBlocks, round, pickResponsesReasoning(payload));
|
|
1868
1929
|
const providerToolCalls = pickResponsesToolCalls(payload);
|
|
1869
1930
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
1870
1931
|
if (functionCalls.length === 0) {
|
|
1871
1932
|
const text = pickResponsesText(payload) || pickAssistantText(payload);
|
|
1872
1933
|
return {
|
|
1873
1934
|
text,
|
|
1935
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
1936
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1874
1937
|
raw: payload,
|
|
1875
1938
|
usage: aggregatedUsage,
|
|
1876
1939
|
finishReason,
|
|
@@ -1898,6 +1961,8 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
1898
1961
|
}
|
|
1899
1962
|
return {
|
|
1900
1963
|
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
1964
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
1965
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1901
1966
|
raw: lastPayload,
|
|
1902
1967
|
usage: aggregatedUsage,
|
|
1903
1968
|
finishReason,
|
|
@@ -1913,9 +1978,9 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
1913
1978
|
let lastPayload;
|
|
1914
1979
|
const executedToolCalls = [];
|
|
1915
1980
|
const toolExecutions = [];
|
|
1981
|
+
const reasoningBlocks = [];
|
|
1916
1982
|
callbacks.onStart?.();
|
|
1917
1983
|
let lastRoundText = "";
|
|
1918
|
-
let lastRoundReasoning = "";
|
|
1919
1984
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1920
1985
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1921
1986
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
@@ -1977,6 +2042,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
1977
2042
|
const chunk = {
|
|
1978
2043
|
textDelta: delta,
|
|
1979
2044
|
reasoningDelta: reasoningDelta || undefined,
|
|
2045
|
+
turnIndex: round,
|
|
1980
2046
|
raw: json,
|
|
1981
2047
|
usage: chunkUsage,
|
|
1982
2048
|
finishReason: chunkFinishReason
|
|
@@ -1989,22 +2055,41 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
1989
2055
|
finishReason = roundFinishReason;
|
|
1990
2056
|
}
|
|
1991
2057
|
const calledTools = buildOpenAIStreamToolCalls(streamedToolCalls);
|
|
2058
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
2059
|
+
request.onTurnTransition?.({
|
|
2060
|
+
turnIndex: round,
|
|
2061
|
+
kind: "reasoningComplete",
|
|
2062
|
+
reasoningText: roundReasoning
|
|
2063
|
+
});
|
|
1992
2064
|
if (calledTools.length === 0) {
|
|
1993
2065
|
const out2 = {
|
|
1994
2066
|
text: roundText,
|
|
1995
|
-
reasoning:
|
|
2067
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2068
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1996
2069
|
raw: lastPayload,
|
|
1997
2070
|
usage: aggregatedUsage,
|
|
1998
2071
|
finishReason,
|
|
1999
2072
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2000
2073
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2001
2074
|
};
|
|
2075
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2002
2076
|
callbacks.onComplete?.(out2);
|
|
2003
2077
|
return out2;
|
|
2004
2078
|
}
|
|
2005
2079
|
if (round > maxToolRounds) {
|
|
2006
2080
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
2007
2081
|
}
|
|
2082
|
+
request.onTurnTransition?.({
|
|
2083
|
+
turnIndex: round,
|
|
2084
|
+
kind: "toolCallsEmit",
|
|
2085
|
+
toolCalls: calledTools
|
|
2086
|
+
});
|
|
2087
|
+
callbacks.onChunk?.({
|
|
2088
|
+
textDelta: "",
|
|
2089
|
+
turnIndex: round,
|
|
2090
|
+
toolCalls: calledTools,
|
|
2091
|
+
finishReason: roundFinishReason
|
|
2092
|
+
});
|
|
2008
2093
|
const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
|
|
2009
2094
|
round,
|
|
2010
2095
|
request,
|
|
@@ -2013,8 +2098,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2013
2098
|
});
|
|
2014
2099
|
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2015
2100
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2101
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2016
2102
|
lastRoundText = roundText;
|
|
2017
|
-
lastRoundReasoning = roundReasoning;
|
|
2018
2103
|
const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
|
|
2019
2104
|
reasoning: roundReasoning,
|
|
2020
2105
|
reasoningFieldName
|
|
@@ -2028,13 +2113,15 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2028
2113
|
}
|
|
2029
2114
|
const out = {
|
|
2030
2115
|
text: lastRoundText,
|
|
2031
|
-
reasoning:
|
|
2116
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2117
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2032
2118
|
raw: lastPayload,
|
|
2033
2119
|
usage: aggregatedUsage,
|
|
2034
2120
|
finishReason,
|
|
2035
2121
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2036
2122
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2037
2123
|
};
|
|
2124
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2038
2125
|
callbacks.onComplete?.(out);
|
|
2039
2126
|
return out;
|
|
2040
2127
|
}
|
|
@@ -2117,6 +2204,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2117
2204
|
let lastPayload;
|
|
2118
2205
|
const executedToolCalls = [];
|
|
2119
2206
|
const toolExecutions = [];
|
|
2207
|
+
const reasoningBlocks = [];
|
|
2120
2208
|
callbacks.onStart?.();
|
|
2121
2209
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2122
2210
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
@@ -2145,6 +2233,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2145
2233
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2146
2234
|
}
|
|
2147
2235
|
let roundText = "";
|
|
2236
|
+
let roundReasoning = "";
|
|
2148
2237
|
let roundUsage;
|
|
2149
2238
|
let roundFinishReason;
|
|
2150
2239
|
let roundPayload;
|
|
@@ -2163,6 +2252,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2163
2252
|
lastPayload = payload;
|
|
2164
2253
|
}
|
|
2165
2254
|
const delta = pickResponsesStreamTextDelta(json);
|
|
2255
|
+
const reasoningDelta = pickResponsesStreamReasoningDelta(json);
|
|
2166
2256
|
const chunkUsage = pickResponsesStreamUsage(json);
|
|
2167
2257
|
const chunkFinishReason = pickResponsesStreamFinishReason(json);
|
|
2168
2258
|
collectResponsesStreamToolCalls(json, streamedToolCalls);
|
|
@@ -2174,9 +2264,14 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2174
2264
|
roundText += delta;
|
|
2175
2265
|
callbacks.onToken?.(delta);
|
|
2176
2266
|
}
|
|
2177
|
-
if (
|
|
2267
|
+
if (reasoningDelta) {
|
|
2268
|
+
roundReasoning += reasoningDelta;
|
|
2269
|
+
}
|
|
2270
|
+
if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
|
|
2178
2271
|
const chunk = {
|
|
2179
2272
|
textDelta: delta,
|
|
2273
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
2274
|
+
turnIndex: round,
|
|
2180
2275
|
raw: json,
|
|
2181
2276
|
usage: chunkUsage,
|
|
2182
2277
|
finishReason: chunkFinishReason
|
|
@@ -2192,25 +2287,48 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2192
2287
|
finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
|
|
2193
2288
|
}
|
|
2194
2289
|
const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
|
|
2290
|
+
if (roundPayload && roundReasoning.length === 0) {
|
|
2291
|
+
roundReasoning = pickResponsesReasoning(roundPayload);
|
|
2292
|
+
}
|
|
2195
2293
|
const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
|
|
2196
2294
|
const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
|
|
2197
2295
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
2296
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
2297
|
+
request.onTurnTransition?.({
|
|
2298
|
+
turnIndex: round,
|
|
2299
|
+
kind: "reasoningComplete",
|
|
2300
|
+
reasoningText: roundReasoning
|
|
2301
|
+
});
|
|
2198
2302
|
if (functionCalls.length === 0) {
|
|
2199
2303
|
const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
|
|
2200
2304
|
const out2 = {
|
|
2201
2305
|
text: finalText,
|
|
2306
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2307
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2202
2308
|
raw: roundPayload ?? lastPayload,
|
|
2203
2309
|
usage: aggregatedUsage,
|
|
2204
2310
|
finishReason,
|
|
2205
2311
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2206
2312
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2207
2313
|
};
|
|
2314
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2208
2315
|
callbacks.onComplete?.(out2);
|
|
2209
2316
|
return out2;
|
|
2210
2317
|
}
|
|
2211
2318
|
if (round > maxToolRounds) {
|
|
2212
2319
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
2213
2320
|
}
|
|
2321
|
+
request.onTurnTransition?.({
|
|
2322
|
+
turnIndex: round,
|
|
2323
|
+
kind: "toolCallsEmit",
|
|
2324
|
+
toolCalls: functionCalls
|
|
2325
|
+
});
|
|
2326
|
+
callbacks.onChunk?.({
|
|
2327
|
+
textDelta: "",
|
|
2328
|
+
turnIndex: round,
|
|
2329
|
+
toolCalls: functionCalls,
|
|
2330
|
+
finishReason: roundFinishReason
|
|
2331
|
+
});
|
|
2214
2332
|
const outputs = await executeMCPToolCalls(functionCalls, mcpToolset, {
|
|
2215
2333
|
round,
|
|
2216
2334
|
request,
|
|
@@ -2219,6 +2337,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2219
2337
|
});
|
|
2220
2338
|
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2221
2339
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2340
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2222
2341
|
input = outputs.map((entry) => ({
|
|
2223
2342
|
type: "function_call_output",
|
|
2224
2343
|
call_id: entry.call.id,
|
|
@@ -2228,12 +2347,15 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2228
2347
|
}
|
|
2229
2348
|
const out = {
|
|
2230
2349
|
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
2350
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2351
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2231
2352
|
raw: lastPayload,
|
|
2232
2353
|
usage: aggregatedUsage,
|
|
2233
2354
|
finishReason,
|
|
2234
2355
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2235
2356
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2236
2357
|
};
|
|
2358
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2237
2359
|
callbacks.onComplete?.(out);
|
|
2238
2360
|
return out;
|
|
2239
2361
|
}
|
|
@@ -2518,6 +2640,20 @@ function pickResponsesStreamTextDelta(payload) {
|
|
|
2518
2640
|
}
|
|
2519
2641
|
return "";
|
|
2520
2642
|
}
|
|
2643
|
+
function pickResponsesStreamReasoningDelta(payload) {
|
|
2644
|
+
const eventType = pickString(payload.type) ?? "";
|
|
2645
|
+
if (!eventType.includes("reasoning") && !eventType.includes("thinking")) {
|
|
2646
|
+
return "";
|
|
2647
|
+
}
|
|
2648
|
+
const direct = pickString(payload.delta);
|
|
2649
|
+
if (direct) {
|
|
2650
|
+
return direct;
|
|
2651
|
+
}
|
|
2652
|
+
if (isRecord2(payload.delta)) {
|
|
2653
|
+
return pickReasoningText(payload.delta) || pickString(payload.delta.text) || pickString(payload.delta.summary_text) || "";
|
|
2654
|
+
}
|
|
2655
|
+
return "";
|
|
2656
|
+
}
|
|
2521
2657
|
function pickResponsesStreamUsage(payload) {
|
|
2522
2658
|
const direct = pickUsage(payload);
|
|
2523
2659
|
if (direct) {
|
|
@@ -2652,6 +2788,30 @@ function pickResponsesText(payload) {
|
|
|
2652
2788
|
}).join("");
|
|
2653
2789
|
}).join("");
|
|
2654
2790
|
}
|
|
2791
|
+
function pickResponsesReasoning(payload) {
|
|
2792
|
+
const direct = pickReasoningText(payload);
|
|
2793
|
+
if (direct) {
|
|
2794
|
+
return direct;
|
|
2795
|
+
}
|
|
2796
|
+
const output = payload.output;
|
|
2797
|
+
if (!Array.isArray(output)) {
|
|
2798
|
+
return "";
|
|
2799
|
+
}
|
|
2800
|
+
return output.map((item) => {
|
|
2801
|
+
if (!isRecord2(item)) {
|
|
2802
|
+
return "";
|
|
2803
|
+
}
|
|
2804
|
+
const itemReasoning = pickReasoningText(item);
|
|
2805
|
+
if (itemReasoning) {
|
|
2806
|
+
return itemReasoning;
|
|
2807
|
+
}
|
|
2808
|
+
const itemType = pickString(item.type) ?? "";
|
|
2809
|
+
if ((itemType.includes("reasoning") || itemType.includes("thinking")) && Array.isArray(item.content)) {
|
|
2810
|
+
return item.content.map((part) => isRecord2(part) ? pickTextLike(part) : "").join("");
|
|
2811
|
+
}
|
|
2812
|
+
return "";
|
|
2813
|
+
}).join("");
|
|
2814
|
+
}
|
|
2655
2815
|
function pickAssistantText(payload) {
|
|
2656
2816
|
const message = pickAssistantMessage(payload);
|
|
2657
2817
|
if (message) {
|
|
@@ -2672,6 +2832,18 @@ function pickAssistantText(payload) {
|
|
|
2672
2832
|
function pickReasoningText(value) {
|
|
2673
2833
|
return pickTextLike(value.reasoning) || pickTextLike(value.reasoning_content);
|
|
2674
2834
|
}
|
|
2835
|
+
function pushReasoningBlock(blocks, turnIndex, text) {
|
|
2836
|
+
const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
|
|
2837
|
+
if (!clean) {
|
|
2838
|
+
return;
|
|
2839
|
+
}
|
|
2840
|
+
blocks.push({ turnIndex, text: clean });
|
|
2841
|
+
}
|
|
2842
|
+
function joinReasoningBlocks(blocks) {
|
|
2843
|
+
return blocks.map((block) => block.text).filter(Boolean).join(`
|
|
2844
|
+
|
|
2845
|
+
`);
|
|
2846
|
+
}
|
|
2675
2847
|
function pickTextFromOpenAIContent(value) {
|
|
2676
2848
|
return pickTextLike(value);
|
|
2677
2849
|
}
|
|
@@ -2852,6 +3024,7 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2852
3024
|
let lastPayload;
|
|
2853
3025
|
const toolCalls = [];
|
|
2854
3026
|
const toolExecutions = [];
|
|
3027
|
+
const reasoningBlocks = [];
|
|
2855
3028
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2856
3029
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2857
3030
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
@@ -2881,9 +3054,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2881
3054
|
finishReason = pickFinishReason2(payload);
|
|
2882
3055
|
const content = Array.isArray(payload.content) ? payload.content : [];
|
|
2883
3056
|
const calledTools = pickAnthropicToolCalls(payload).filter((call) => call.type === "function");
|
|
3057
|
+
pushReasoningBlock2(reasoningBlocks, round, extractAnthropicReasoning(payload));
|
|
2884
3058
|
if (calledTools.length === 0) {
|
|
2885
3059
|
return {
|
|
2886
3060
|
text: extractAnthropicText(payload),
|
|
3061
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3062
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2887
3063
|
raw: payload,
|
|
2888
3064
|
usage: aggregatedUsage,
|
|
2889
3065
|
finishReason,
|
|
@@ -2919,6 +3095,8 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2919
3095
|
}
|
|
2920
3096
|
return {
|
|
2921
3097
|
text: extractAnthropicText(lastPayload ?? {}),
|
|
3098
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3099
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2922
3100
|
raw: lastPayload,
|
|
2923
3101
|
usage: aggregatedUsage,
|
|
2924
3102
|
finishReason,
|
|
@@ -2935,6 +3113,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
2935
3113
|
let lastPayload;
|
|
2936
3114
|
const toolCalls = [];
|
|
2937
3115
|
const toolExecutions = [];
|
|
3116
|
+
const reasoningBlocks = [];
|
|
2938
3117
|
callbacks.onStart?.();
|
|
2939
3118
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2940
3119
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
@@ -2960,6 +3139,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
2960
3139
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2961
3140
|
}
|
|
2962
3141
|
let roundText = "";
|
|
3142
|
+
let roundReasoning = "";
|
|
2963
3143
|
let roundUsage;
|
|
2964
3144
|
let roundFinishReason;
|
|
2965
3145
|
const streamedToolCalls = new Map;
|
|
@@ -2973,6 +3153,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
2973
3153
|
}
|
|
2974
3154
|
lastPayload = json;
|
|
2975
3155
|
const delta = pickAnthropicDelta(json);
|
|
3156
|
+
const reasoningDelta = pickAnthropicReasoningDelta(json);
|
|
2976
3157
|
const chunkUsage = pickUsage2(json);
|
|
2977
3158
|
const chunkFinishReason = pickFinishReason2(json);
|
|
2978
3159
|
collectAnthropicStreamToolCalls(json, streamedToolCalls);
|
|
@@ -2984,9 +3165,14 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
2984
3165
|
roundText += delta;
|
|
2985
3166
|
callbacks.onToken?.(delta);
|
|
2986
3167
|
}
|
|
2987
|
-
if (
|
|
3168
|
+
if (reasoningDelta) {
|
|
3169
|
+
roundReasoning += reasoningDelta;
|
|
3170
|
+
}
|
|
3171
|
+
if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
|
|
2988
3172
|
const chunk = {
|
|
2989
3173
|
textDelta: delta,
|
|
3174
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
3175
|
+
turnIndex: round,
|
|
2990
3176
|
raw: json,
|
|
2991
3177
|
usage: chunkUsage,
|
|
2992
3178
|
finishReason: chunkFinishReason
|
|
@@ -2999,21 +3185,41 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
2999
3185
|
finishReason = roundFinishReason;
|
|
3000
3186
|
}
|
|
3001
3187
|
const calledTools = buildAnthropicStreamToolCalls(streamedToolCalls);
|
|
3188
|
+
pushReasoningBlock2(reasoningBlocks, round, roundReasoning);
|
|
3189
|
+
request.onTurnTransition?.({
|
|
3190
|
+
turnIndex: round,
|
|
3191
|
+
kind: "reasoningComplete",
|
|
3192
|
+
reasoningText: roundReasoning
|
|
3193
|
+
});
|
|
3002
3194
|
if (calledTools.length === 0) {
|
|
3003
3195
|
const out2 = {
|
|
3004
3196
|
text: roundText,
|
|
3197
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3198
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3005
3199
|
raw: lastPayload,
|
|
3006
3200
|
usage: aggregatedUsage,
|
|
3007
3201
|
finishReason,
|
|
3008
3202
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
3009
3203
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
3010
3204
|
};
|
|
3205
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
3011
3206
|
callbacks.onComplete?.(out2);
|
|
3012
3207
|
return out2;
|
|
3013
3208
|
}
|
|
3014
3209
|
if (round > maxToolRounds) {
|
|
3015
3210
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
3016
3211
|
}
|
|
3212
|
+
request.onTurnTransition?.({
|
|
3213
|
+
turnIndex: round,
|
|
3214
|
+
kind: "toolCallsEmit",
|
|
3215
|
+
toolCalls: calledTools
|
|
3216
|
+
});
|
|
3217
|
+
callbacks.onChunk?.({
|
|
3218
|
+
textDelta: "",
|
|
3219
|
+
turnIndex: round,
|
|
3220
|
+
toolCalls: calledTools,
|
|
3221
|
+
finishReason: roundFinishReason
|
|
3222
|
+
});
|
|
3017
3223
|
const toolResultContent = [];
|
|
3018
3224
|
const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
|
|
3019
3225
|
round,
|
|
@@ -3023,6 +3229,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3023
3229
|
});
|
|
3024
3230
|
toolCalls.push(...outputs.map((entry) => entry.call));
|
|
3025
3231
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
3232
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
3026
3233
|
for (const entry of outputs) {
|
|
3027
3234
|
toolResultContent.push({
|
|
3028
3235
|
type: "tool_result",
|
|
@@ -3039,12 +3246,15 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3039
3246
|
}
|
|
3040
3247
|
const out = {
|
|
3041
3248
|
text: "",
|
|
3249
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3250
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3042
3251
|
raw: lastPayload,
|
|
3043
3252
|
usage: aggregatedUsage,
|
|
3044
3253
|
finishReason,
|
|
3045
3254
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
3046
3255
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
3047
3256
|
};
|
|
3257
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
3048
3258
|
callbacks.onComplete?.(out);
|
|
3049
3259
|
return out;
|
|
3050
3260
|
}
|
|
@@ -3165,6 +3375,22 @@ function extractAnthropicText(payload) {
|
|
|
3165
3375
|
return typeof text === "string" ? text : "";
|
|
3166
3376
|
}).join("");
|
|
3167
3377
|
}
|
|
3378
|
+
function extractAnthropicReasoning(payload) {
|
|
3379
|
+
const content = payload.content;
|
|
3380
|
+
if (!Array.isArray(content)) {
|
|
3381
|
+
return "";
|
|
3382
|
+
}
|
|
3383
|
+
return content.map((part) => {
|
|
3384
|
+
if (!isRecord2(part)) {
|
|
3385
|
+
return "";
|
|
3386
|
+
}
|
|
3387
|
+
const type = pickString(part.type) ?? "";
|
|
3388
|
+
if (type !== "thinking" && type !== "reasoning") {
|
|
3389
|
+
return "";
|
|
3390
|
+
}
|
|
3391
|
+
return pickString(part.thinking) ?? pickString(part.text) ?? pickString(part.reasoning) ?? "";
|
|
3392
|
+
}).join("");
|
|
3393
|
+
}
|
|
3168
3394
|
function pickAnthropicToolCalls(payload) {
|
|
3169
3395
|
const content = payload.content;
|
|
3170
3396
|
if (!Array.isArray(content)) {
|
|
@@ -3195,6 +3421,35 @@ function pickAnthropicDelta(payload) {
|
|
|
3195
3421
|
}
|
|
3196
3422
|
return "";
|
|
3197
3423
|
}
|
|
3424
|
+
function pickAnthropicReasoningDelta(payload) {
|
|
3425
|
+
const deltaObject = payload.delta;
|
|
3426
|
+
if (isRecord2(deltaObject)) {
|
|
3427
|
+
const type = pickString(deltaObject.type) ?? "";
|
|
3428
|
+
if (type === "thinking_delta" || type === "reasoning_delta") {
|
|
3429
|
+
return pickString(deltaObject.thinking) ?? pickString(deltaObject.text) ?? "";
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
const contentBlock = payload.content_block;
|
|
3433
|
+
if (isRecord2(contentBlock)) {
|
|
3434
|
+
const type = pickString(contentBlock.type) ?? "";
|
|
3435
|
+
if (type === "thinking" || type === "reasoning") {
|
|
3436
|
+
return pickString(contentBlock.thinking) ?? pickString(contentBlock.text) ?? "";
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
return "";
|
|
3440
|
+
}
|
|
3441
|
+
function pushReasoningBlock2(blocks, turnIndex, text) {
|
|
3442
|
+
const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
|
|
3443
|
+
if (!clean) {
|
|
3444
|
+
return;
|
|
3445
|
+
}
|
|
3446
|
+
blocks.push({ turnIndex, text: clean });
|
|
3447
|
+
}
|
|
3448
|
+
function joinReasoningBlocks2(blocks) {
|
|
3449
|
+
return blocks.map((block) => block.text).filter(Boolean).join(`
|
|
3450
|
+
|
|
3451
|
+
`);
|
|
3452
|
+
}
|
|
3198
3453
|
function collectAnthropicStreamToolCalls(payload, state) {
|
|
3199
3454
|
const eventType = pickString(payload.type);
|
|
3200
3455
|
if (!eventType) {
|
|
@@ -3668,6 +3923,7 @@ function normalizeStreamConfig(option) {
|
|
|
3668
3923
|
return {
|
|
3669
3924
|
enabled: option.enabled ?? true,
|
|
3670
3925
|
onData: option.onData,
|
|
3926
|
+
onTurnTransition: option.onTurnTransition,
|
|
3671
3927
|
to: option.to
|
|
3672
3928
|
};
|
|
3673
3929
|
}
|
|
@@ -3735,6 +3991,7 @@ async function callModel(adapter, options) {
|
|
|
3735
3991
|
transformToolCallParams: options.request?.transformToolCallParams,
|
|
3736
3992
|
unknownToolError: options.request?.unknownToolError,
|
|
3737
3993
|
toolDebug: options.request?.toolDebug,
|
|
3994
|
+
onTurnTransition: options.stream.onTurnTransition,
|
|
3738
3995
|
body: options.request?.body,
|
|
3739
3996
|
signal: requestSignal
|
|
3740
3997
|
};
|
|
@@ -3762,13 +4019,21 @@ async function callModel(adapter, options) {
|
|
|
3762
4019
|
let latestFinishReason;
|
|
3763
4020
|
let streamedProviderText = "";
|
|
3764
4021
|
let streamedDedicatedReasoning = "";
|
|
4022
|
+
let currentTurnIndex;
|
|
4023
|
+
let currentToolCalls;
|
|
4024
|
+
let streamedReasoningBlocks;
|
|
3765
4025
|
let lastSnapshotFingerprint;
|
|
3766
4026
|
let previousSnapshotText = "";
|
|
3767
4027
|
let previousSnapshotReasoning = "";
|
|
3768
4028
|
const emitStreamingData = (done, usage2, finishReason2) => {
|
|
3769
|
-
const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
|
|
4029
|
+
const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
|
|
3770
4030
|
const snapshot = options.buildSnapshot(normalized2);
|
|
3771
|
-
const fingerprint = toStreamDataFingerprint(
|
|
4031
|
+
const fingerprint = toStreamDataFingerprint({
|
|
4032
|
+
snapshot,
|
|
4033
|
+
done,
|
|
4034
|
+
turnIndex: currentTurnIndex,
|
|
4035
|
+
toolCalls: currentToolCalls
|
|
4036
|
+
});
|
|
3772
4037
|
if (!done && fingerprint === lastSnapshotFingerprint) {
|
|
3773
4038
|
return;
|
|
3774
4039
|
}
|
|
@@ -3784,7 +4049,9 @@ async function callModel(adapter, options) {
|
|
|
3784
4049
|
snapshot,
|
|
3785
4050
|
done,
|
|
3786
4051
|
usage: usage2,
|
|
3787
|
-
finishReason: finishReason2
|
|
4052
|
+
finishReason: finishReason2,
|
|
4053
|
+
turnIndex: currentTurnIndex,
|
|
4054
|
+
toolCalls: currentToolCalls
|
|
3788
4055
|
});
|
|
3789
4056
|
if (options.stream.to === "stdout" && delta.text) {
|
|
3790
4057
|
process.stdout.write(delta.text);
|
|
@@ -3819,8 +4086,21 @@ async function callModel(adapter, options) {
|
|
|
3819
4086
|
streamedDedicatedReasoning += delta;
|
|
3820
4087
|
emitStreamingData(false);
|
|
3821
4088
|
};
|
|
3822
|
-
const
|
|
4089
|
+
const streamRequestPayload = {
|
|
4090
|
+
...requestPayload,
|
|
4091
|
+
onTurnTransition: (transition) => {
|
|
4092
|
+
if (transition.kind === "reasoningComplete") {
|
|
4093
|
+
streamedReasoningBlocks = appendReasoningBlock(streamedReasoningBlocks, transition);
|
|
4094
|
+
}
|
|
4095
|
+
options.stream.onTurnTransition?.(transition);
|
|
4096
|
+
}
|
|
4097
|
+
};
|
|
4098
|
+
const response2 = await adapter.stream(streamRequestPayload, {
|
|
3823
4099
|
onChunk: (chunk) => {
|
|
4100
|
+
if (chunk.turnIndex !== undefined) {
|
|
4101
|
+
currentTurnIndex = chunk.turnIndex;
|
|
4102
|
+
}
|
|
4103
|
+
currentToolCalls = chunk.toolCalls;
|
|
3824
4104
|
if (chunk.textDelta) {
|
|
3825
4105
|
handleTextDelta(chunk.textDelta);
|
|
3826
4106
|
}
|
|
@@ -3833,11 +4113,15 @@ async function callModel(adapter, options) {
|
|
|
3833
4113
|
if (chunk.finishReason) {
|
|
3834
4114
|
latestFinishReason = chunk.finishReason;
|
|
3835
4115
|
}
|
|
4116
|
+
if (!chunk.textDelta && !chunk.reasoningDelta && (chunk.turnIndex !== undefined || chunk.toolCalls)) {
|
|
4117
|
+
emitStreamingData(false, chunk.usage, chunk.finishReason);
|
|
4118
|
+
}
|
|
3836
4119
|
}
|
|
3837
4120
|
});
|
|
3838
4121
|
streamedProviderText = typeof response2.text === "string" ? response2.text : streamedProviderText;
|
|
3839
4122
|
streamedDedicatedReasoning = typeof response2.reasoning === "string" ? response2.reasoning : streamedDedicatedReasoning;
|
|
3840
|
-
|
|
4123
|
+
streamedReasoningBlocks = response2.reasoningBlocks ?? streamedReasoningBlocks;
|
|
4124
|
+
const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
|
|
3841
4125
|
const usage = preferLatestUsage(latestUsage, response2.usage);
|
|
3842
4126
|
const finishReason = response2.finishReason ?? latestFinishReason;
|
|
3843
4127
|
emitStreamingData(true, usage, finishReason);
|
|
@@ -3869,11 +4153,12 @@ async function callModel(adapter, options) {
|
|
|
3869
4153
|
parseSource: finalNormalized.parseSource,
|
|
3870
4154
|
via: "stream",
|
|
3871
4155
|
usage,
|
|
3872
|
-
finishReason
|
|
4156
|
+
finishReason,
|
|
4157
|
+
reasoningBlocks: finalNormalized.reasoningBlocks
|
|
3873
4158
|
};
|
|
3874
4159
|
}
|
|
3875
4160
|
const response = await adapter.complete(requestPayload);
|
|
3876
|
-
const normalized = normalizeModelOutput(response.text, response.reasoning);
|
|
4161
|
+
const normalized = normalizeModelOutput(response.text, response.reasoning, response.reasoningBlocks);
|
|
3877
4162
|
options.observe?.(options.buildEvent({
|
|
3878
4163
|
stage: "llm.response",
|
|
3879
4164
|
message: "Completion response received.",
|
|
@@ -3902,10 +4187,11 @@ async function callModel(adapter, options) {
|
|
|
3902
4187
|
parseSource: normalized.parseSource,
|
|
3903
4188
|
via: "complete",
|
|
3904
4189
|
usage: response.usage,
|
|
3905
|
-
finishReason: response.finishReason
|
|
4190
|
+
finishReason: response.finishReason,
|
|
4191
|
+
reasoningBlocks: normalized.reasoningBlocks
|
|
3906
4192
|
};
|
|
3907
4193
|
}
|
|
3908
|
-
function normalizeModelOutput(text, dedicatedReasoning) {
|
|
4194
|
+
function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
|
|
3909
4195
|
const sanitized = sanitizeThink(text);
|
|
3910
4196
|
const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
|
|
3911
4197
|
const reasoning = joinReasoningSegments([
|
|
@@ -3915,10 +4201,29 @@ function normalizeModelOutput(text, dedicatedReasoning) {
|
|
|
3915
4201
|
return {
|
|
3916
4202
|
text: visibleText,
|
|
3917
4203
|
reasoning,
|
|
4204
|
+
reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
|
|
3918
4205
|
thinkBlocks: sanitized.thinkBlocks,
|
|
3919
4206
|
parseSource: composeParseSource(visibleText, reasoning)
|
|
3920
4207
|
};
|
|
3921
4208
|
}
|
|
4209
|
+
function normalizeReasoningBlocks(blocks) {
|
|
4210
|
+
if (!Array.isArray(blocks)) {
|
|
4211
|
+
return;
|
|
4212
|
+
}
|
|
4213
|
+
const normalized = blocks.map((block) => ({
|
|
4214
|
+
turnIndex: block.turnIndex,
|
|
4215
|
+
text: block.text.replace(RE_THINK_TAGS, "").trim()
|
|
4216
|
+
})).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
|
|
4217
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
4218
|
+
}
|
|
4219
|
+
function appendReasoningBlock(blocks, transition) {
|
|
4220
|
+
const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
|
|
4221
|
+
if (!text) {
|
|
4222
|
+
return blocks;
|
|
4223
|
+
}
|
|
4224
|
+
const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
|
|
4225
|
+
return normalizeReasoningBlocks(next);
|
|
4226
|
+
}
|
|
3922
4227
|
function composeParseSource(text, reasoning) {
|
|
3923
4228
|
if (typeof reasoning !== "string" || reasoning.length === 0) {
|
|
3924
4229
|
return text;
|
|
@@ -4078,7 +4383,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4078
4383
|
}),
|
|
4079
4384
|
buildSnapshot: (model) => ({
|
|
4080
4385
|
text: model.text,
|
|
4081
|
-
reasoning: model.reasoning
|
|
4386
|
+
reasoning: model.reasoning,
|
|
4387
|
+
...model.reasoningBlocks ? { reasoningBlocks: model.reasoningBlocks } : {}
|
|
4082
4388
|
}),
|
|
4083
4389
|
debug: debugConfig,
|
|
4084
4390
|
debugLabel: "generate",
|
|
@@ -4093,7 +4399,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4093
4399
|
text: response.text,
|
|
4094
4400
|
reasoning: response.reasoning,
|
|
4095
4401
|
usage: response.usage,
|
|
4096
|
-
finishReason: response.finishReason
|
|
4402
|
+
finishReason: response.finishReason,
|
|
4403
|
+
...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {}
|
|
4097
4404
|
};
|
|
4098
4405
|
const attempts = [attempt];
|
|
4099
4406
|
normalized.observe?.({
|
|
@@ -4110,7 +4417,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4110
4417
|
reasoning: attempt.reasoning,
|
|
4111
4418
|
attempts,
|
|
4112
4419
|
usage: aggregateUsage(attempts),
|
|
4113
|
-
finishReason: attempt.finishReason
|
|
4420
|
+
finishReason: attempt.finishReason,
|
|
4421
|
+
...attempt.reasoningBlocks ? { reasoningBlocks: attempt.reasoningBlocks } : {}
|
|
4114
4422
|
};
|
|
4115
4423
|
}
|
|
4116
4424
|
function normalizeGenerateInput(promptOrOptions, callOptions) {
|
|
@@ -4930,6 +5238,7 @@ async function executeAttempt(adapter, input) {
|
|
|
4930
5238
|
success: parsed.success,
|
|
4931
5239
|
usage: response.usage,
|
|
4932
5240
|
finishReason: response.finishReason,
|
|
5241
|
+
...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {},
|
|
4933
5242
|
parsed
|
|
4934
5243
|
};
|
|
4935
5244
|
return {
|
|
@@ -4950,6 +5259,7 @@ async function callModel2(adapter, options) {
|
|
|
4950
5259
|
buildSnapshot: (normalized) => ({
|
|
4951
5260
|
text: normalized.text,
|
|
4952
5261
|
reasoning: normalized.reasoning,
|
|
5262
|
+
...normalized.reasoningBlocks ? { reasoningBlocks: normalized.reasoningBlocks } : {},
|
|
4953
5263
|
data: parseStreamingStructuredData(normalized.parseSource) ?? null
|
|
4954
5264
|
}),
|
|
4955
5265
|
debugLabel: "structured"
|
|
@@ -5050,7 +5360,8 @@ function buildSuccessResult(data, attempts) {
|
|
|
5050
5360
|
json: final?.json ?? null,
|
|
5051
5361
|
attempts,
|
|
5052
5362
|
usage: aggregateUsage(attempts),
|
|
5053
|
-
finishReason: final?.finishReason
|
|
5363
|
+
finishReason: final?.finishReason,
|
|
5364
|
+
...final?.reasoningBlocks ? { reasoningBlocks: final.reasoningBlocks } : {}
|
|
5054
5365
|
};
|
|
5055
5366
|
}
|
|
5056
5367
|
function toStructuredError(attempt) {
|
|
@@ -5517,11 +5828,11 @@ function inferSchemaExample(schema) {
|
|
|
5517
5828
|
}
|
|
5518
5829
|
function getObjectShape(schema) {
|
|
5519
5830
|
const unwrapped = unwrap2(schema).schema;
|
|
5520
|
-
const typeName = unwrapped.
|
|
5831
|
+
const typeName = unwrapped.def?.type;
|
|
5521
5832
|
if (typeName !== "object") {
|
|
5522
5833
|
return null;
|
|
5523
5834
|
}
|
|
5524
|
-
const rawShape = unwrapped.
|
|
5835
|
+
const rawShape = unwrapped.def?.shape;
|
|
5525
5836
|
if (typeof rawShape === "function") {
|
|
5526
5837
|
return rawShape();
|
|
5527
5838
|
}
|
|
@@ -5529,11 +5840,11 @@ function getObjectShape(schema) {
|
|
|
5529
5840
|
}
|
|
5530
5841
|
function readDefaultValue(schema) {
|
|
5531
5842
|
let current = schema;
|
|
5532
|
-
while (current?.
|
|
5533
|
-
const typeName = current.
|
|
5843
|
+
while (current?.def?.type) {
|
|
5844
|
+
const typeName = current.def.type;
|
|
5534
5845
|
if (typeName === "default") {
|
|
5535
5846
|
try {
|
|
5536
|
-
const raw = current.
|
|
5847
|
+
const raw = current.def.defaultValue;
|
|
5537
5848
|
if (typeof raw === "function") {
|
|
5538
5849
|
return raw();
|
|
5539
5850
|
}
|
|
@@ -5543,11 +5854,11 @@ function readDefaultValue(schema) {
|
|
|
5543
5854
|
}
|
|
5544
5855
|
}
|
|
5545
5856
|
if (typeName === "optional" || typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
|
|
5546
|
-
current = current.
|
|
5857
|
+
current = current.def.innerType ?? current;
|
|
5547
5858
|
continue;
|
|
5548
5859
|
}
|
|
5549
5860
|
if (typeName === "pipe") {
|
|
5550
|
-
current = current.
|
|
5861
|
+
current = current.def.in ?? current;
|
|
5551
5862
|
continue;
|
|
5552
5863
|
}
|
|
5553
5864
|
return;
|
|
@@ -5556,22 +5867,22 @@ function readDefaultValue(schema) {
|
|
|
5556
5867
|
}
|
|
5557
5868
|
function readSchemaDescription2(schema) {
|
|
5558
5869
|
let current = schema;
|
|
5559
|
-
while (current?.
|
|
5870
|
+
while (current?.def?.type) {
|
|
5560
5871
|
const desc = current.description;
|
|
5561
5872
|
if (typeof desc === "string" && desc.trim().length > 0) {
|
|
5562
5873
|
return desc.trim();
|
|
5563
5874
|
}
|
|
5564
|
-
const typeName = current.
|
|
5875
|
+
const typeName = current.def.type;
|
|
5565
5876
|
if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
|
|
5566
|
-
current = current.
|
|
5877
|
+
current = current.def.innerType ?? current;
|
|
5567
5878
|
continue;
|
|
5568
5879
|
}
|
|
5569
5880
|
if (typeName === "catch" || typeName === "readonly") {
|
|
5570
|
-
current = current.
|
|
5881
|
+
current = current.def.innerType ?? current;
|
|
5571
5882
|
continue;
|
|
5572
5883
|
}
|
|
5573
5884
|
if (typeName === "pipe") {
|
|
5574
|
-
current = current.
|
|
5885
|
+
current = current.def.in ?? current;
|
|
5575
5886
|
continue;
|
|
5576
5887
|
}
|
|
5577
5888
|
break;
|
|
@@ -5581,19 +5892,19 @@ function readSchemaDescription2(schema) {
|
|
|
5581
5892
|
function unwrap2(schema) {
|
|
5582
5893
|
let current = schema;
|
|
5583
5894
|
let optional = false;
|
|
5584
|
-
while (current?.
|
|
5585
|
-
const typeName = current.
|
|
5895
|
+
while (current?.def?.type) {
|
|
5896
|
+
const typeName = current.def.type;
|
|
5586
5897
|
if (typeName === "optional" || typeName === "default") {
|
|
5587
5898
|
optional = true;
|
|
5588
|
-
current = current.
|
|
5899
|
+
current = current.def.innerType ?? current;
|
|
5589
5900
|
continue;
|
|
5590
5901
|
}
|
|
5591
5902
|
if (typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
|
|
5592
|
-
current = current.
|
|
5903
|
+
current = current.def.innerType ?? current;
|
|
5593
5904
|
continue;
|
|
5594
5905
|
}
|
|
5595
5906
|
if (typeName === "pipe") {
|
|
5596
|
-
current = current.
|
|
5907
|
+
current = current.def.in ?? current;
|
|
5597
5908
|
continue;
|
|
5598
5909
|
}
|
|
5599
5910
|
break;
|