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.cjs
CHANGED
|
@@ -790,36 +790,36 @@ function unwrap(schema) {
|
|
|
790
790
|
let optional = false;
|
|
791
791
|
let nullable = false;
|
|
792
792
|
while (true) {
|
|
793
|
-
const typeName = current?.
|
|
793
|
+
const typeName = current?.def?.type;
|
|
794
794
|
if (!typeName) {
|
|
795
795
|
break;
|
|
796
796
|
}
|
|
797
797
|
if (typeName === "optional") {
|
|
798
798
|
optional = true;
|
|
799
|
-
current = current.
|
|
799
|
+
current = current.def?.innerType ?? current;
|
|
800
800
|
continue;
|
|
801
801
|
}
|
|
802
802
|
if (typeName === "default") {
|
|
803
803
|
optional = true;
|
|
804
|
-
current = current.
|
|
804
|
+
current = current.def?.innerType ?? current;
|
|
805
805
|
continue;
|
|
806
806
|
}
|
|
807
807
|
if (typeName === "nullable") {
|
|
808
808
|
nullable = true;
|
|
809
|
-
current = current.
|
|
809
|
+
current = current.def?.innerType ?? current;
|
|
810
810
|
continue;
|
|
811
811
|
}
|
|
812
812
|
if (typeName === "pipe") {
|
|
813
|
-
const outType = current.
|
|
813
|
+
const outType = current.def?.out?.def?.type;
|
|
814
814
|
if (outType === "transform") {
|
|
815
|
-
current = current.
|
|
815
|
+
current = current.def?.in ?? current;
|
|
816
816
|
} else {
|
|
817
|
-
current = current.
|
|
817
|
+
current = current.def?.out ?? current;
|
|
818
818
|
}
|
|
819
819
|
continue;
|
|
820
820
|
}
|
|
821
821
|
if (typeName === "catch" || typeName === "readonly") {
|
|
822
|
-
current = current.
|
|
822
|
+
current = current.def?.innerType ?? current;
|
|
823
823
|
continue;
|
|
824
824
|
}
|
|
825
825
|
break;
|
|
@@ -835,7 +835,7 @@ function formatCore(schema, depth, seen) {
|
|
|
835
835
|
return "unknown";
|
|
836
836
|
}
|
|
837
837
|
seen.add(schema);
|
|
838
|
-
const typeName = schema?.
|
|
838
|
+
const typeName = schema?.def?.type;
|
|
839
839
|
switch (typeName) {
|
|
840
840
|
case "string":
|
|
841
841
|
return "string";
|
|
@@ -860,44 +860,44 @@ function formatCore(schema, depth, seen) {
|
|
|
860
860
|
case "void":
|
|
861
861
|
return "void";
|
|
862
862
|
case "literal": {
|
|
863
|
-
const value = schema.
|
|
863
|
+
const value = schema.def?.values?.[0];
|
|
864
864
|
return JSON.stringify(value);
|
|
865
865
|
}
|
|
866
866
|
case "enum": {
|
|
867
|
-
const entries = schema.
|
|
867
|
+
const entries = schema.def?.entries;
|
|
868
868
|
const values = Object.values(entries ?? {});
|
|
869
869
|
const unique = [...new Set(values.filter((v) => typeof v !== "string" || Number.isNaN(Number(v))))];
|
|
870
870
|
return unique.map((v) => JSON.stringify(v)).join(" | ") || "string";
|
|
871
871
|
}
|
|
872
872
|
case "array": {
|
|
873
|
-
const inner = formatType(schema.
|
|
873
|
+
const inner = formatType(schema.def?.element ?? schema, depth, seen);
|
|
874
874
|
return requiresParentheses(inner) ? `(${inner})[]` : `${inner}[]`;
|
|
875
875
|
}
|
|
876
876
|
case "tuple": {
|
|
877
|
-
const items = (schema.
|
|
877
|
+
const items = (schema.def?.items ?? []).map((item) => formatType(item, depth, seen));
|
|
878
878
|
return `[${items.join(", ")}]`;
|
|
879
879
|
}
|
|
880
880
|
case "union": {
|
|
881
|
-
const options = (schema.
|
|
881
|
+
const options = (schema.def?.options ?? []).map((option) => formatType(option, depth, seen));
|
|
882
882
|
return options.join(" | ") || "unknown";
|
|
883
883
|
}
|
|
884
884
|
case "intersection": {
|
|
885
|
-
const left = formatType(schema.
|
|
886
|
-
const right = formatType(schema.
|
|
885
|
+
const left = formatType(schema.def?.left ?? schema, depth, seen);
|
|
886
|
+
const right = formatType(schema.def?.right ?? schema, depth, seen);
|
|
887
887
|
return `${left} & ${right}`;
|
|
888
888
|
}
|
|
889
889
|
case "record": {
|
|
890
|
-
const keyType = formatType(schema.
|
|
891
|
-
const valueType = formatType(schema.
|
|
890
|
+
const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
|
|
891
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
892
892
|
return `Record<${keyType}, ${valueType}>`;
|
|
893
893
|
}
|
|
894
894
|
case "map": {
|
|
895
|
-
const keyType = formatType(schema.
|
|
896
|
-
const valueType = formatType(schema.
|
|
895
|
+
const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
|
|
896
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
897
897
|
return `Map<${keyType}, ${valueType}>`;
|
|
898
898
|
}
|
|
899
899
|
case "set": {
|
|
900
|
-
const valueType = formatType(schema.
|
|
900
|
+
const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
|
|
901
901
|
return `Set<${valueType}>`;
|
|
902
902
|
}
|
|
903
903
|
case "object":
|
|
@@ -911,7 +911,7 @@ function formatCore(schema, depth, seen) {
|
|
|
911
911
|
function formatObject(schema, depth, seen) {
|
|
912
912
|
const indent = " ".repeat(depth);
|
|
913
913
|
const innerIndent = " ".repeat(depth + 1);
|
|
914
|
-
const rawShape = schema.
|
|
914
|
+
const rawShape = schema.def?.shape;
|
|
915
915
|
const shape = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
|
|
916
916
|
const entries = Object.entries(shape);
|
|
917
917
|
if (entries.length === 0) {
|
|
@@ -937,27 +937,27 @@ function requiresParentheses(typeText) {
|
|
|
937
937
|
return typeText.includes(" | ") || typeText.includes(" & ");
|
|
938
938
|
}
|
|
939
939
|
function isIntegerNumber(schema) {
|
|
940
|
-
const checks = schema.
|
|
940
|
+
const checks = schema.def?.checks ?? [];
|
|
941
941
|
return checks.some((check) => check.isInt === true);
|
|
942
942
|
}
|
|
943
943
|
function readSchemaDescription(schema) {
|
|
944
944
|
let current = schema;
|
|
945
|
-
while (current?.
|
|
945
|
+
while (current?.def?.type) {
|
|
946
946
|
const desc = current.description;
|
|
947
947
|
if (typeof desc === "string" && desc.trim().length > 0) {
|
|
948
948
|
return sanitizeDescription(desc);
|
|
949
949
|
}
|
|
950
|
-
const typeName = current.
|
|
950
|
+
const typeName = current.def.type;
|
|
951
951
|
if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
|
|
952
|
-
current = current.
|
|
952
|
+
current = current.def.innerType ?? current;
|
|
953
953
|
continue;
|
|
954
954
|
}
|
|
955
955
|
if (typeName === "pipe") {
|
|
956
|
-
current = current.
|
|
956
|
+
current = current.def.in ?? current;
|
|
957
957
|
continue;
|
|
958
958
|
}
|
|
959
959
|
if (typeName === "catch" || typeName === "readonly") {
|
|
960
|
-
current = current.
|
|
960
|
+
current = current.def.innerType ?? current;
|
|
961
961
|
continue;
|
|
962
962
|
}
|
|
963
963
|
break;
|
|
@@ -1220,7 +1220,7 @@ function findSSEBoundary(buffer) {
|
|
|
1220
1220
|
}
|
|
1221
1221
|
|
|
1222
1222
|
// src/providers/mcp-runtime.ts
|
|
1223
|
-
var DEFAULT_MAX_TOOL_ROUNDS =
|
|
1223
|
+
var DEFAULT_MAX_TOOL_ROUNDS = 100;
|
|
1224
1224
|
async function resolveMCPToolset(clients) {
|
|
1225
1225
|
if (!Array.isArray(clients) || clients.length === 0) {
|
|
1226
1226
|
return {
|
|
@@ -1553,7 +1553,15 @@ function normalizeBaseURL(baseURL) {
|
|
|
1553
1553
|
return baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
|
|
1554
1554
|
}
|
|
1555
1555
|
function buildURL(baseURL, path) {
|
|
1556
|
-
|
|
1556
|
+
try {
|
|
1557
|
+
return new URL(path).toString();
|
|
1558
|
+
} catch {}
|
|
1559
|
+
const base = new URL(normalizeBaseURL(baseURL));
|
|
1560
|
+
const resolvedPath = new URL(path, "http://provider-path.local");
|
|
1561
|
+
base.pathname = mergePathnames(base.pathname, resolvedPath.pathname);
|
|
1562
|
+
base.search = resolvedPath.search;
|
|
1563
|
+
base.hash = resolvedPath.hash;
|
|
1564
|
+
return base.toString();
|
|
1557
1565
|
}
|
|
1558
1566
|
function safeJSONParse(input) {
|
|
1559
1567
|
try {
|
|
@@ -1628,6 +1636,36 @@ function addOptional(a, b) {
|
|
|
1628
1636
|
}
|
|
1629
1637
|
return (a ?? 0) + (b ?? 0);
|
|
1630
1638
|
}
|
|
1639
|
+
function mergePathnames(basePathname, pathPathname) {
|
|
1640
|
+
const baseSegments = splitPathSegments(basePathname);
|
|
1641
|
+
const pathSegments = splitPathSegments(pathPathname);
|
|
1642
|
+
const overlap = findPathOverlap(baseSegments, pathSegments);
|
|
1643
|
+
const mergedSegments = [...baseSegments, ...pathSegments.slice(overlap)];
|
|
1644
|
+
if (mergedSegments.length === 0) {
|
|
1645
|
+
return "/";
|
|
1646
|
+
}
|
|
1647
|
+
const mergedPathname = `/${mergedSegments.join("/")}`;
|
|
1648
|
+
return pathPathname.endsWith("/") && pathPathname !== "/" ? `${mergedPathname}/` : mergedPathname;
|
|
1649
|
+
}
|
|
1650
|
+
function splitPathSegments(pathname) {
|
|
1651
|
+
return pathname.split("/").filter((segment) => segment.length > 0);
|
|
1652
|
+
}
|
|
1653
|
+
function findPathOverlap(baseSegments, pathSegments) {
|
|
1654
|
+
const maxOverlap = Math.min(baseSegments.length, pathSegments.length);
|
|
1655
|
+
for (let size = maxOverlap;size > 0; size -= 1) {
|
|
1656
|
+
let matches = true;
|
|
1657
|
+
for (let index = 0;index < size; index += 1) {
|
|
1658
|
+
if (baseSegments[baseSegments.length - size + index] !== pathSegments[index]) {
|
|
1659
|
+
matches = false;
|
|
1660
|
+
break;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
if (matches) {
|
|
1664
|
+
return size;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
return 0;
|
|
1668
|
+
}
|
|
1631
1669
|
|
|
1632
1670
|
// src/providers/openai-compatible.ts
|
|
1633
1671
|
function createOpenAICompatibleAdapter(options) {
|
|
@@ -1786,7 +1824,7 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
|
|
|
1786
1824
|
const message = await response.text();
|
|
1787
1825
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
1788
1826
|
}
|
|
1789
|
-
const payload = await response
|
|
1827
|
+
const payload = await parseOpenAICompatibleJSONResponse(response, "Failed to parse OpenAI-compatible chat completion response");
|
|
1790
1828
|
const assistantMessage = pickAssistantMessage(payload);
|
|
1791
1829
|
if (!assistantMessage) {
|
|
1792
1830
|
throw new Error("No assistant message in OpenAI-compatible response.");
|
|
@@ -1802,6 +1840,25 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
|
|
|
1802
1840
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined
|
|
1803
1841
|
};
|
|
1804
1842
|
}
|
|
1843
|
+
async function parseOpenAICompatibleJSONResponse(response, context) {
|
|
1844
|
+
const rawBody = await response.text();
|
|
1845
|
+
try {
|
|
1846
|
+
return JSON.parse(rawBody);
|
|
1847
|
+
} catch (error) {
|
|
1848
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1849
|
+
throw new Error(`${context} (HTTP ${response.status}): ${message}. Raw body: ${formatResponseBodyForError(rawBody)}`);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
function formatResponseBodyForError(rawBody, maxLength = 2000) {
|
|
1853
|
+
const normalized = rawBody.trim();
|
|
1854
|
+
if (normalized.length === 0) {
|
|
1855
|
+
return "[empty body]";
|
|
1856
|
+
}
|
|
1857
|
+
if (normalized.length <= maxLength) {
|
|
1858
|
+
return normalized;
|
|
1859
|
+
}
|
|
1860
|
+
return `${normalized.slice(0, maxLength)}...[truncated ${normalized.length - maxLength} chars]`;
|
|
1861
|
+
}
|
|
1805
1862
|
async function completeWithChatCompletionsWithMCP(options, fetcher, path, request) {
|
|
1806
1863
|
const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
|
|
1807
1864
|
let messages = buildMessages(request);
|
|
@@ -1810,6 +1867,7 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1810
1867
|
let lastPayload;
|
|
1811
1868
|
const toolCalls = [];
|
|
1812
1869
|
const toolExecutions = [];
|
|
1870
|
+
const reasoningBlocks = [];
|
|
1813
1871
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1814
1872
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1815
1873
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
@@ -1840,14 +1898,17 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1840
1898
|
finishReason = pickFinishReason(payload);
|
|
1841
1899
|
const assistantMessage = pickAssistantMessage(payload);
|
|
1842
1900
|
const calledTools = pickChatToolCalls(payload);
|
|
1901
|
+
const roundReasoning = pickAssistantReasoning(payload);
|
|
1902
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
1843
1903
|
if (!assistantMessage) {
|
|
1844
1904
|
throw new Error("No assistant message in OpenAI-compatible response.");
|
|
1845
1905
|
}
|
|
1846
1906
|
if (calledTools.length === 0) {
|
|
1847
|
-
const reasoning =
|
|
1907
|
+
const reasoning = joinReasoningBlocks(reasoningBlocks) || undefined;
|
|
1848
1908
|
return {
|
|
1849
1909
|
text: pickAssistantText(payload),
|
|
1850
|
-
reasoning
|
|
1910
|
+
reasoning,
|
|
1911
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1851
1912
|
raw: payload,
|
|
1852
1913
|
usage: aggregatedUsage,
|
|
1853
1914
|
finishReason,
|
|
@@ -1875,10 +1936,8 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
|
|
|
1875
1936
|
}
|
|
1876
1937
|
return {
|
|
1877
1938
|
text: pickAssistantText(lastPayload ?? {}),
|
|
1878
|
-
reasoning: (
|
|
1879
|
-
|
|
1880
|
-
return value.length > 0 ? value : undefined;
|
|
1881
|
-
})(),
|
|
1939
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
1940
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1882
1941
|
raw: lastPayload,
|
|
1883
1942
|
usage: aggregatedUsage,
|
|
1884
1943
|
finishReason,
|
|
@@ -1926,6 +1985,7 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
1926
1985
|
let lastPayload;
|
|
1927
1986
|
const executedToolCalls = [];
|
|
1928
1987
|
const toolExecutions = [];
|
|
1988
|
+
const reasoningBlocks = [];
|
|
1929
1989
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
1930
1990
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
1931
1991
|
const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
|
|
@@ -1955,12 +2015,15 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
1955
2015
|
lastPayload = payload;
|
|
1956
2016
|
aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
|
|
1957
2017
|
finishReason = pickResponsesFinishReason(payload) ?? finishReason;
|
|
2018
|
+
pushReasoningBlock(reasoningBlocks, round, pickResponsesReasoning(payload));
|
|
1958
2019
|
const providerToolCalls = pickResponsesToolCalls(payload);
|
|
1959
2020
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
1960
2021
|
if (functionCalls.length === 0) {
|
|
1961
2022
|
const text = pickResponsesText(payload) || pickAssistantText(payload);
|
|
1962
2023
|
return {
|
|
1963
2024
|
text,
|
|
2025
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2026
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1964
2027
|
raw: payload,
|
|
1965
2028
|
usage: aggregatedUsage,
|
|
1966
2029
|
finishReason,
|
|
@@ -1988,6 +2051,8 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
|
|
|
1988
2051
|
}
|
|
1989
2052
|
return {
|
|
1990
2053
|
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
2054
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2055
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
1991
2056
|
raw: lastPayload,
|
|
1992
2057
|
usage: aggregatedUsage,
|
|
1993
2058
|
finishReason,
|
|
@@ -2003,9 +2068,9 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2003
2068
|
let lastPayload;
|
|
2004
2069
|
const executedToolCalls = [];
|
|
2005
2070
|
const toolExecutions = [];
|
|
2071
|
+
const reasoningBlocks = [];
|
|
2006
2072
|
callbacks.onStart?.();
|
|
2007
2073
|
let lastRoundText = "";
|
|
2008
|
-
let lastRoundReasoning = "";
|
|
2009
2074
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2010
2075
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2011
2076
|
const transportTools = toProviderFunctionTools(mcpToolset);
|
|
@@ -2067,6 +2132,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2067
2132
|
const chunk = {
|
|
2068
2133
|
textDelta: delta,
|
|
2069
2134
|
reasoningDelta: reasoningDelta || undefined,
|
|
2135
|
+
turnIndex: round,
|
|
2070
2136
|
raw: json,
|
|
2071
2137
|
usage: chunkUsage,
|
|
2072
2138
|
finishReason: chunkFinishReason
|
|
@@ -2079,22 +2145,41 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2079
2145
|
finishReason = roundFinishReason;
|
|
2080
2146
|
}
|
|
2081
2147
|
const calledTools = buildOpenAIStreamToolCalls(streamedToolCalls);
|
|
2148
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
2149
|
+
request.onTurnTransition?.({
|
|
2150
|
+
turnIndex: round,
|
|
2151
|
+
kind: "reasoningComplete",
|
|
2152
|
+
reasoningText: roundReasoning
|
|
2153
|
+
});
|
|
2082
2154
|
if (calledTools.length === 0) {
|
|
2083
2155
|
const out2 = {
|
|
2084
2156
|
text: roundText,
|
|
2085
|
-
reasoning:
|
|
2157
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2158
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2086
2159
|
raw: lastPayload,
|
|
2087
2160
|
usage: aggregatedUsage,
|
|
2088
2161
|
finishReason,
|
|
2089
2162
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2090
2163
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2091
2164
|
};
|
|
2165
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2092
2166
|
callbacks.onComplete?.(out2);
|
|
2093
2167
|
return out2;
|
|
2094
2168
|
}
|
|
2095
2169
|
if (round > maxToolRounds) {
|
|
2096
2170
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
2097
2171
|
}
|
|
2172
|
+
request.onTurnTransition?.({
|
|
2173
|
+
turnIndex: round,
|
|
2174
|
+
kind: "toolCallsEmit",
|
|
2175
|
+
toolCalls: calledTools
|
|
2176
|
+
});
|
|
2177
|
+
callbacks.onChunk?.({
|
|
2178
|
+
textDelta: "",
|
|
2179
|
+
turnIndex: round,
|
|
2180
|
+
toolCalls: calledTools,
|
|
2181
|
+
finishReason: roundFinishReason
|
|
2182
|
+
});
|
|
2098
2183
|
const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
|
|
2099
2184
|
round,
|
|
2100
2185
|
request,
|
|
@@ -2103,8 +2188,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2103
2188
|
});
|
|
2104
2189
|
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2105
2190
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2191
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2106
2192
|
lastRoundText = roundText;
|
|
2107
|
-
lastRoundReasoning = roundReasoning;
|
|
2108
2193
|
const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
|
|
2109
2194
|
reasoning: roundReasoning,
|
|
2110
2195
|
reasoningFieldName
|
|
@@ -2118,13 +2203,15 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
|
|
|
2118
2203
|
}
|
|
2119
2204
|
const out = {
|
|
2120
2205
|
text: lastRoundText,
|
|
2121
|
-
reasoning:
|
|
2206
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2207
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2122
2208
|
raw: lastPayload,
|
|
2123
2209
|
usage: aggregatedUsage,
|
|
2124
2210
|
finishReason,
|
|
2125
2211
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2126
2212
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2127
2213
|
};
|
|
2214
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2128
2215
|
callbacks.onComplete?.(out);
|
|
2129
2216
|
return out;
|
|
2130
2217
|
}
|
|
@@ -2207,6 +2294,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2207
2294
|
let lastPayload;
|
|
2208
2295
|
const executedToolCalls = [];
|
|
2209
2296
|
const toolExecutions = [];
|
|
2297
|
+
const reasoningBlocks = [];
|
|
2210
2298
|
callbacks.onStart?.();
|
|
2211
2299
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2212
2300
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
@@ -2235,6 +2323,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2235
2323
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
2236
2324
|
}
|
|
2237
2325
|
let roundText = "";
|
|
2326
|
+
let roundReasoning = "";
|
|
2238
2327
|
let roundUsage;
|
|
2239
2328
|
let roundFinishReason;
|
|
2240
2329
|
let roundPayload;
|
|
@@ -2253,6 +2342,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2253
2342
|
lastPayload = payload;
|
|
2254
2343
|
}
|
|
2255
2344
|
const delta = pickResponsesStreamTextDelta(json);
|
|
2345
|
+
const reasoningDelta = pickResponsesStreamReasoningDelta(json);
|
|
2256
2346
|
const chunkUsage = pickResponsesStreamUsage(json);
|
|
2257
2347
|
const chunkFinishReason = pickResponsesStreamFinishReason(json);
|
|
2258
2348
|
collectResponsesStreamToolCalls(json, streamedToolCalls);
|
|
@@ -2264,9 +2354,14 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2264
2354
|
roundText += delta;
|
|
2265
2355
|
callbacks.onToken?.(delta);
|
|
2266
2356
|
}
|
|
2267
|
-
if (
|
|
2357
|
+
if (reasoningDelta) {
|
|
2358
|
+
roundReasoning += reasoningDelta;
|
|
2359
|
+
}
|
|
2360
|
+
if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
|
|
2268
2361
|
const chunk = {
|
|
2269
2362
|
textDelta: delta,
|
|
2363
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
2364
|
+
turnIndex: round,
|
|
2270
2365
|
raw: json,
|
|
2271
2366
|
usage: chunkUsage,
|
|
2272
2367
|
finishReason: chunkFinishReason
|
|
@@ -2282,25 +2377,48 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2282
2377
|
finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
|
|
2283
2378
|
}
|
|
2284
2379
|
const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
|
|
2380
|
+
if (roundPayload && roundReasoning.length === 0) {
|
|
2381
|
+
roundReasoning = pickResponsesReasoning(roundPayload);
|
|
2382
|
+
}
|
|
2285
2383
|
const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
|
|
2286
2384
|
const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
|
|
2287
2385
|
const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
|
|
2386
|
+
pushReasoningBlock(reasoningBlocks, round, roundReasoning);
|
|
2387
|
+
request.onTurnTransition?.({
|
|
2388
|
+
turnIndex: round,
|
|
2389
|
+
kind: "reasoningComplete",
|
|
2390
|
+
reasoningText: roundReasoning
|
|
2391
|
+
});
|
|
2288
2392
|
if (functionCalls.length === 0) {
|
|
2289
2393
|
const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
|
|
2290
2394
|
const out2 = {
|
|
2291
2395
|
text: finalText,
|
|
2396
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2397
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2292
2398
|
raw: roundPayload ?? lastPayload,
|
|
2293
2399
|
usage: aggregatedUsage,
|
|
2294
2400
|
finishReason,
|
|
2295
2401
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2296
2402
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2297
2403
|
};
|
|
2404
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
2298
2405
|
callbacks.onComplete?.(out2);
|
|
2299
2406
|
return out2;
|
|
2300
2407
|
}
|
|
2301
2408
|
if (round > maxToolRounds) {
|
|
2302
2409
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
2303
2410
|
}
|
|
2411
|
+
request.onTurnTransition?.({
|
|
2412
|
+
turnIndex: round,
|
|
2413
|
+
kind: "toolCallsEmit",
|
|
2414
|
+
toolCalls: functionCalls
|
|
2415
|
+
});
|
|
2416
|
+
callbacks.onChunk?.({
|
|
2417
|
+
textDelta: "",
|
|
2418
|
+
turnIndex: round,
|
|
2419
|
+
toolCalls: functionCalls,
|
|
2420
|
+
finishReason: roundFinishReason
|
|
2421
|
+
});
|
|
2304
2422
|
const outputs = await executeMCPToolCalls(functionCalls, mcpToolset, {
|
|
2305
2423
|
round,
|
|
2306
2424
|
request,
|
|
@@ -2309,6 +2427,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2309
2427
|
});
|
|
2310
2428
|
executedToolCalls.push(...outputs.map((entry) => entry.call));
|
|
2311
2429
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
2430
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
2312
2431
|
input = outputs.map((entry) => ({
|
|
2313
2432
|
type: "function_call_output",
|
|
2314
2433
|
call_id: entry.call.id,
|
|
@@ -2318,12 +2437,15 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
|
|
|
2318
2437
|
}
|
|
2319
2438
|
const out = {
|
|
2320
2439
|
text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
|
|
2440
|
+
reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
|
|
2441
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2321
2442
|
raw: lastPayload,
|
|
2322
2443
|
usage: aggregatedUsage,
|
|
2323
2444
|
finishReason,
|
|
2324
2445
|
toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
|
|
2325
2446
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
2326
2447
|
};
|
|
2448
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
2327
2449
|
callbacks.onComplete?.(out);
|
|
2328
2450
|
return out;
|
|
2329
2451
|
}
|
|
@@ -2608,6 +2730,20 @@ function pickResponsesStreamTextDelta(payload) {
|
|
|
2608
2730
|
}
|
|
2609
2731
|
return "";
|
|
2610
2732
|
}
|
|
2733
|
+
function pickResponsesStreamReasoningDelta(payload) {
|
|
2734
|
+
const eventType = pickString(payload.type) ?? "";
|
|
2735
|
+
if (!eventType.includes("reasoning") && !eventType.includes("thinking")) {
|
|
2736
|
+
return "";
|
|
2737
|
+
}
|
|
2738
|
+
const direct = pickString(payload.delta);
|
|
2739
|
+
if (direct) {
|
|
2740
|
+
return direct;
|
|
2741
|
+
}
|
|
2742
|
+
if (isRecord2(payload.delta)) {
|
|
2743
|
+
return pickReasoningText(payload.delta) || pickString(payload.delta.text) || pickString(payload.delta.summary_text) || "";
|
|
2744
|
+
}
|
|
2745
|
+
return "";
|
|
2746
|
+
}
|
|
2611
2747
|
function pickResponsesStreamUsage(payload) {
|
|
2612
2748
|
const direct = pickUsage(payload);
|
|
2613
2749
|
if (direct) {
|
|
@@ -2742,6 +2878,30 @@ function pickResponsesText(payload) {
|
|
|
2742
2878
|
}).join("");
|
|
2743
2879
|
}).join("");
|
|
2744
2880
|
}
|
|
2881
|
+
function pickResponsesReasoning(payload) {
|
|
2882
|
+
const direct = pickReasoningText(payload);
|
|
2883
|
+
if (direct) {
|
|
2884
|
+
return direct;
|
|
2885
|
+
}
|
|
2886
|
+
const output = payload.output;
|
|
2887
|
+
if (!Array.isArray(output)) {
|
|
2888
|
+
return "";
|
|
2889
|
+
}
|
|
2890
|
+
return output.map((item) => {
|
|
2891
|
+
if (!isRecord2(item)) {
|
|
2892
|
+
return "";
|
|
2893
|
+
}
|
|
2894
|
+
const itemReasoning = pickReasoningText(item);
|
|
2895
|
+
if (itemReasoning) {
|
|
2896
|
+
return itemReasoning;
|
|
2897
|
+
}
|
|
2898
|
+
const itemType = pickString(item.type) ?? "";
|
|
2899
|
+
if ((itemType.includes("reasoning") || itemType.includes("thinking")) && Array.isArray(item.content)) {
|
|
2900
|
+
return item.content.map((part) => isRecord2(part) ? pickTextLike(part) : "").join("");
|
|
2901
|
+
}
|
|
2902
|
+
return "";
|
|
2903
|
+
}).join("");
|
|
2904
|
+
}
|
|
2745
2905
|
function pickAssistantText(payload) {
|
|
2746
2906
|
const message = pickAssistantMessage(payload);
|
|
2747
2907
|
if (message) {
|
|
@@ -2762,6 +2922,18 @@ function pickAssistantText(payload) {
|
|
|
2762
2922
|
function pickReasoningText(value) {
|
|
2763
2923
|
return pickTextLike(value.reasoning) || pickTextLike(value.reasoning_content);
|
|
2764
2924
|
}
|
|
2925
|
+
function pushReasoningBlock(blocks, turnIndex, text) {
|
|
2926
|
+
const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
|
|
2927
|
+
if (!clean) {
|
|
2928
|
+
return;
|
|
2929
|
+
}
|
|
2930
|
+
blocks.push({ turnIndex, text: clean });
|
|
2931
|
+
}
|
|
2932
|
+
function joinReasoningBlocks(blocks) {
|
|
2933
|
+
return blocks.map((block) => block.text).filter(Boolean).join(`
|
|
2934
|
+
|
|
2935
|
+
`);
|
|
2936
|
+
}
|
|
2765
2937
|
function pickTextFromOpenAIContent(value) {
|
|
2766
2938
|
return pickTextLike(value);
|
|
2767
2939
|
}
|
|
@@ -2942,6 +3114,7 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2942
3114
|
let lastPayload;
|
|
2943
3115
|
const toolCalls = [];
|
|
2944
3116
|
const toolExecutions = [];
|
|
3117
|
+
const reasoningBlocks = [];
|
|
2945
3118
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
2946
3119
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
2947
3120
|
const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
|
|
@@ -2971,9 +3144,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
2971
3144
|
finishReason = pickFinishReason2(payload);
|
|
2972
3145
|
const content = Array.isArray(payload.content) ? payload.content : [];
|
|
2973
3146
|
const calledTools = pickAnthropicToolCalls(payload).filter((call) => call.type === "function");
|
|
3147
|
+
pushReasoningBlock2(reasoningBlocks, round, extractAnthropicReasoning(payload));
|
|
2974
3148
|
if (calledTools.length === 0) {
|
|
2975
3149
|
return {
|
|
2976
3150
|
text: extractAnthropicText(payload),
|
|
3151
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3152
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
2977
3153
|
raw: payload,
|
|
2978
3154
|
usage: aggregatedUsage,
|
|
2979
3155
|
finishReason,
|
|
@@ -3009,6 +3185,8 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
|
|
|
3009
3185
|
}
|
|
3010
3186
|
return {
|
|
3011
3187
|
text: extractAnthropicText(lastPayload ?? {}),
|
|
3188
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3189
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3012
3190
|
raw: lastPayload,
|
|
3013
3191
|
usage: aggregatedUsage,
|
|
3014
3192
|
finishReason,
|
|
@@ -3025,6 +3203,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3025
3203
|
let lastPayload;
|
|
3026
3204
|
const toolCalls = [];
|
|
3027
3205
|
const toolExecutions = [];
|
|
3206
|
+
const reasoningBlocks = [];
|
|
3028
3207
|
callbacks.onStart?.();
|
|
3029
3208
|
for (let round = 1;round <= maxToolRounds + 1; round += 1) {
|
|
3030
3209
|
const mcpToolset = await resolveMCPToolset(request.mcpClients);
|
|
@@ -3050,6 +3229,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3050
3229
|
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
3051
3230
|
}
|
|
3052
3231
|
let roundText = "";
|
|
3232
|
+
let roundReasoning = "";
|
|
3053
3233
|
let roundUsage;
|
|
3054
3234
|
let roundFinishReason;
|
|
3055
3235
|
const streamedToolCalls = new Map;
|
|
@@ -3063,6 +3243,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3063
3243
|
}
|
|
3064
3244
|
lastPayload = json;
|
|
3065
3245
|
const delta = pickAnthropicDelta(json);
|
|
3246
|
+
const reasoningDelta = pickAnthropicReasoningDelta(json);
|
|
3066
3247
|
const chunkUsage = pickUsage2(json);
|
|
3067
3248
|
const chunkFinishReason = pickFinishReason2(json);
|
|
3068
3249
|
collectAnthropicStreamToolCalls(json, streamedToolCalls);
|
|
@@ -3074,9 +3255,14 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3074
3255
|
roundText += delta;
|
|
3075
3256
|
callbacks.onToken?.(delta);
|
|
3076
3257
|
}
|
|
3077
|
-
if (
|
|
3258
|
+
if (reasoningDelta) {
|
|
3259
|
+
roundReasoning += reasoningDelta;
|
|
3260
|
+
}
|
|
3261
|
+
if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
|
|
3078
3262
|
const chunk = {
|
|
3079
3263
|
textDelta: delta,
|
|
3264
|
+
reasoningDelta: reasoningDelta || undefined,
|
|
3265
|
+
turnIndex: round,
|
|
3080
3266
|
raw: json,
|
|
3081
3267
|
usage: chunkUsage,
|
|
3082
3268
|
finishReason: chunkFinishReason
|
|
@@ -3089,21 +3275,41 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3089
3275
|
finishReason = roundFinishReason;
|
|
3090
3276
|
}
|
|
3091
3277
|
const calledTools = buildAnthropicStreamToolCalls(streamedToolCalls);
|
|
3278
|
+
pushReasoningBlock2(reasoningBlocks, round, roundReasoning);
|
|
3279
|
+
request.onTurnTransition?.({
|
|
3280
|
+
turnIndex: round,
|
|
3281
|
+
kind: "reasoningComplete",
|
|
3282
|
+
reasoningText: roundReasoning
|
|
3283
|
+
});
|
|
3092
3284
|
if (calledTools.length === 0) {
|
|
3093
3285
|
const out2 = {
|
|
3094
3286
|
text: roundText,
|
|
3287
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3288
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3095
3289
|
raw: lastPayload,
|
|
3096
3290
|
usage: aggregatedUsage,
|
|
3097
3291
|
finishReason,
|
|
3098
3292
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
3099
3293
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
3100
3294
|
};
|
|
3295
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
|
|
3101
3296
|
callbacks.onComplete?.(out2);
|
|
3102
3297
|
return out2;
|
|
3103
3298
|
}
|
|
3104
3299
|
if (round > maxToolRounds) {
|
|
3105
3300
|
throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
|
|
3106
3301
|
}
|
|
3302
|
+
request.onTurnTransition?.({
|
|
3303
|
+
turnIndex: round,
|
|
3304
|
+
kind: "toolCallsEmit",
|
|
3305
|
+
toolCalls: calledTools
|
|
3306
|
+
});
|
|
3307
|
+
callbacks.onChunk?.({
|
|
3308
|
+
textDelta: "",
|
|
3309
|
+
turnIndex: round,
|
|
3310
|
+
toolCalls: calledTools,
|
|
3311
|
+
finishReason: roundFinishReason
|
|
3312
|
+
});
|
|
3107
3313
|
const toolResultContent = [];
|
|
3108
3314
|
const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
|
|
3109
3315
|
round,
|
|
@@ -3113,6 +3319,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3113
3319
|
});
|
|
3114
3320
|
toolCalls.push(...outputs.map((entry) => entry.call));
|
|
3115
3321
|
toolExecutions.push(...outputs.map((entry) => entry.execution));
|
|
3322
|
+
request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
|
|
3116
3323
|
for (const entry of outputs) {
|
|
3117
3324
|
toolResultContent.push({
|
|
3118
3325
|
type: "tool_result",
|
|
@@ -3129,12 +3336,15 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
|
|
|
3129
3336
|
}
|
|
3130
3337
|
const out = {
|
|
3131
3338
|
text: "",
|
|
3339
|
+
reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
|
|
3340
|
+
reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
|
|
3132
3341
|
raw: lastPayload,
|
|
3133
3342
|
usage: aggregatedUsage,
|
|
3134
3343
|
finishReason,
|
|
3135
3344
|
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
3136
3345
|
toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
|
|
3137
3346
|
};
|
|
3347
|
+
request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
|
|
3138
3348
|
callbacks.onComplete?.(out);
|
|
3139
3349
|
return out;
|
|
3140
3350
|
}
|
|
@@ -3255,6 +3465,22 @@ function extractAnthropicText(payload) {
|
|
|
3255
3465
|
return typeof text === "string" ? text : "";
|
|
3256
3466
|
}).join("");
|
|
3257
3467
|
}
|
|
3468
|
+
function extractAnthropicReasoning(payload) {
|
|
3469
|
+
const content = payload.content;
|
|
3470
|
+
if (!Array.isArray(content)) {
|
|
3471
|
+
return "";
|
|
3472
|
+
}
|
|
3473
|
+
return content.map((part) => {
|
|
3474
|
+
if (!isRecord2(part)) {
|
|
3475
|
+
return "";
|
|
3476
|
+
}
|
|
3477
|
+
const type = pickString(part.type) ?? "";
|
|
3478
|
+
if (type !== "thinking" && type !== "reasoning") {
|
|
3479
|
+
return "";
|
|
3480
|
+
}
|
|
3481
|
+
return pickString(part.thinking) ?? pickString(part.text) ?? pickString(part.reasoning) ?? "";
|
|
3482
|
+
}).join("");
|
|
3483
|
+
}
|
|
3258
3484
|
function pickAnthropicToolCalls(payload) {
|
|
3259
3485
|
const content = payload.content;
|
|
3260
3486
|
if (!Array.isArray(content)) {
|
|
@@ -3285,6 +3511,35 @@ function pickAnthropicDelta(payload) {
|
|
|
3285
3511
|
}
|
|
3286
3512
|
return "";
|
|
3287
3513
|
}
|
|
3514
|
+
function pickAnthropicReasoningDelta(payload) {
|
|
3515
|
+
const deltaObject = payload.delta;
|
|
3516
|
+
if (isRecord2(deltaObject)) {
|
|
3517
|
+
const type = pickString(deltaObject.type) ?? "";
|
|
3518
|
+
if (type === "thinking_delta" || type === "reasoning_delta") {
|
|
3519
|
+
return pickString(deltaObject.thinking) ?? pickString(deltaObject.text) ?? "";
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
const contentBlock = payload.content_block;
|
|
3523
|
+
if (isRecord2(contentBlock)) {
|
|
3524
|
+
const type = pickString(contentBlock.type) ?? "";
|
|
3525
|
+
if (type === "thinking" || type === "reasoning") {
|
|
3526
|
+
return pickString(contentBlock.thinking) ?? pickString(contentBlock.text) ?? "";
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
return "";
|
|
3530
|
+
}
|
|
3531
|
+
function pushReasoningBlock2(blocks, turnIndex, text) {
|
|
3532
|
+
const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
|
|
3533
|
+
if (!clean) {
|
|
3534
|
+
return;
|
|
3535
|
+
}
|
|
3536
|
+
blocks.push({ turnIndex, text: clean });
|
|
3537
|
+
}
|
|
3538
|
+
function joinReasoningBlocks2(blocks) {
|
|
3539
|
+
return blocks.map((block) => block.text).filter(Boolean).join(`
|
|
3540
|
+
|
|
3541
|
+
`);
|
|
3542
|
+
}
|
|
3288
3543
|
function collectAnthropicStreamToolCalls(payload, state) {
|
|
3289
3544
|
const eventType = pickString(payload.type);
|
|
3290
3545
|
if (!eventType) {
|
|
@@ -3758,6 +4013,7 @@ function normalizeStreamConfig(option) {
|
|
|
3758
4013
|
return {
|
|
3759
4014
|
enabled: option.enabled ?? true,
|
|
3760
4015
|
onData: option.onData,
|
|
4016
|
+
onTurnTransition: option.onTurnTransition,
|
|
3761
4017
|
to: option.to
|
|
3762
4018
|
};
|
|
3763
4019
|
}
|
|
@@ -3825,6 +4081,7 @@ async function callModel(adapter, options) {
|
|
|
3825
4081
|
transformToolCallParams: options.request?.transformToolCallParams,
|
|
3826
4082
|
unknownToolError: options.request?.unknownToolError,
|
|
3827
4083
|
toolDebug: options.request?.toolDebug,
|
|
4084
|
+
onTurnTransition: options.stream.onTurnTransition,
|
|
3828
4085
|
body: options.request?.body,
|
|
3829
4086
|
signal: requestSignal
|
|
3830
4087
|
};
|
|
@@ -3852,13 +4109,21 @@ async function callModel(adapter, options) {
|
|
|
3852
4109
|
let latestFinishReason;
|
|
3853
4110
|
let streamedProviderText = "";
|
|
3854
4111
|
let streamedDedicatedReasoning = "";
|
|
4112
|
+
let currentTurnIndex;
|
|
4113
|
+
let currentToolCalls;
|
|
4114
|
+
let streamedReasoningBlocks;
|
|
3855
4115
|
let lastSnapshotFingerprint;
|
|
3856
4116
|
let previousSnapshotText = "";
|
|
3857
4117
|
let previousSnapshotReasoning = "";
|
|
3858
4118
|
const emitStreamingData = (done, usage2, finishReason2) => {
|
|
3859
|
-
const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
|
|
4119
|
+
const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
|
|
3860
4120
|
const snapshot = options.buildSnapshot(normalized2);
|
|
3861
|
-
const fingerprint = toStreamDataFingerprint(
|
|
4121
|
+
const fingerprint = toStreamDataFingerprint({
|
|
4122
|
+
snapshot,
|
|
4123
|
+
done,
|
|
4124
|
+
turnIndex: currentTurnIndex,
|
|
4125
|
+
toolCalls: currentToolCalls
|
|
4126
|
+
});
|
|
3862
4127
|
if (!done && fingerprint === lastSnapshotFingerprint) {
|
|
3863
4128
|
return;
|
|
3864
4129
|
}
|
|
@@ -3874,7 +4139,9 @@ async function callModel(adapter, options) {
|
|
|
3874
4139
|
snapshot,
|
|
3875
4140
|
done,
|
|
3876
4141
|
usage: usage2,
|
|
3877
|
-
finishReason: finishReason2
|
|
4142
|
+
finishReason: finishReason2,
|
|
4143
|
+
turnIndex: currentTurnIndex,
|
|
4144
|
+
toolCalls: currentToolCalls
|
|
3878
4145
|
});
|
|
3879
4146
|
if (options.stream.to === "stdout" && delta.text) {
|
|
3880
4147
|
process.stdout.write(delta.text);
|
|
@@ -3909,8 +4176,21 @@ async function callModel(adapter, options) {
|
|
|
3909
4176
|
streamedDedicatedReasoning += delta;
|
|
3910
4177
|
emitStreamingData(false);
|
|
3911
4178
|
};
|
|
3912
|
-
const
|
|
4179
|
+
const streamRequestPayload = {
|
|
4180
|
+
...requestPayload,
|
|
4181
|
+
onTurnTransition: (transition) => {
|
|
4182
|
+
if (transition.kind === "reasoningComplete") {
|
|
4183
|
+
streamedReasoningBlocks = appendReasoningBlock(streamedReasoningBlocks, transition);
|
|
4184
|
+
}
|
|
4185
|
+
options.stream.onTurnTransition?.(transition);
|
|
4186
|
+
}
|
|
4187
|
+
};
|
|
4188
|
+
const response2 = await adapter.stream(streamRequestPayload, {
|
|
3913
4189
|
onChunk: (chunk) => {
|
|
4190
|
+
if (chunk.turnIndex !== undefined) {
|
|
4191
|
+
currentTurnIndex = chunk.turnIndex;
|
|
4192
|
+
}
|
|
4193
|
+
currentToolCalls = chunk.toolCalls;
|
|
3914
4194
|
if (chunk.textDelta) {
|
|
3915
4195
|
handleTextDelta(chunk.textDelta);
|
|
3916
4196
|
}
|
|
@@ -3923,11 +4203,15 @@ async function callModel(adapter, options) {
|
|
|
3923
4203
|
if (chunk.finishReason) {
|
|
3924
4204
|
latestFinishReason = chunk.finishReason;
|
|
3925
4205
|
}
|
|
4206
|
+
if (!chunk.textDelta && !chunk.reasoningDelta && (chunk.turnIndex !== undefined || chunk.toolCalls)) {
|
|
4207
|
+
emitStreamingData(false, chunk.usage, chunk.finishReason);
|
|
4208
|
+
}
|
|
3926
4209
|
}
|
|
3927
4210
|
});
|
|
3928
4211
|
streamedProviderText = typeof response2.text === "string" ? response2.text : streamedProviderText;
|
|
3929
4212
|
streamedDedicatedReasoning = typeof response2.reasoning === "string" ? response2.reasoning : streamedDedicatedReasoning;
|
|
3930
|
-
|
|
4213
|
+
streamedReasoningBlocks = response2.reasoningBlocks ?? streamedReasoningBlocks;
|
|
4214
|
+
const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
|
|
3931
4215
|
const usage = preferLatestUsage(latestUsage, response2.usage);
|
|
3932
4216
|
const finishReason = response2.finishReason ?? latestFinishReason;
|
|
3933
4217
|
emitStreamingData(true, usage, finishReason);
|
|
@@ -3959,11 +4243,12 @@ async function callModel(adapter, options) {
|
|
|
3959
4243
|
parseSource: finalNormalized.parseSource,
|
|
3960
4244
|
via: "stream",
|
|
3961
4245
|
usage,
|
|
3962
|
-
finishReason
|
|
4246
|
+
finishReason,
|
|
4247
|
+
reasoningBlocks: finalNormalized.reasoningBlocks
|
|
3963
4248
|
};
|
|
3964
4249
|
}
|
|
3965
4250
|
const response = await adapter.complete(requestPayload);
|
|
3966
|
-
const normalized = normalizeModelOutput(response.text, response.reasoning);
|
|
4251
|
+
const normalized = normalizeModelOutput(response.text, response.reasoning, response.reasoningBlocks);
|
|
3967
4252
|
options.observe?.(options.buildEvent({
|
|
3968
4253
|
stage: "llm.response",
|
|
3969
4254
|
message: "Completion response received.",
|
|
@@ -3992,10 +4277,11 @@ async function callModel(adapter, options) {
|
|
|
3992
4277
|
parseSource: normalized.parseSource,
|
|
3993
4278
|
via: "complete",
|
|
3994
4279
|
usage: response.usage,
|
|
3995
|
-
finishReason: response.finishReason
|
|
4280
|
+
finishReason: response.finishReason,
|
|
4281
|
+
reasoningBlocks: normalized.reasoningBlocks
|
|
3996
4282
|
};
|
|
3997
4283
|
}
|
|
3998
|
-
function normalizeModelOutput(text, dedicatedReasoning) {
|
|
4284
|
+
function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
|
|
3999
4285
|
const sanitized = sanitizeThink(text);
|
|
4000
4286
|
const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
|
|
4001
4287
|
const reasoning = joinReasoningSegments([
|
|
@@ -4005,10 +4291,29 @@ function normalizeModelOutput(text, dedicatedReasoning) {
|
|
|
4005
4291
|
return {
|
|
4006
4292
|
text: visibleText,
|
|
4007
4293
|
reasoning,
|
|
4294
|
+
reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
|
|
4008
4295
|
thinkBlocks: sanitized.thinkBlocks,
|
|
4009
4296
|
parseSource: composeParseSource(visibleText, reasoning)
|
|
4010
4297
|
};
|
|
4011
4298
|
}
|
|
4299
|
+
function normalizeReasoningBlocks(blocks) {
|
|
4300
|
+
if (!Array.isArray(blocks)) {
|
|
4301
|
+
return;
|
|
4302
|
+
}
|
|
4303
|
+
const normalized = blocks.map((block) => ({
|
|
4304
|
+
turnIndex: block.turnIndex,
|
|
4305
|
+
text: block.text.replace(RE_THINK_TAGS, "").trim()
|
|
4306
|
+
})).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
|
|
4307
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
4308
|
+
}
|
|
4309
|
+
function appendReasoningBlock(blocks, transition) {
|
|
4310
|
+
const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
|
|
4311
|
+
if (!text) {
|
|
4312
|
+
return blocks;
|
|
4313
|
+
}
|
|
4314
|
+
const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
|
|
4315
|
+
return normalizeReasoningBlocks(next);
|
|
4316
|
+
}
|
|
4012
4317
|
function composeParseSource(text, reasoning) {
|
|
4013
4318
|
if (typeof reasoning !== "string" || reasoning.length === 0) {
|
|
4014
4319
|
return text;
|
|
@@ -4168,7 +4473,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4168
4473
|
}),
|
|
4169
4474
|
buildSnapshot: (model) => ({
|
|
4170
4475
|
text: model.text,
|
|
4171
|
-
reasoning: model.reasoning
|
|
4476
|
+
reasoning: model.reasoning,
|
|
4477
|
+
...model.reasoningBlocks ? { reasoningBlocks: model.reasoningBlocks } : {}
|
|
4172
4478
|
}),
|
|
4173
4479
|
debug: debugConfig,
|
|
4174
4480
|
debugLabel: "generate",
|
|
@@ -4183,7 +4489,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4183
4489
|
text: response.text,
|
|
4184
4490
|
reasoning: response.reasoning,
|
|
4185
4491
|
usage: response.usage,
|
|
4186
|
-
finishReason: response.finishReason
|
|
4492
|
+
finishReason: response.finishReason,
|
|
4493
|
+
...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {}
|
|
4187
4494
|
};
|
|
4188
4495
|
const attempts = [attempt];
|
|
4189
4496
|
normalized.observe?.({
|
|
@@ -4200,7 +4507,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
|
|
|
4200
4507
|
reasoning: attempt.reasoning,
|
|
4201
4508
|
attempts,
|
|
4202
4509
|
usage: aggregateUsage(attempts),
|
|
4203
|
-
finishReason: attempt.finishReason
|
|
4510
|
+
finishReason: attempt.finishReason,
|
|
4511
|
+
...attempt.reasoningBlocks ? { reasoningBlocks: attempt.reasoningBlocks } : {}
|
|
4204
4512
|
};
|
|
4205
4513
|
}
|
|
4206
4514
|
function normalizeGenerateInput(promptOrOptions, callOptions) {
|
|
@@ -5020,6 +5328,7 @@ async function executeAttempt(adapter, input) {
|
|
|
5020
5328
|
success: parsed.success,
|
|
5021
5329
|
usage: response.usage,
|
|
5022
5330
|
finishReason: response.finishReason,
|
|
5331
|
+
...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {},
|
|
5023
5332
|
parsed
|
|
5024
5333
|
};
|
|
5025
5334
|
return {
|
|
@@ -5040,6 +5349,7 @@ async function callModel2(adapter, options) {
|
|
|
5040
5349
|
buildSnapshot: (normalized) => ({
|
|
5041
5350
|
text: normalized.text,
|
|
5042
5351
|
reasoning: normalized.reasoning,
|
|
5352
|
+
...normalized.reasoningBlocks ? { reasoningBlocks: normalized.reasoningBlocks } : {},
|
|
5043
5353
|
data: parseStreamingStructuredData(normalized.parseSource) ?? null
|
|
5044
5354
|
}),
|
|
5045
5355
|
debugLabel: "structured"
|
|
@@ -5140,7 +5450,8 @@ function buildSuccessResult(data, attempts) {
|
|
|
5140
5450
|
json: final?.json ?? null,
|
|
5141
5451
|
attempts,
|
|
5142
5452
|
usage: aggregateUsage(attempts),
|
|
5143
|
-
finishReason: final?.finishReason
|
|
5453
|
+
finishReason: final?.finishReason,
|
|
5454
|
+
...final?.reasoningBlocks ? { reasoningBlocks: final.reasoningBlocks } : {}
|
|
5144
5455
|
};
|
|
5145
5456
|
}
|
|
5146
5457
|
function toStructuredError(attempt) {
|
|
@@ -5603,11 +5914,11 @@ function inferSchemaExample(schema) {
|
|
|
5603
5914
|
}
|
|
5604
5915
|
function getObjectShape(schema) {
|
|
5605
5916
|
const unwrapped = unwrap2(schema).schema;
|
|
5606
|
-
const typeName = unwrapped.
|
|
5917
|
+
const typeName = unwrapped.def?.type;
|
|
5607
5918
|
if (typeName !== "object") {
|
|
5608
5919
|
return null;
|
|
5609
5920
|
}
|
|
5610
|
-
const rawShape = unwrapped.
|
|
5921
|
+
const rawShape = unwrapped.def?.shape;
|
|
5611
5922
|
if (typeof rawShape === "function") {
|
|
5612
5923
|
return rawShape();
|
|
5613
5924
|
}
|
|
@@ -5615,11 +5926,11 @@ function getObjectShape(schema) {
|
|
|
5615
5926
|
}
|
|
5616
5927
|
function readDefaultValue(schema) {
|
|
5617
5928
|
let current = schema;
|
|
5618
|
-
while (current?.
|
|
5619
|
-
const typeName = current.
|
|
5929
|
+
while (current?.def?.type) {
|
|
5930
|
+
const typeName = current.def.type;
|
|
5620
5931
|
if (typeName === "default") {
|
|
5621
5932
|
try {
|
|
5622
|
-
const raw = current.
|
|
5933
|
+
const raw = current.def.defaultValue;
|
|
5623
5934
|
if (typeof raw === "function") {
|
|
5624
5935
|
return raw();
|
|
5625
5936
|
}
|
|
@@ -5629,11 +5940,11 @@ function readDefaultValue(schema) {
|
|
|
5629
5940
|
}
|
|
5630
5941
|
}
|
|
5631
5942
|
if (typeName === "optional" || typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
|
|
5632
|
-
current = current.
|
|
5943
|
+
current = current.def.innerType ?? current;
|
|
5633
5944
|
continue;
|
|
5634
5945
|
}
|
|
5635
5946
|
if (typeName === "pipe") {
|
|
5636
|
-
current = current.
|
|
5947
|
+
current = current.def.in ?? current;
|
|
5637
5948
|
continue;
|
|
5638
5949
|
}
|
|
5639
5950
|
return;
|
|
@@ -5642,22 +5953,22 @@ function readDefaultValue(schema) {
|
|
|
5642
5953
|
}
|
|
5643
5954
|
function readSchemaDescription2(schema) {
|
|
5644
5955
|
let current = schema;
|
|
5645
|
-
while (current?.
|
|
5956
|
+
while (current?.def?.type) {
|
|
5646
5957
|
const desc = current.description;
|
|
5647
5958
|
if (typeof desc === "string" && desc.trim().length > 0) {
|
|
5648
5959
|
return desc.trim();
|
|
5649
5960
|
}
|
|
5650
|
-
const typeName = current.
|
|
5961
|
+
const typeName = current.def.type;
|
|
5651
5962
|
if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
|
|
5652
|
-
current = current.
|
|
5963
|
+
current = current.def.innerType ?? current;
|
|
5653
5964
|
continue;
|
|
5654
5965
|
}
|
|
5655
5966
|
if (typeName === "catch" || typeName === "readonly") {
|
|
5656
|
-
current = current.
|
|
5967
|
+
current = current.def.innerType ?? current;
|
|
5657
5968
|
continue;
|
|
5658
5969
|
}
|
|
5659
5970
|
if (typeName === "pipe") {
|
|
5660
|
-
current = current.
|
|
5971
|
+
current = current.def.in ?? current;
|
|
5661
5972
|
continue;
|
|
5662
5973
|
}
|
|
5663
5974
|
break;
|
|
@@ -5667,19 +5978,19 @@ function readSchemaDescription2(schema) {
|
|
|
5667
5978
|
function unwrap2(schema) {
|
|
5668
5979
|
let current = schema;
|
|
5669
5980
|
let optional = false;
|
|
5670
|
-
while (current?.
|
|
5671
|
-
const typeName = current.
|
|
5981
|
+
while (current?.def?.type) {
|
|
5982
|
+
const typeName = current.def.type;
|
|
5672
5983
|
if (typeName === "optional" || typeName === "default") {
|
|
5673
5984
|
optional = true;
|
|
5674
|
-
current = current.
|
|
5985
|
+
current = current.def.innerType ?? current;
|
|
5675
5986
|
continue;
|
|
5676
5987
|
}
|
|
5677
5988
|
if (typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
|
|
5678
|
-
current = current.
|
|
5989
|
+
current = current.def.innerType ?? current;
|
|
5679
5990
|
continue;
|
|
5680
5991
|
}
|
|
5681
5992
|
if (typeName === "pipe") {
|
|
5682
|
-
current = current.
|
|
5993
|
+
current = current.def.in ?? current;
|
|
5683
5994
|
continue;
|
|
5684
5995
|
}
|
|
5685
5996
|
break;
|