@kortyx/hooks 0.15.0 → 0.16.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/CHANGELOG.md +15 -0
- package/dist/index.d.ts +22 -2
- package/dist/index.js +423 -57
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +4 -1
- package/dist/internal.js +12 -5
- package/dist/internal.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.16.0](https://github.com/kortyx-io/kortyx/compare/hooks-v0.15.0...hooks-v0.16.0) (2026-05-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add MCP tool support to useReason ([8352721](https://github.com/kortyx-io/kortyx/commit/83527215fd2fa6362348d842f8f3b9e9d9d03704))
|
|
9
|
+
* rename useReason tool policy to tool execution ([d6ee2f0](https://github.com/kortyx-io/kortyx/commit/d6ee2f0c92b05f9f51cb38745e94c7fed6158278))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Dependencies
|
|
13
|
+
|
|
14
|
+
* The following workspace dependencies were updated
|
|
15
|
+
* dependencies
|
|
16
|
+
* @kortyx/providers bumped to 0.5.0
|
|
17
|
+
|
|
3
18
|
## [0.15.0](https://github.com/kortyx-io/kortyx/compare/hooks-v0.14.0...hooks-v0.15.0) (2026-05-24)
|
|
4
19
|
|
|
5
20
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NodeConfig, InterruptInput, InterruptResult, GraphState } from '@kortyx/core';
|
|
2
2
|
import { R as ReasonTraceAdapter, K as KortyxTraceMetadata } from './tracing-DK-T8zX5.js';
|
|
3
3
|
export { a as KortyxTelemetryConfig, b as KortyxTelemetryContentCapture, c as KortyxTelemetryPrompt, d as KortyxTraceAdapter, e as ReasonTraceAttributes, f as ReasonTraceSpan, g as ReasonTraceSpanEndArgs, h as ReasonTraceSpanStartArgs } from './tracing-DK-T8zX5.js';
|
|
4
|
-
import { ProviderModelRef, KortyxReasoningOptions, KortyxResponseFormat, KortyxUsage, KortyxFinishReason, KortyxProviderMetadata, KortyxWarning } from '@kortyx/providers';
|
|
4
|
+
import { ProviderModelRef, KortyxReasoningOptions, KortyxResponseFormat, KortyxExecutableTool, KortyxUsage, KortyxFinishReason, KortyxProviderMetadata, KortyxWarning, KortyxToolCall, KortyxToolResult } from '@kortyx/providers';
|
|
5
5
|
|
|
6
6
|
type HookNodeRuntimeContext = {
|
|
7
7
|
graph: {
|
|
@@ -91,6 +91,11 @@ type UseReasonInterruptConfig<TRequest extends InterruptInput = InterruptInput,
|
|
|
91
91
|
schemaId?: string | undefined;
|
|
92
92
|
schemaVersion?: string | undefined;
|
|
93
93
|
};
|
|
94
|
+
type UseReasonToolExecution = {
|
|
95
|
+
maxSteps?: number | undefined;
|
|
96
|
+
approval?: boolean | Record<string, boolean> | undefined;
|
|
97
|
+
emit?: boolean | Record<string, boolean> | undefined;
|
|
98
|
+
};
|
|
94
99
|
type UseReasonArgs<TOutput = unknown, TRequest extends InterruptInput = InterruptInput, TResponse = InterruptResult> = {
|
|
95
100
|
model: ProviderModelRef;
|
|
96
101
|
input: string;
|
|
@@ -109,6 +114,18 @@ type UseReasonArgs<TOutput = unknown, TRequest extends InterruptInput = Interrup
|
|
|
109
114
|
outputSchema?: SchemaLike<TOutput> | undefined;
|
|
110
115
|
structured?: UseReasonStructuredConfig | undefined;
|
|
111
116
|
interrupt?: UseReasonInterruptConfig<TRequest, TResponse> | undefined;
|
|
117
|
+
tools?: KortyxExecutableTool[] | undefined;
|
|
118
|
+
toolExecution?: UseReasonToolExecution | undefined;
|
|
119
|
+
};
|
|
120
|
+
type UseReasonStep = {
|
|
121
|
+
stepIndex: number;
|
|
122
|
+
text: string;
|
|
123
|
+
toolCalls: KortyxToolCall[];
|
|
124
|
+
toolResults: KortyxToolResult[];
|
|
125
|
+
usage?: KortyxUsage | undefined;
|
|
126
|
+
finishReason?: KortyxFinishReason | undefined;
|
|
127
|
+
providerMetadata?: KortyxProviderMetadata | undefined;
|
|
128
|
+
warnings?: KortyxWarning[] | undefined;
|
|
112
129
|
};
|
|
113
130
|
type UseReasonResult<TOutput = unknown, TResponse = InterruptResult> = {
|
|
114
131
|
id?: string;
|
|
@@ -121,6 +138,9 @@ type UseReasonResult<TOutput = unknown, TResponse = InterruptResult> = {
|
|
|
121
138
|
warnings?: KortyxWarning[];
|
|
122
139
|
output?: TOutput;
|
|
123
140
|
interruptResponse?: TResponse;
|
|
141
|
+
toolCalls?: KortyxToolCall[];
|
|
142
|
+
toolResults?: KortyxToolResult[];
|
|
143
|
+
steps?: UseReasonStep[];
|
|
124
144
|
};
|
|
125
145
|
type UseInterruptArgs<TRequest extends InterruptInput = InterruptInput, TResponse = InterruptResult> = {
|
|
126
146
|
request: TRequest;
|
|
@@ -140,4 +160,4 @@ declare function useRuntimeContext<TContext extends Record<string, unknown> = Re
|
|
|
140
160
|
declare function useNodeState<T>(initialValue: T): [T, StateSetter<T>];
|
|
141
161
|
declare function useWorkflowState<T>(key: string, ...rest: [] | [T]): [T, StateSetter<T>];
|
|
142
162
|
|
|
143
|
-
export { type HookRuntimeContext, KortyxTraceMetadata, ReasonTraceAdapter, type UseInterruptArgs, type UseReasonArgs, type UseReasonResult, type UseStructuredDataArgs, runWithHookContext, useInterrupt, useNodeState, useReason, useRuntimeContext, useStructuredData, useWorkflowState };
|
|
163
|
+
export { type HookRuntimeContext, KortyxTraceMetadata, ReasonTraceAdapter, type UseInterruptArgs, type UseReasonArgs, type UseReasonResult, type UseReasonStep, type UseReasonToolExecution, type UseStructuredDataArgs, runWithHookContext, useInterrupt, useNodeState, useReason, useRuntimeContext, useStructuredData, useWorkflowState };
|
package/dist/index.js
CHANGED
|
@@ -373,6 +373,7 @@ async function runReasonEngine(args) {
|
|
|
373
373
|
const reasoning = args.reasoning ?? args.model.options?.reasoning;
|
|
374
374
|
const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
|
|
375
375
|
const providerOptions = args.providerOptions ?? args.model.options?.providerOptions;
|
|
376
|
+
const tools = args.tools ?? args.model.options?.tools;
|
|
376
377
|
const model = args.model.provider.getModel(args.model.modelId, {
|
|
377
378
|
...temperature !== void 0 ? { temperature } : {},
|
|
378
379
|
streaming: stream,
|
|
@@ -381,13 +382,13 @@ async function runReasonEngine(args) {
|
|
|
381
382
|
...abortSignal !== void 0 ? { abortSignal } : {},
|
|
382
383
|
...reasoning !== void 0 ? { reasoning } : {},
|
|
383
384
|
...responseFormat !== void 0 ? { responseFormat } : {},
|
|
385
|
+
...tools !== void 0 ? { tools } : {},
|
|
384
386
|
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
385
387
|
});
|
|
386
|
-
const messages = [
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
messages.push({ role: "user", content: String(args.input ?? "") });
|
|
388
|
+
const messages = args.messages !== void 0 ? args.messages : [
|
|
389
|
+
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
390
|
+
{ role: "user", content: String(args.input ?? "") }
|
|
391
|
+
];
|
|
391
392
|
const opId = typeof args.opId === "string" && args.opId.length > 0 ? args.opId : createRuntimeId();
|
|
392
393
|
const segmentId = typeof args.segmentId === "string" && args.segmentId.length > 0 ? args.segmentId : createRuntimeId();
|
|
393
394
|
const commonMeta = {
|
|
@@ -443,6 +444,7 @@ async function runReasonEngine(args) {
|
|
|
443
444
|
let raw;
|
|
444
445
|
let usage;
|
|
445
446
|
let finishReason;
|
|
447
|
+
let toolCalls;
|
|
446
448
|
let providerMetadata;
|
|
447
449
|
let warnings;
|
|
448
450
|
if (emit) {
|
|
@@ -481,6 +483,9 @@ async function runReasonEngine(args) {
|
|
|
481
483
|
if (chunk.finishReason !== void 0) {
|
|
482
484
|
finishReason = chunk.finishReason;
|
|
483
485
|
}
|
|
486
|
+
if (chunk.toolCalls !== void 0) {
|
|
487
|
+
toolCalls = chunk.toolCalls;
|
|
488
|
+
}
|
|
484
489
|
if (chunk.providerMetadata !== void 0) {
|
|
485
490
|
providerMetadata = chunk.providerMetadata;
|
|
486
491
|
}
|
|
@@ -520,6 +525,7 @@ async function runReasonEngine(args) {
|
|
|
520
525
|
...raw !== void 0 ? { raw } : {},
|
|
521
526
|
...usage !== void 0 ? { usage } : {},
|
|
522
527
|
...finishReason !== void 0 ? { finishReason } : {},
|
|
528
|
+
...toolCalls !== void 0 ? { toolCalls } : {},
|
|
523
529
|
...providerMetadata !== void 0 ? { providerMetadata } : {},
|
|
524
530
|
...warnings !== void 0 ? { warnings } : {}
|
|
525
531
|
};
|
|
@@ -546,6 +552,7 @@ async function runReasonEngine(args) {
|
|
|
546
552
|
...response.raw !== void 0 ? { raw: response.raw } : {},
|
|
547
553
|
...response.usage !== void 0 ? { usage: response.usage } : {},
|
|
548
554
|
...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
|
|
555
|
+
...response.toolCalls !== void 0 ? { toolCalls: response.toolCalls } : {},
|
|
549
556
|
...response.providerMetadata !== void 0 ? { providerMetadata: response.providerMetadata } : {},
|
|
550
557
|
...response.warnings !== void 0 ? { warnings: response.warnings } : {}
|
|
551
558
|
};
|
|
@@ -578,6 +585,8 @@ async function reasonEngine(args, meta, inputOverride) {
|
|
|
578
585
|
reasoning: args.reasoning,
|
|
579
586
|
responseFormat: args.responseFormat,
|
|
580
587
|
providerOptions: args.providerOptions,
|
|
588
|
+
tools: args.tools,
|
|
589
|
+
messages: args.messages,
|
|
581
590
|
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
582
591
|
stream: args.stream,
|
|
583
592
|
emit: args.emit,
|
|
@@ -903,6 +912,65 @@ var withStructuredStreamHints = (input, args) => {
|
|
|
903
912
|
].join("\n\n");
|
|
904
913
|
};
|
|
905
914
|
|
|
915
|
+
// src/reason/result.ts
|
|
916
|
+
var mergeUsage = (left, right) => {
|
|
917
|
+
if (!left) return right;
|
|
918
|
+
if (!right) return left;
|
|
919
|
+
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
920
|
+
const raw = left.raw || right.raw ? {
|
|
921
|
+
...left.raw ?? {},
|
|
922
|
+
...right.raw ?? {}
|
|
923
|
+
} : void 0;
|
|
924
|
+
const input = sum(left.input, right.input);
|
|
925
|
+
const output = sum(left.output, right.output);
|
|
926
|
+
const total = sum(left.total, right.total);
|
|
927
|
+
const reasoning = sum(left.reasoning, right.reasoning);
|
|
928
|
+
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
929
|
+
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
930
|
+
const merged = {};
|
|
931
|
+
if (input !== void 0) merged.input = input;
|
|
932
|
+
if (output !== void 0) merged.output = output;
|
|
933
|
+
if (total !== void 0) merged.total = total;
|
|
934
|
+
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
935
|
+
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
936
|
+
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
937
|
+
if (raw) merged.raw = raw;
|
|
938
|
+
return merged;
|
|
939
|
+
};
|
|
940
|
+
var mergeWarnings = (left, right) => {
|
|
941
|
+
if (!left?.length) return right;
|
|
942
|
+
if (!right?.length) return left;
|
|
943
|
+
const seen = /* @__PURE__ */ new Set();
|
|
944
|
+
const merged = [];
|
|
945
|
+
for (const warning of [...left, ...right]) {
|
|
946
|
+
const key = JSON.stringify(warning);
|
|
947
|
+
if (seen.has(key)) continue;
|
|
948
|
+
seen.add(key);
|
|
949
|
+
merged.push(warning);
|
|
950
|
+
}
|
|
951
|
+
return merged;
|
|
952
|
+
};
|
|
953
|
+
var mergeProviderMetadata = (left, right) => {
|
|
954
|
+
if (!left) return right;
|
|
955
|
+
if (!right) return left;
|
|
956
|
+
return {
|
|
957
|
+
...left,
|
|
958
|
+
...right
|
|
959
|
+
};
|
|
960
|
+
};
|
|
961
|
+
var emitReasonStructuredOutput = (args) => {
|
|
962
|
+
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
963
|
+
emitStructuredData({
|
|
964
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
965
|
+
data: args.output,
|
|
966
|
+
kind: "final",
|
|
967
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
968
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
969
|
+
...args.id ? { id: args.id } : {},
|
|
970
|
+
streamId: args.opId
|
|
971
|
+
});
|
|
972
|
+
};
|
|
973
|
+
|
|
906
974
|
// src/reason/structured-stream.ts
|
|
907
975
|
var resolveAppendFieldPaths = (cfg) => {
|
|
908
976
|
const fields = cfg?.fields;
|
|
@@ -1463,64 +1531,353 @@ var extractStreamingStringValues = (args) => {
|
|
|
1463
1531
|
});
|
|
1464
1532
|
};
|
|
1465
1533
|
|
|
1466
|
-
// src/reason/
|
|
1467
|
-
var
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
if (output !== void 0) merged.output = output;
|
|
1484
|
-
if (total !== void 0) merged.total = total;
|
|
1485
|
-
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1486
|
-
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1487
|
-
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
1488
|
-
if (raw) merged.raw = raw;
|
|
1489
|
-
return merged;
|
|
1534
|
+
// src/reason/tools.ts
|
|
1535
|
+
var toErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
1536
|
+
var toToolDefinitions = (tools) => tools.map((tool) => ({
|
|
1537
|
+
name: tool.name,
|
|
1538
|
+
...tool.title ? { title: tool.title } : {},
|
|
1539
|
+
...tool.description ? { description: tool.description } : {},
|
|
1540
|
+
inputSchema: tool.inputSchema,
|
|
1541
|
+
...tool.outputSchema !== void 0 ? { outputSchema: tool.outputSchema } : {},
|
|
1542
|
+
...tool.annotations !== void 0 ? { annotations: tool.annotations } : {},
|
|
1543
|
+
...tool.metadata !== void 0 ? { metadata: tool.metadata } : {}
|
|
1544
|
+
}));
|
|
1545
|
+
var resolveToolExecutionBoolean = (value, toolName, fallback) => {
|
|
1546
|
+
if (typeof value === "boolean") return value;
|
|
1547
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1548
|
+
return value[toolName] ?? fallback;
|
|
1549
|
+
}
|
|
1550
|
+
return fallback;
|
|
1490
1551
|
};
|
|
1491
|
-
var
|
|
1492
|
-
if (!
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
merged.push(warning);
|
|
1552
|
+
var normalizeToolResult = (toolCall, value) => {
|
|
1553
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value) {
|
|
1554
|
+
return value;
|
|
1555
|
+
}
|
|
1556
|
+
let content;
|
|
1557
|
+
try {
|
|
1558
|
+
content = typeof value === "string" ? value : JSON.stringify(value);
|
|
1559
|
+
} catch {
|
|
1560
|
+
content = String(value);
|
|
1501
1561
|
}
|
|
1502
|
-
return merged;
|
|
1503
|
-
};
|
|
1504
|
-
var mergeProviderMetadata = (left, right) => {
|
|
1505
|
-
if (!left) return right;
|
|
1506
|
-
if (!right) return left;
|
|
1507
1562
|
return {
|
|
1508
|
-
|
|
1509
|
-
|
|
1563
|
+
toolCallId: toolCall.id,
|
|
1564
|
+
name: toolCall.name,
|
|
1565
|
+
content,
|
|
1566
|
+
...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
|
|
1510
1567
|
};
|
|
1511
1568
|
};
|
|
1512
|
-
var
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
...
|
|
1520
|
-
|
|
1521
|
-
|
|
1569
|
+
var closeOwnedTools = async (tools) => {
|
|
1570
|
+
const closeFns = /* @__PURE__ */ new Set();
|
|
1571
|
+
for (const tool of tools) {
|
|
1572
|
+
if (tool.closeAfterUse === false || !tool.close) continue;
|
|
1573
|
+
closeFns.add(tool.close);
|
|
1574
|
+
}
|
|
1575
|
+
await Promise.all(
|
|
1576
|
+
[...closeFns].map(async (close) => {
|
|
1577
|
+
await close();
|
|
1578
|
+
})
|
|
1579
|
+
);
|
|
1580
|
+
};
|
|
1581
|
+
var createInitialMessages = (args) => [
|
|
1582
|
+
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
1583
|
+
{ role: "user", content: String(args.input ?? "") }
|
|
1584
|
+
];
|
|
1585
|
+
var runReasonToolLoop = async (args) => {
|
|
1586
|
+
const { useReasonArgs, id, opId, traceSpan } = args;
|
|
1587
|
+
const ctx = getHookContext();
|
|
1588
|
+
const tools = useReasonArgs.tools ?? [];
|
|
1589
|
+
const toolByName = /* @__PURE__ */ new Map();
|
|
1590
|
+
let validationCompleted = false;
|
|
1591
|
+
try {
|
|
1592
|
+
for (const tool of tools) {
|
|
1593
|
+
if (toolByName.has(tool.name)) {
|
|
1594
|
+
throw new Error(
|
|
1595
|
+
`useReason received duplicate tool name "${tool.name}".`
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
toolByName.set(tool.name, tool);
|
|
1599
|
+
}
|
|
1600
|
+
if (useReasonArgs.interrupt) {
|
|
1601
|
+
throw new Error(
|
|
1602
|
+
"useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
validationCompleted = true;
|
|
1606
|
+
} finally {
|
|
1607
|
+
if (!validationCompleted) {
|
|
1608
|
+
await closeOwnedTools(tools);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
const toolDefinitions = toToolDefinitions(tools);
|
|
1612
|
+
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
1613
|
+
const messages = createInitialMessages({
|
|
1614
|
+
system: useReasonArgs.system,
|
|
1615
|
+
input: useReasonArgs.input
|
|
1522
1616
|
});
|
|
1617
|
+
const steps = [];
|
|
1618
|
+
const allToolCalls = [];
|
|
1619
|
+
const allToolResults = [];
|
|
1620
|
+
let finalText = "";
|
|
1621
|
+
let finalRaw;
|
|
1622
|
+
let finalOutput;
|
|
1623
|
+
let aggregatedUsage;
|
|
1624
|
+
let finalFinishReason;
|
|
1625
|
+
let aggregatedProviderMetadata;
|
|
1626
|
+
let aggregatedWarnings;
|
|
1627
|
+
const emitToolEvent = (event, payload) => {
|
|
1628
|
+
ctx.node.emit(event, {
|
|
1629
|
+
...payload,
|
|
1630
|
+
node: ctx.node.graph.node,
|
|
1631
|
+
...id ? { id } : {},
|
|
1632
|
+
opId
|
|
1633
|
+
});
|
|
1634
|
+
};
|
|
1635
|
+
try {
|
|
1636
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
1637
|
+
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
1638
|
+
stepIndex,
|
|
1639
|
+
toolCount: toolDefinitions.length
|
|
1640
|
+
});
|
|
1641
|
+
const step = await reasonEngine(
|
|
1642
|
+
{
|
|
1643
|
+
...useReasonArgs,
|
|
1644
|
+
tools: toolDefinitions,
|
|
1645
|
+
messages,
|
|
1646
|
+
emit: false,
|
|
1647
|
+
stream: false
|
|
1648
|
+
},
|
|
1649
|
+
{ ...id ? { id } : {}, opId }
|
|
1650
|
+
);
|
|
1651
|
+
finalText = step.text;
|
|
1652
|
+
finalRaw = step.raw;
|
|
1653
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
|
|
1654
|
+
finalFinishReason = step.finishReason;
|
|
1655
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1656
|
+
aggregatedProviderMetadata,
|
|
1657
|
+
step.providerMetadata
|
|
1658
|
+
);
|
|
1659
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
1660
|
+
const toolCalls = step.toolCalls ?? [];
|
|
1661
|
+
const toolResults = [];
|
|
1662
|
+
steps.push({
|
|
1663
|
+
stepIndex,
|
|
1664
|
+
text: step.text,
|
|
1665
|
+
toolCalls,
|
|
1666
|
+
toolResults,
|
|
1667
|
+
...step.usage ? { usage: step.usage } : {},
|
|
1668
|
+
...step.finishReason ? { finishReason: step.finishReason } : {},
|
|
1669
|
+
...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
|
|
1670
|
+
...step.warnings ? { warnings: step.warnings } : {}
|
|
1671
|
+
});
|
|
1672
|
+
if (toolCalls.length === 0) {
|
|
1673
|
+
if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
|
|
1674
|
+
emitToolEvent("text-start", {});
|
|
1675
|
+
emitToolEvent("text-delta", { delta: step.text });
|
|
1676
|
+
emitToolEvent("text-end", {});
|
|
1677
|
+
}
|
|
1678
|
+
break;
|
|
1679
|
+
}
|
|
1680
|
+
allToolCalls.push(...toolCalls);
|
|
1681
|
+
messages.push({
|
|
1682
|
+
role: "assistant",
|
|
1683
|
+
content: step.text,
|
|
1684
|
+
toolCalls
|
|
1685
|
+
});
|
|
1686
|
+
for (const toolCall of toolCalls) {
|
|
1687
|
+
const tool = toolByName.get(toolCall.name);
|
|
1688
|
+
if (!tool) {
|
|
1689
|
+
throw new Error(
|
|
1690
|
+
`Model requested unknown tool "${toolCall.name}" from useReason.`
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
const shouldEmitTool = resolveToolExecutionBoolean(
|
|
1694
|
+
useReasonArgs.toolExecution?.emit,
|
|
1695
|
+
tool.name,
|
|
1696
|
+
false
|
|
1697
|
+
);
|
|
1698
|
+
const shouldApproveTool = resolveToolExecutionBoolean(
|
|
1699
|
+
useReasonArgs.toolExecution?.approval,
|
|
1700
|
+
tool.name,
|
|
1701
|
+
false
|
|
1702
|
+
);
|
|
1703
|
+
if (shouldEmitTool) {
|
|
1704
|
+
emitToolEvent("tool-call-start", {
|
|
1705
|
+
tool: tool.name,
|
|
1706
|
+
toolCallId: toolCall.id,
|
|
1707
|
+
input: toolCall.input
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
if (shouldApproveTool) {
|
|
1711
|
+
const approval = await awaitInterruptInternal({
|
|
1712
|
+
id: `tool:${toolCall.id}`,
|
|
1713
|
+
request: {
|
|
1714
|
+
kind: "choice",
|
|
1715
|
+
question: `Approve ${tool.name}?`,
|
|
1716
|
+
options: [
|
|
1717
|
+
{ id: "approve", label: "Approve" },
|
|
1718
|
+
{ id: "deny", label: "Deny" }
|
|
1719
|
+
],
|
|
1720
|
+
meta: {
|
|
1721
|
+
tool: tool.name,
|
|
1722
|
+
toolCallId: toolCall.id,
|
|
1723
|
+
input: toolCall.input
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
});
|
|
1727
|
+
const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
|
|
1728
|
+
if (!approved) {
|
|
1729
|
+
const denied = {
|
|
1730
|
+
toolCallId: toolCall.id,
|
|
1731
|
+
name: tool.name,
|
|
1732
|
+
content: "Tool call denied by user.",
|
|
1733
|
+
isError: true
|
|
1734
|
+
};
|
|
1735
|
+
toolResults.push(denied);
|
|
1736
|
+
allToolResults.push(denied);
|
|
1737
|
+
if (shouldEmitTool) {
|
|
1738
|
+
emitToolEvent("tool-call-result", {
|
|
1739
|
+
tool: tool.name,
|
|
1740
|
+
toolCallId: toolCall.id,
|
|
1741
|
+
content: denied.content,
|
|
1742
|
+
isError: true
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
continue;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
try {
|
|
1749
|
+
const rawResult = await tool.execute(toolCall.input, {
|
|
1750
|
+
toolCallId: toolCall.id,
|
|
1751
|
+
...useReasonArgs.abortSignal ? { abortSignal: useReasonArgs.abortSignal } : {}
|
|
1752
|
+
});
|
|
1753
|
+
const result2 = normalizeToolResult(toolCall, rawResult);
|
|
1754
|
+
toolResults.push(result2);
|
|
1755
|
+
allToolResults.push(result2);
|
|
1756
|
+
if (shouldEmitTool) {
|
|
1757
|
+
emitToolEvent("tool-call-result", {
|
|
1758
|
+
tool: result2.name,
|
|
1759
|
+
toolCallId: result2.toolCallId,
|
|
1760
|
+
content: result2.content,
|
|
1761
|
+
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1762
|
+
...result2.isError !== void 0 ? { isError: result2.isError } : {}
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
traceSpan?.addEvent?.("useReason.tool-call.complete", {
|
|
1766
|
+
stepIndex,
|
|
1767
|
+
tool: result2.name,
|
|
1768
|
+
toolCallId: result2.toolCallId,
|
|
1769
|
+
isError: Boolean(result2.isError)
|
|
1770
|
+
});
|
|
1771
|
+
} catch (error) {
|
|
1772
|
+
const result2 = {
|
|
1773
|
+
toolCallId: toolCall.id,
|
|
1774
|
+
name: tool.name,
|
|
1775
|
+
content: toErrorMessage(error),
|
|
1776
|
+
isError: true
|
|
1777
|
+
};
|
|
1778
|
+
toolResults.push(result2);
|
|
1779
|
+
allToolResults.push(result2);
|
|
1780
|
+
if (shouldEmitTool) {
|
|
1781
|
+
emitToolEvent("tool-call-error", {
|
|
1782
|
+
tool: tool.name,
|
|
1783
|
+
toolCallId: toolCall.id,
|
|
1784
|
+
message: result2.content
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
traceSpan?.addEvent?.("useReason.tool-call.error", {
|
|
1788
|
+
stepIndex,
|
|
1789
|
+
tool: tool.name,
|
|
1790
|
+
toolCallId: toolCall.id,
|
|
1791
|
+
message: result2.content
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
const currentStep = steps[steps.length - 1];
|
|
1796
|
+
if (currentStep) {
|
|
1797
|
+
steps[steps.length - 1] = {
|
|
1798
|
+
...currentStep,
|
|
1799
|
+
toolResults
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
for (const result2 of toolResults) {
|
|
1803
|
+
messages.push({
|
|
1804
|
+
role: "tool",
|
|
1805
|
+
content: result2.content,
|
|
1806
|
+
toolCallId: result2.toolCallId,
|
|
1807
|
+
name: result2.name,
|
|
1808
|
+
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1809
|
+
...result2.isError !== void 0 ? { isError: result2.isError } : {},
|
|
1810
|
+
...result2.raw !== void 0 ? { raw: result2.raw } : {}
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
|
|
1815
|
+
throw new Error(
|
|
1816
|
+
`useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
|
|
1817
|
+
);
|
|
1818
|
+
}
|
|
1819
|
+
if (useReasonArgs.outputSchema) {
|
|
1820
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
1821
|
+
text: finalText,
|
|
1822
|
+
schema: useReasonArgs.outputSchema,
|
|
1823
|
+
...finalFinishReason ? { finishReason: finalFinishReason } : {},
|
|
1824
|
+
label: "useReason output"
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
if (finalOutput !== void 0) {
|
|
1828
|
+
emitReasonStructuredOutput({
|
|
1829
|
+
...id ? { id } : {},
|
|
1830
|
+
opId,
|
|
1831
|
+
output: finalOutput,
|
|
1832
|
+
structured: useReasonArgs.structured,
|
|
1833
|
+
emit: useReasonArgs.emit ?? true
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
const result = {
|
|
1837
|
+
...id ? { id } : {},
|
|
1838
|
+
opId,
|
|
1839
|
+
text: finalText,
|
|
1840
|
+
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
1841
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
1842
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
1843
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
1844
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
1845
|
+
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
1846
|
+
toolCalls: allToolCalls,
|
|
1847
|
+
toolResults: allToolResults,
|
|
1848
|
+
steps
|
|
1849
|
+
};
|
|
1850
|
+
traceSpan?.end?.({
|
|
1851
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
1852
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
1853
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
1854
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
1855
|
+
attributes: {
|
|
1856
|
+
textLength: finalText.length,
|
|
1857
|
+
toolCallCount: allToolCalls.length,
|
|
1858
|
+
toolResultCount: allToolResults.length,
|
|
1859
|
+
toolStepCount: steps.length
|
|
1860
|
+
},
|
|
1861
|
+
telemetry: {
|
|
1862
|
+
...useReasonArgs.telemetry ?? {},
|
|
1863
|
+
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1866
|
+
return result;
|
|
1867
|
+
} catch (error) {
|
|
1868
|
+
traceSpan?.fail?.(error, {
|
|
1869
|
+
attributes: {
|
|
1870
|
+
toolStepCount: steps.length,
|
|
1871
|
+
toolCallCount: allToolCalls.length
|
|
1872
|
+
}
|
|
1873
|
+
});
|
|
1874
|
+
throw error;
|
|
1875
|
+
} finally {
|
|
1876
|
+
await closeOwnedTools(tools);
|
|
1877
|
+
}
|
|
1523
1878
|
};
|
|
1879
|
+
|
|
1880
|
+
// src/reason/use-reason.ts
|
|
1524
1881
|
var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
|
|
1525
1882
|
var resolveEffectiveReasoningIncludeThoughts = (args) => args.reasoning?.includeThoughts ?? args.model.options?.reasoning?.includeThoughts ?? false;
|
|
1526
1883
|
var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
|
|
@@ -1567,13 +1924,22 @@ async function useReason(args) {
|
|
|
1567
1924
|
...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
|
|
1568
1925
|
hasOutputSchema: Boolean(args.outputSchema),
|
|
1569
1926
|
hasInterrupt: Boolean(args.interrupt),
|
|
1570
|
-
hasStructured: Boolean(args.structured)
|
|
1927
|
+
hasStructured: Boolean(args.structured),
|
|
1928
|
+
hasTools: Boolean(args.tools?.length)
|
|
1571
1929
|
},
|
|
1572
1930
|
telemetry: {
|
|
1573
1931
|
...args.telemetry ?? {},
|
|
1574
1932
|
input: args.telemetry?.input ?? args.input
|
|
1575
1933
|
}
|
|
1576
1934
|
});
|
|
1935
|
+
if (args.tools?.length) {
|
|
1936
|
+
return runReasonToolLoop({
|
|
1937
|
+
useReasonArgs: args,
|
|
1938
|
+
...id ? { id } : {},
|
|
1939
|
+
opId,
|
|
1940
|
+
...traceSpan ? { traceSpan } : {}
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1577
1943
|
const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
|
|
1578
1944
|
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
1579
1945
|
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|