@kortyx/hooks 0.29.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +11 -0
- package/dist/index.d.ts +61 -4
- package/dist/index.js +486 -45
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
completeResponse: () => completeResponse,
|
|
25
25
|
createWorkflowHooks: () => createWorkflowHooks,
|
|
26
26
|
defineInterruptContract: () => defineInterruptContract,
|
|
27
|
+
defineOutputContract: () => defineOutputContract,
|
|
27
28
|
emitWorkflowCall: () => emitWorkflowCall,
|
|
28
29
|
parallel: () => parallel,
|
|
29
30
|
reportError: () => reportError,
|
|
@@ -441,6 +442,118 @@ var normalizeReasonInterrupts = (args) => {
|
|
|
441
442
|
};
|
|
442
443
|
var isLegacyInterruptContracts = (config) => Boolean(config && legacyConfigs.has(config));
|
|
443
444
|
|
|
445
|
+
// src/output-contract.ts
|
|
446
|
+
var import_zod2 = require("zod");
|
|
447
|
+
|
|
448
|
+
// src/structured-path.ts
|
|
449
|
+
var parseStructuredPath = (path, source) => {
|
|
450
|
+
if (path.length === 0) {
|
|
451
|
+
throw new Error(`${source} path must be a non-empty dot-separated string.`);
|
|
452
|
+
}
|
|
453
|
+
const parts = path.split(".");
|
|
454
|
+
if (parts.some((part) => part.length === 0)) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
`${source} path "${path}" must not contain empty segments.`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
return parts;
|
|
460
|
+
};
|
|
461
|
+
var assertStructuredPath = (path, source) => {
|
|
462
|
+
parseStructuredPath(path, source);
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// src/output-contract.ts
|
|
466
|
+
var EMIT_PREFIX = "kortyx_emit__";
|
|
467
|
+
var RETURN_PREFIX = "kortyx_return__";
|
|
468
|
+
var STREAM_EMIT_PREFIX = "kortyx_stream_emit__";
|
|
469
|
+
var STREAM_RETURN_PREFIX = "kortyx_stream_return__";
|
|
470
|
+
var legacyOutputWarningEmitted = false;
|
|
471
|
+
var warnLegacyReasonOutput = () => {
|
|
472
|
+
if (legacyOutputWarningEmitted) return;
|
|
473
|
+
legacyOutputWarningEmitted = true;
|
|
474
|
+
process.emitWarning(
|
|
475
|
+
"useReason({ outputSchema, structured }) and result.output are deprecated and will be removed in the next major release. Define reusable contracts with defineOutputContract() and pass them through useReason({ outputs }). See docs/internal/next-major-removals.md.",
|
|
476
|
+
{ code: "KORTYX_USE_REASON_OUTPUT_DEPRECATED", type: "DeprecationWarning" }
|
|
477
|
+
);
|
|
478
|
+
};
|
|
479
|
+
var defineOutputContract = (contract) => {
|
|
480
|
+
if (!contract || typeof contract !== "object")
|
|
481
|
+
throw new Error("defineOutputContract requires a contract object.");
|
|
482
|
+
for (const [field, value] of [
|
|
483
|
+
["description", contract.description],
|
|
484
|
+
["schemaId", contract.schemaId],
|
|
485
|
+
["schemaVersion", contract.schemaVersion]
|
|
486
|
+
])
|
|
487
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
488
|
+
throw new Error(`Output contract ${field} must be a non-empty string.`);
|
|
489
|
+
if (typeof contract.schema?.safeParse !== "function")
|
|
490
|
+
throw new Error("Output contract schema must provide safeParse(value).");
|
|
491
|
+
if (contract.stream) {
|
|
492
|
+
if (!contract.stream.fields || Object.keys(contract.stream.fields).length === 0)
|
|
493
|
+
throw new Error("Output contract stream.fields must not be empty.");
|
|
494
|
+
for (const [path, mode] of Object.entries(contract.stream.fields)) {
|
|
495
|
+
assertStructuredPath(path, "Output contract stream.fields");
|
|
496
|
+
if (!["set", "append", "text-delta"].includes(mode))
|
|
497
|
+
throw new Error(`Invalid output contract stream mode for "${path}".`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return contract;
|
|
501
|
+
};
|
|
502
|
+
var outputControlToolName = (kind, name) => `${kind === "emit" ? EMIT_PREFIX : kind === "return" ? RETURN_PREFIX : kind === "stream-emit" ? STREAM_EMIT_PREFIX : STREAM_RETURN_PREFIX}${name}`;
|
|
503
|
+
var isOutputControlToolName = (name) => name.startsWith(EMIT_PREFIX) || name.startsWith(RETURN_PREFIX) || name.startsWith(STREAM_EMIT_PREFIX) || name.startsWith(STREAM_RETURN_PREFIX);
|
|
504
|
+
var normalizeReasonOutputs = (config, hasLegacyOutput) => {
|
|
505
|
+
if (!config) return void 0;
|
|
506
|
+
if (hasLegacyOutput)
|
|
507
|
+
throw new Error(
|
|
508
|
+
"useReason cannot combine `outputs` with deprecated `outputSchema` or `structured`."
|
|
509
|
+
);
|
|
510
|
+
const entries = [
|
|
511
|
+
...Object.entries(config.emit ?? {}).map(([name, contract]) => ({
|
|
512
|
+
name,
|
|
513
|
+
contract,
|
|
514
|
+
kind: "emit"
|
|
515
|
+
})),
|
|
516
|
+
...Object.entries(config.return ?? {}).map(([name, contract]) => ({
|
|
517
|
+
name,
|
|
518
|
+
contract,
|
|
519
|
+
kind: "return"
|
|
520
|
+
}))
|
|
521
|
+
];
|
|
522
|
+
if (entries.length === 0)
|
|
523
|
+
throw new Error(
|
|
524
|
+
"useReason outputs must include at least one emit or return contract."
|
|
525
|
+
);
|
|
526
|
+
for (const { name, contract, kind } of entries) {
|
|
527
|
+
if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(name) || outputControlToolName(kind, name).length > 64)
|
|
528
|
+
throw new Error(`Invalid useReason output contract name "${name}".`);
|
|
529
|
+
defineOutputContract(contract);
|
|
530
|
+
if (contract.schema["~kortyx"]?.nativeOutput === true)
|
|
531
|
+
throw new Error(
|
|
532
|
+
`useReason output contract "${name}" uses a provider-native output schema that cannot be selected through tools. Continue using outputSchema for this provider until a non-tool contract path is available.`
|
|
533
|
+
);
|
|
534
|
+
if (contract.stream && outputControlToolName(
|
|
535
|
+
kind === "emit" ? "stream-emit" : "stream-return",
|
|
536
|
+
name
|
|
537
|
+
).length > 64)
|
|
538
|
+
throw new Error(
|
|
539
|
+
`Invalid useReason output contract name "${name}" for streaming tools.`
|
|
540
|
+
);
|
|
541
|
+
try {
|
|
542
|
+
const json = (0, import_zod2.toJSONSchema)(contract.schema);
|
|
543
|
+
if (json.type !== "object")
|
|
544
|
+
throw new Error("The tool input schema must have an object root.");
|
|
545
|
+
} catch (error) {
|
|
546
|
+
throw new Error(
|
|
547
|
+
`useReason output contract "${name}" schema must support JSON Schema conversion.`,
|
|
548
|
+
{ cause: error }
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (config.maxEmissions !== void 0 && (!Number.isInteger(config.maxEmissions) || config.maxEmissions < 1))
|
|
553
|
+
throw new Error("useReason outputs.maxEmissions must be at least 1.");
|
|
554
|
+
return config;
|
|
555
|
+
};
|
|
556
|
+
|
|
444
557
|
// src/reason/use-reason.ts
|
|
445
558
|
var import_core6 = require("@kortyx/core");
|
|
446
559
|
|
|
@@ -526,25 +639,6 @@ async function withSafeTraceSpan(adapter, args, fn) {
|
|
|
526
639
|
|
|
527
640
|
// src/structured.ts
|
|
528
641
|
var import_node_crypto = require("crypto");
|
|
529
|
-
|
|
530
|
-
// src/structured-path.ts
|
|
531
|
-
var parseStructuredPath = (path, source) => {
|
|
532
|
-
if (path.length === 0) {
|
|
533
|
-
throw new Error(`${source} path must be a non-empty dot-separated string.`);
|
|
534
|
-
}
|
|
535
|
-
const parts = path.split(".");
|
|
536
|
-
if (parts.some((part) => part.length === 0)) {
|
|
537
|
-
throw new Error(
|
|
538
|
-
`${source} path "${path}" must not contain empty segments.`
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
return parts;
|
|
542
|
-
};
|
|
543
|
-
var assertStructuredPath = (path, source) => {
|
|
544
|
-
parseStructuredPath(path, source);
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
// src/structured.ts
|
|
548
642
|
var createStructuredStreamId = () => {
|
|
549
643
|
try {
|
|
550
644
|
return (0, import_node_crypto.randomUUID)();
|
|
@@ -1356,12 +1450,12 @@ async function reasonEngine(args, meta, inputOverride) {
|
|
|
1356
1450
|
}
|
|
1357
1451
|
|
|
1358
1452
|
// src/reason/output-format.ts
|
|
1359
|
-
var
|
|
1453
|
+
var import_zod3 = require("zod");
|
|
1360
1454
|
function inferOutputFormat(schema, explicit) {
|
|
1361
1455
|
if (explicit) return { responseFormat: explicit };
|
|
1362
1456
|
if (!schema) return {};
|
|
1363
1457
|
try {
|
|
1364
|
-
const json = (0,
|
|
1458
|
+
const json = (0, import_zod3.toJSONSchema)(schema);
|
|
1365
1459
|
const clean = (value) => {
|
|
1366
1460
|
if (Array.isArray(value)) return value.map(clean);
|
|
1367
1461
|
if (!value || typeof value !== "object") return value;
|
|
@@ -2116,7 +2210,7 @@ var createTruncatedStructuredOutputError = (label, cause) => new import_errors5.
|
|
|
2116
2210
|
);
|
|
2117
2211
|
var createInvalidStructuredOutputError = (label, cause) => new import_errors5.ValidationError(
|
|
2118
2212
|
"INVALID_MODEL_JSON",
|
|
2119
|
-
`${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. Check outputSchema and provider structured-output settings.`,
|
|
2213
|
+
`${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. Check the output contract or outputSchema and provider structured-output settings.`,
|
|
2120
2214
|
cause
|
|
2121
2215
|
);
|
|
2122
2216
|
var parseReasonOutputWithSchema = (args) => {
|
|
@@ -2241,7 +2335,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
2241
2335
|
};
|
|
2242
2336
|
|
|
2243
2337
|
// src/reason/prompting.ts
|
|
2244
|
-
var
|
|
2338
|
+
var import_zod4 = require("zod");
|
|
2245
2339
|
var tryStringify2 = (value) => {
|
|
2246
2340
|
if (typeof value === "string") return value;
|
|
2247
2341
|
try {
|
|
@@ -2252,7 +2346,7 @@ var tryStringify2 = (value) => {
|
|
|
2252
2346
|
};
|
|
2253
2347
|
var tryGetSchemaHint = (schema) => {
|
|
2254
2348
|
try {
|
|
2255
|
-
const hint = (0,
|
|
2349
|
+
const hint = (0, import_zod4.toJSONSchema)(schema);
|
|
2256
2350
|
return JSON.stringify(hint, null, 2);
|
|
2257
2351
|
} catch {
|
|
2258
2352
|
return void 0;
|
|
@@ -2454,7 +2548,7 @@ var emitReasonStructuredOutput = (args) => {
|
|
|
2454
2548
|
// src/reason/tools.ts
|
|
2455
2549
|
var import_core5 = require("@kortyx/core");
|
|
2456
2550
|
var import_errors6 = require("@kortyx/core/errors");
|
|
2457
|
-
var
|
|
2551
|
+
var import_zod5 = require("zod");
|
|
2458
2552
|
var toToolDefinitions = (tools2) => tools2.map((tool) => ({
|
|
2459
2553
|
name: tool.name,
|
|
2460
2554
|
...tool.title ? { title: tool.title } : {},
|
|
@@ -2490,10 +2584,14 @@ var normalizeToolResult = (toolCall, value) => {
|
|
|
2490
2584
|
};
|
|
2491
2585
|
};
|
|
2492
2586
|
var createInitialMessages = (args) => [
|
|
2493
|
-
...typeof args.system === "string" || args.interruptInstructions ? [
|
|
2587
|
+
...typeof args.system === "string" || args.interruptInstructions || args.outputInstructions ? [
|
|
2494
2588
|
{
|
|
2495
2589
|
role: "system",
|
|
2496
|
-
content: [
|
|
2590
|
+
content: [
|
|
2591
|
+
args.system,
|
|
2592
|
+
args.interruptInstructions,
|
|
2593
|
+
args.outputInstructions
|
|
2594
|
+
].filter((value) => Boolean(value)).join("\n\n")
|
|
2497
2595
|
}
|
|
2498
2596
|
] : [],
|
|
2499
2597
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -2515,12 +2613,13 @@ var runReasonToolLoop = async (args) => {
|
|
|
2515
2613
|
interrupts: useReasonArgs.interrupts
|
|
2516
2614
|
});
|
|
2517
2615
|
const interruptByToolName = /* @__PURE__ */ new Map();
|
|
2616
|
+
const outputByToolName = /* @__PURE__ */ new Map();
|
|
2518
2617
|
const toolByName = /* @__PURE__ */ new Map();
|
|
2519
2618
|
let separateOutput = false;
|
|
2520
2619
|
for (const tool of tools2) {
|
|
2521
|
-
if (isInterruptControlToolName(tool.name))
|
|
2620
|
+
if (isInterruptControlToolName(tool.name) || isOutputControlToolName(tool.name))
|
|
2522
2621
|
throw new Error(
|
|
2523
|
-
`useReason tool name "${tool.name}" uses
|
|
2622
|
+
`useReason tool name "${tool.name}" uses a reserved control-tool namespace.`
|
|
2524
2623
|
);
|
|
2525
2624
|
if (toolByName.has(tool.name)) {
|
|
2526
2625
|
throw new Error(`useReason received duplicate tool name "${tool.name}".`);
|
|
@@ -2549,7 +2648,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2549
2648
|
);
|
|
2550
2649
|
let inputSchema;
|
|
2551
2650
|
try {
|
|
2552
|
-
inputSchema = (0,
|
|
2651
|
+
inputSchema = (0, import_zod5.toJSONSchema)(contract.requestSchema);
|
|
2553
2652
|
} catch (error) {
|
|
2554
2653
|
throw new Error(
|
|
2555
2654
|
`useReason interrupt contract "${name}" requestSchema must support JSON Schema conversion.`,
|
|
@@ -2573,9 +2672,66 @@ var runReasonToolLoop = async (args) => {
|
|
|
2573
2672
|
}
|
|
2574
2673
|
});
|
|
2575
2674
|
}
|
|
2675
|
+
const outputDefinitions = [];
|
|
2676
|
+
for (const kind of ["emit", "return"]) {
|
|
2677
|
+
for (const [name, contract] of Object.entries(
|
|
2678
|
+
useReasonArgs.outputs?.[kind] ?? {}
|
|
2679
|
+
)) {
|
|
2680
|
+
if (contract.stream) {
|
|
2681
|
+
const streamName = outputControlToolName(
|
|
2682
|
+
kind === "emit" ? "stream-emit" : "stream-return",
|
|
2683
|
+
name
|
|
2684
|
+
);
|
|
2685
|
+
outputByToolName.set(streamName, {
|
|
2686
|
+
name,
|
|
2687
|
+
kind,
|
|
2688
|
+
contract,
|
|
2689
|
+
stream: true
|
|
2690
|
+
});
|
|
2691
|
+
outputDefinitions.push({
|
|
2692
|
+
name: streamName,
|
|
2693
|
+
title: `Stream structured output: ${name}`,
|
|
2694
|
+
description: `${contract.description} Call this to start realtime generation of this structured value. The value is generated in a separate streamed model pass, validated, and ${kind === "emit" ? "returned as a tool result so you can continue" : "used to finish this operation"}.`,
|
|
2695
|
+
inputSchema: {
|
|
2696
|
+
type: "object",
|
|
2697
|
+
properties: {
|
|
2698
|
+
instruction: {
|
|
2699
|
+
type: "string",
|
|
2700
|
+
description: "What the streamed output should contain."
|
|
2701
|
+
}
|
|
2702
|
+
},
|
|
2703
|
+
required: ["instruction"],
|
|
2704
|
+
additionalProperties: false
|
|
2705
|
+
},
|
|
2706
|
+
metadata: {
|
|
2707
|
+
kortyxControl: kind === "emit" ? "output.stream.emit" : "output.stream.return",
|
|
2708
|
+
contract: name,
|
|
2709
|
+
schemaId: contract.schemaId,
|
|
2710
|
+
schemaVersion: contract.schemaVersion
|
|
2711
|
+
}
|
|
2712
|
+
});
|
|
2713
|
+
} else {
|
|
2714
|
+
const toolName = outputControlToolName(kind, name);
|
|
2715
|
+
outputByToolName.set(toolName, { name, kind, contract, stream: false });
|
|
2716
|
+
outputDefinitions.push({
|
|
2717
|
+
name: toolName,
|
|
2718
|
+
title: `${kind === "emit" ? "Emit" : "Return"} structured output: ${name}`,
|
|
2719
|
+
description: `${contract.description} ${kind === "emit" ? "Emit this structured value and continue reasoning." : "Return this structured value and finish reasoning."}`,
|
|
2720
|
+
inputSchema: (0, import_zod5.toJSONSchema)(contract.schema),
|
|
2721
|
+
metadata: {
|
|
2722
|
+
kortyxControl: kind === "emit" ? "output.emit" : "output.return",
|
|
2723
|
+
contract: name,
|
|
2724
|
+
schemaId: contract.schemaId,
|
|
2725
|
+
schemaVersion: contract.schemaVersion
|
|
2726
|
+
}
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2576
2731
|
const toolDefinitions = [
|
|
2577
2732
|
...toToolDefinitions(tools2),
|
|
2578
|
-
...interruptDefinitions
|
|
2733
|
+
...interruptDefinitions,
|
|
2734
|
+
...outputDefinitions
|
|
2579
2735
|
];
|
|
2580
2736
|
let finalizing = checkpoint?.finalizing ?? false;
|
|
2581
2737
|
let completed = false;
|
|
@@ -2590,11 +2746,27 @@ var runReasonToolLoop = async (args) => {
|
|
|
2590
2746
|
"- Call at most one human-input tool in a model turn, and do not combine it with other tool calls in that turn.",
|
|
2591
2747
|
interruptConfig.mode === "required" ? "- You must call at least one human-input tool before returning the final answer." : "- Human input is optional; call a human-input tool only when the request cannot be completed safely without it."
|
|
2592
2748
|
].join("\n")
|
|
2749
|
+
} : {},
|
|
2750
|
+
...useReasonArgs.outputs ? {
|
|
2751
|
+
outputInstructions: [
|
|
2752
|
+
"Structured output rules:",
|
|
2753
|
+
"- The kortyx_emit__* tools publish validated structured output and return an acknowledgement. You may continue with text or more tools afterward.",
|
|
2754
|
+
"- The kortyx_return__* tools publish a validated result and end this reasoning operation. If return tools are available, call exactly one before finishing.",
|
|
2755
|
+
"- For contracts with realtime streaming, use kortyx_stream_emit__* or kortyx_stream_return__*. These start a separate streamed JSON generation; its fields become visible as tokens arrive.",
|
|
2756
|
+
"- Call a structured output control tool alone in its model turn."
|
|
2757
|
+
].join("\n")
|
|
2593
2758
|
} : {}
|
|
2594
2759
|
});
|
|
2595
2760
|
const steps = checkpoint?.steps ?? [];
|
|
2596
|
-
const allToolCalls = steps.flatMap((step) => step.toolCalls).filter(
|
|
2597
|
-
|
|
2761
|
+
const allToolCalls = steps.flatMap((step) => step.toolCalls).filter(
|
|
2762
|
+
(call) => !interruptByToolName.has(call.name) && !outputByToolName.has(call.name)
|
|
2763
|
+
);
|
|
2764
|
+
const knownToolCallIds = new Set(
|
|
2765
|
+
steps.flatMap((step) => step.toolCalls.map((call) => call.id))
|
|
2766
|
+
);
|
|
2767
|
+
const allToolResults = steps.flatMap((step) => step.toolResults).filter(
|
|
2768
|
+
(result) => !interruptByToolName.has(result.name) && !outputByToolName.has(result.name)
|
|
2769
|
+
);
|
|
2598
2770
|
let finalText = "";
|
|
2599
2771
|
let finalRaw;
|
|
2600
2772
|
let finalOutput;
|
|
@@ -2609,8 +2781,11 @@ var runReasonToolLoop = async (args) => {
|
|
|
2609
2781
|
args.initialWarnings
|
|
2610
2782
|
);
|
|
2611
2783
|
let pending = checkpoint?.pending;
|
|
2784
|
+
let pendingStepIndex = checkpoint?.pendingStepIndex;
|
|
2612
2785
|
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
2613
2786
|
const interruptHistory = checkpoint?.interruptHistory ?? [];
|
|
2787
|
+
const emissions = checkpoint?.emissions ?? [];
|
|
2788
|
+
let returned = checkpoint?.returned;
|
|
2614
2789
|
const save = () => {
|
|
2615
2790
|
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
2616
2791
|
status: "tool_loop",
|
|
@@ -2619,8 +2794,11 @@ var runReasonToolLoop = async (args) => {
|
|
|
2619
2794
|
steps,
|
|
2620
2795
|
approvedCalls,
|
|
2621
2796
|
interruptHistory,
|
|
2797
|
+
emissions,
|
|
2798
|
+
...returned ? { returned } : {},
|
|
2622
2799
|
finalizing,
|
|
2623
|
-
...pending ? { pending } : {}
|
|
2800
|
+
...pending ? { pending } : {},
|
|
2801
|
+
...pending && pendingStepIndex !== void 0 ? { pendingStepIndex } : {}
|
|
2624
2802
|
};
|
|
2625
2803
|
ctx.stateDirty = true;
|
|
2626
2804
|
};
|
|
@@ -2633,7 +2811,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2633
2811
|
});
|
|
2634
2812
|
};
|
|
2635
2813
|
try {
|
|
2636
|
-
for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
2814
|
+
for (let stepIndex = pending ? pendingStepIndex ?? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
2637
2815
|
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2638
2816
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
2639
2817
|
stepIndex,
|
|
@@ -2643,6 +2821,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2643
2821
|
const stream = useReasonArgs.stream ?? useReasonArgs.model.options?.streaming ?? true;
|
|
2644
2822
|
const emit = useReasonArgs.emit ?? true;
|
|
2645
2823
|
let textStarted = false;
|
|
2824
|
+
const segmentId = `step-${stepIndex}`;
|
|
2646
2825
|
const structuredChunk = useReasonArgs.outputSchema && (!separateOutput || finalizing) && stream && emit && shouldStreamStructured(useReasonArgs.structured) ? createStructuredOutputStreamer(useReasonArgs, id, opId) : void 0;
|
|
2647
2826
|
const step = pending ?? await reasonEngine(
|
|
2648
2827
|
{
|
|
@@ -2656,15 +2835,15 @@ var runReasonToolLoop = async (args) => {
|
|
|
2656
2835
|
if (structuredChunk) structuredChunk(delta);
|
|
2657
2836
|
if (!emit || useReasonArgs.outputSchema || !delta) return;
|
|
2658
2837
|
if (!textStarted) {
|
|
2659
|
-
emitToolEvent("text-start", {});
|
|
2838
|
+
emitToolEvent("text-start", { segmentId });
|
|
2660
2839
|
textStarted = true;
|
|
2661
2840
|
}
|
|
2662
|
-
emitToolEvent("text-delta", { delta });
|
|
2841
|
+
emitToolEvent("text-delta", { delta, segmentId });
|
|
2663
2842
|
}
|
|
2664
2843
|
},
|
|
2665
2844
|
{ ...id ? { id } : {}, opId }
|
|
2666
2845
|
);
|
|
2667
|
-
if (textStarted) emitToolEvent("text-end", {});
|
|
2846
|
+
if (textStarted) emitToolEvent("text-end", { segmentId });
|
|
2668
2847
|
finalText = step.text;
|
|
2669
2848
|
finalRaw = step.raw;
|
|
2670
2849
|
if (!reused) {
|
|
@@ -2680,7 +2859,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2680
2859
|
const toolCalls = step.toolCalls ?? [];
|
|
2681
2860
|
const ids = /* @__PURE__ */ new Set();
|
|
2682
2861
|
for (const call of toolCalls) {
|
|
2683
|
-
if (!call.id || ids.has(call.id) || !reused &&
|
|
2862
|
+
if (!call.id || ids.has(call.id) || !reused && knownToolCallIds.has(call.id))
|
|
2684
2863
|
throw new Error("Model returned duplicate or empty tool call IDs.");
|
|
2685
2864
|
ids.add(call.id);
|
|
2686
2865
|
}
|
|
@@ -2696,7 +2875,12 @@ var runReasonToolLoop = async (args) => {
|
|
|
2696
2875
|
...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
|
|
2697
2876
|
...step.warnings ? { warnings: step.warnings } : {}
|
|
2698
2877
|
});
|
|
2878
|
+
if (!reused) for (const call of toolCalls) knownToolCallIds.add(call.id);
|
|
2699
2879
|
if (toolCalls.length === 0) {
|
|
2880
|
+
if (Object.keys(useReasonArgs.outputs?.return ?? {}).length > 0 && !returned)
|
|
2881
|
+
throw new Error(
|
|
2882
|
+
"useReason outputs.return requires one terminal output contract call."
|
|
2883
|
+
);
|
|
2700
2884
|
if (interruptConfig?.mode === "required" && interruptHistory.length === 0)
|
|
2701
2885
|
throw new Error(
|
|
2702
2886
|
"useReason interrupts.mode is required, but the model returned a final answer without requesting human input."
|
|
@@ -2734,6 +2918,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2734
2918
|
});
|
|
2735
2919
|
finalizing = true;
|
|
2736
2920
|
pending = void 0;
|
|
2921
|
+
pendingStepIndex = void 0;
|
|
2737
2922
|
save();
|
|
2738
2923
|
continue;
|
|
2739
2924
|
}
|
|
@@ -2746,7 +2931,9 @@ var runReasonToolLoop = async (args) => {
|
|
|
2746
2931
|
);
|
|
2747
2932
|
if (!reused) {
|
|
2748
2933
|
allToolCalls.push(
|
|
2749
|
-
...toolCalls.filter(
|
|
2934
|
+
...toolCalls.filter(
|
|
2935
|
+
(call) => !interruptByToolName.has(call.name) && !outputByToolName.has(call.name)
|
|
2936
|
+
)
|
|
2750
2937
|
);
|
|
2751
2938
|
messages.push({
|
|
2752
2939
|
role: "assistant",
|
|
@@ -2756,7 +2943,197 @@ var runReasonToolLoop = async (args) => {
|
|
|
2756
2943
|
});
|
|
2757
2944
|
}
|
|
2758
2945
|
pending = step;
|
|
2946
|
+
pendingStepIndex = stepIndex;
|
|
2759
2947
|
save();
|
|
2948
|
+
const outputCalls = toolCalls.filter(
|
|
2949
|
+
(call) => outputByToolName.has(call.name)
|
|
2950
|
+
);
|
|
2951
|
+
if (outputCalls.length > 0) {
|
|
2952
|
+
if (toolCalls.length !== 1)
|
|
2953
|
+
throw new Error(
|
|
2954
|
+
"A useReason output contract call must be the only tool call in its model turn."
|
|
2955
|
+
);
|
|
2956
|
+
const call = outputCalls[0];
|
|
2957
|
+
if (!call) throw new Error("Missing useReason output control call.");
|
|
2958
|
+
const resolved = outputByToolName.get(call.name);
|
|
2959
|
+
if (!resolved)
|
|
2960
|
+
throw new Error(
|
|
2961
|
+
`Unknown useReason output contract tool "${call.name}".`
|
|
2962
|
+
);
|
|
2963
|
+
if (toolResults.some((result3) => result3.toolCallId === call.id)) {
|
|
2964
|
+
pending = void 0;
|
|
2965
|
+
pendingStepIndex = void 0;
|
|
2966
|
+
save();
|
|
2967
|
+
continue;
|
|
2968
|
+
}
|
|
2969
|
+
if (resolved.kind === "emit" && emissions.length >= (useReasonArgs.outputs?.maxEmissions ?? 16))
|
|
2970
|
+
throw new Error(
|
|
2971
|
+
`useReason reached outputs.maxEmissions (${useReasonArgs.outputs?.maxEmissions ?? 16}).`
|
|
2972
|
+
);
|
|
2973
|
+
let data;
|
|
2974
|
+
if (resolved.stream) {
|
|
2975
|
+
if (!stream)
|
|
2976
|
+
throw new Error(
|
|
2977
|
+
`useReason streaming output contract "${resolved.name}" requires stream: true.`
|
|
2978
|
+
);
|
|
2979
|
+
if (stepIndex + 1 >= maxSteps)
|
|
2980
|
+
throw new Error(
|
|
2981
|
+
`useReason output streaming requires another model pass within maxSteps (${maxSteps}).`
|
|
2982
|
+
);
|
|
2983
|
+
const instruction = call.input && typeof call.input === "object" && !Array.isArray(call.input) ? call.input.instruction : void 0;
|
|
2984
|
+
if (typeof instruction !== "string" || instruction.trim().length === 0)
|
|
2985
|
+
throw new Error(
|
|
2986
|
+
`useReason streaming output contract "${resolved.name}" requires an instruction.`
|
|
2987
|
+
);
|
|
2988
|
+
const streamId = `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissions.length : "return"}`;
|
|
2989
|
+
const structured = {
|
|
2990
|
+
dataType: resolved.contract.schemaId,
|
|
2991
|
+
schemaId: resolved.contract.schemaId,
|
|
2992
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
2993
|
+
fields: resolved.contract.stream?.fields
|
|
2994
|
+
};
|
|
2995
|
+
const inferred = inferOutputFormat(
|
|
2996
|
+
resolved.contract.schema,
|
|
2997
|
+
void 0
|
|
2998
|
+
);
|
|
2999
|
+
try {
|
|
3000
|
+
const outputStep = await reasonEngine(
|
|
3001
|
+
{
|
|
3002
|
+
...useReasonArgs,
|
|
3003
|
+
tools: [],
|
|
3004
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
3005
|
+
messages: [
|
|
3006
|
+
...messages,
|
|
3007
|
+
{
|
|
3008
|
+
role: "tool",
|
|
3009
|
+
toolCallId: call.id,
|
|
3010
|
+
name: call.name,
|
|
3011
|
+
content: "Generating the requested structured output."
|
|
3012
|
+
},
|
|
3013
|
+
{
|
|
3014
|
+
role: "user",
|
|
3015
|
+
content: withOutputGuardrails(
|
|
3016
|
+
instruction,
|
|
3017
|
+
resolved.contract.schema
|
|
3018
|
+
)
|
|
3019
|
+
}
|
|
3020
|
+
],
|
|
3021
|
+
emit: false,
|
|
3022
|
+
stream,
|
|
3023
|
+
...emit && stream ? {
|
|
3024
|
+
onTextChunk: createStructuredOutputStreamer(
|
|
3025
|
+
{ structured },
|
|
3026
|
+
id,
|
|
3027
|
+
streamId
|
|
3028
|
+
)
|
|
3029
|
+
} : {}
|
|
3030
|
+
},
|
|
3031
|
+
{ ...id ? { id } : {}, opId }
|
|
3032
|
+
);
|
|
3033
|
+
if (outputStep.toolCalls?.length)
|
|
3034
|
+
throw new Error(
|
|
3035
|
+
`Provider requested a tool during streamed output contract "${resolved.name}".`
|
|
3036
|
+
);
|
|
3037
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, outputStep.usage);
|
|
3038
|
+
accumulateTokenUsage(outputStep.usage);
|
|
3039
|
+
aggregatedWarnings = mergeWarnings(
|
|
3040
|
+
aggregatedWarnings,
|
|
3041
|
+
mergeWarnings(inferred.warnings, outputStep.warnings)
|
|
3042
|
+
);
|
|
3043
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
3044
|
+
aggregatedProviderMetadata,
|
|
3045
|
+
outputStep.providerMetadata
|
|
3046
|
+
);
|
|
3047
|
+
finalFinishReason = outputStep.finishReason;
|
|
3048
|
+
finalRaw = outputStep.raw;
|
|
3049
|
+
data = parseReasonOutputWithSchema({
|
|
3050
|
+
text: outputStep.text,
|
|
3051
|
+
schema: resolved.contract.schema,
|
|
3052
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3053
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
3054
|
+
label: `useReason output contract "${resolved.name}"`
|
|
3055
|
+
});
|
|
3056
|
+
steps.push({
|
|
3057
|
+
stepIndex: stepIndex + 1,
|
|
3058
|
+
kind: "output",
|
|
3059
|
+
text: outputStep.text,
|
|
3060
|
+
toolCalls: [],
|
|
3061
|
+
toolResults: [],
|
|
3062
|
+
...outputStep.usage ? { usage: outputStep.usage } : {},
|
|
3063
|
+
...outputStep.finishReason ? { finishReason: outputStep.finishReason } : {},
|
|
3064
|
+
...outputStep.providerMetadata ? { providerMetadata: outputStep.providerMetadata } : {},
|
|
3065
|
+
...outputStep.warnings ? { warnings: outputStep.warnings } : {}
|
|
3066
|
+
});
|
|
3067
|
+
stepIndex += 1;
|
|
3068
|
+
} catch (error) {
|
|
3069
|
+
traceSpan?.addEvent?.("useReason.output.invalidated", {
|
|
3070
|
+
contract: resolved.name,
|
|
3071
|
+
kind: resolved.kind,
|
|
3072
|
+
schemaId: resolved.contract.schemaId,
|
|
3073
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3074
|
+
streamId
|
|
3075
|
+
});
|
|
3076
|
+
if (emit) {
|
|
3077
|
+
try {
|
|
3078
|
+
ctx.node.emit("structured_data_invalidated", {
|
|
3079
|
+
node: ctx.node.graph.node,
|
|
3080
|
+
streamId
|
|
3081
|
+
});
|
|
3082
|
+
} catch {
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
throw error;
|
|
3086
|
+
}
|
|
3087
|
+
} else {
|
|
3088
|
+
data = parseWithSchema(
|
|
3089
|
+
resolved.contract.schema,
|
|
3090
|
+
call.input,
|
|
3091
|
+
`useReason output contract "${resolved.name}"`
|
|
3092
|
+
);
|
|
3093
|
+
}
|
|
3094
|
+
const entry = { contract: resolved.name, data };
|
|
3095
|
+
const emissionIndex = emissions.length;
|
|
3096
|
+
if (resolved.kind === "emit")
|
|
3097
|
+
emissions.push(entry);
|
|
3098
|
+
else returned = entry;
|
|
3099
|
+
if (emit)
|
|
3100
|
+
emitStructuredData({
|
|
3101
|
+
kind: "final",
|
|
3102
|
+
data,
|
|
3103
|
+
dataType: resolved.contract.schemaId,
|
|
3104
|
+
schemaId: resolved.contract.schemaId,
|
|
3105
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3106
|
+
streamId: `${opId}:${resolved.kind}:${resolved.name}:${resolved.kind === "emit" ? emissionIndex : "return"}`,
|
|
3107
|
+
...id ? { id } : {}
|
|
3108
|
+
});
|
|
3109
|
+
traceSpan?.addEvent?.("useReason.output.emitted", {
|
|
3110
|
+
contract: resolved.name,
|
|
3111
|
+
kind: resolved.kind,
|
|
3112
|
+
schemaId: resolved.contract.schemaId,
|
|
3113
|
+
schemaVersion: resolved.contract.schemaVersion,
|
|
3114
|
+
emissionIndex,
|
|
3115
|
+
data
|
|
3116
|
+
});
|
|
3117
|
+
if (resolved.kind === "return") {
|
|
3118
|
+
pending = void 0;
|
|
3119
|
+
pendingStepIndex = void 0;
|
|
3120
|
+
completed = true;
|
|
3121
|
+
save();
|
|
3122
|
+
break;
|
|
3123
|
+
}
|
|
3124
|
+
const result2 = normalizeToolResult(call, { emitted: true, data });
|
|
3125
|
+
toolResults.push(result2);
|
|
3126
|
+
messages.push({
|
|
3127
|
+
role: "tool",
|
|
3128
|
+
content: result2.content,
|
|
3129
|
+
toolCallId: result2.toolCallId,
|
|
3130
|
+
name: result2.name
|
|
3131
|
+
});
|
|
3132
|
+
pending = void 0;
|
|
3133
|
+
pendingStepIndex = void 0;
|
|
3134
|
+
save();
|
|
3135
|
+
continue;
|
|
3136
|
+
}
|
|
2760
3137
|
const interruptCalls = toolCalls.filter(
|
|
2761
3138
|
(call) => interruptByToolName.has(call.name)
|
|
2762
3139
|
);
|
|
@@ -2779,6 +3156,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2779
3156
|
);
|
|
2780
3157
|
if (toolResults.some((result3) => result3.toolCallId === interruptCall.id)) {
|
|
2781
3158
|
pending = void 0;
|
|
3159
|
+
pendingStepIndex = void 0;
|
|
2782
3160
|
save();
|
|
2783
3161
|
continue;
|
|
2784
3162
|
}
|
|
@@ -2832,6 +3210,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2832
3210
|
interruptIndex: interruptHistory.length - 1
|
|
2833
3211
|
});
|
|
2834
3212
|
pending = void 0;
|
|
3213
|
+
pendingStepIndex = void 0;
|
|
2835
3214
|
save();
|
|
2836
3215
|
continue;
|
|
2837
3216
|
}
|
|
@@ -3007,6 +3386,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3007
3386
|
save();
|
|
3008
3387
|
}
|
|
3009
3388
|
pending = void 0;
|
|
3389
|
+
pendingStepIndex = void 0;
|
|
3010
3390
|
save();
|
|
3011
3391
|
}
|
|
3012
3392
|
if (!completed) {
|
|
@@ -3035,7 +3415,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
3035
3415
|
const result = {
|
|
3036
3416
|
...id ? { id } : {},
|
|
3037
3417
|
opId,
|
|
3038
|
-
text: finalText,
|
|
3418
|
+
text: useReasonArgs.outputs ? steps.filter((step) => step.kind !== "output").map((step) => step.text).filter(Boolean).join("\n") : finalText,
|
|
3039
3419
|
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
3040
3420
|
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
3041
3421
|
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
@@ -3045,6 +3425,8 @@ var runReasonToolLoop = async (args) => {
|
|
|
3045
3425
|
toolCalls: allToolCalls,
|
|
3046
3426
|
toolResults: allToolResults,
|
|
3047
3427
|
steps,
|
|
3428
|
+
...emissions.length ? { emissions } : {},
|
|
3429
|
+
...returned ? { returned } : {},
|
|
3048
3430
|
...interruptHistory.length > 0 ? { interruptHistory } : {},
|
|
3049
3431
|
...isLegacyInterruptContracts(interruptConfig) && interruptHistory.length > 0 ? {
|
|
3050
3432
|
interruptResponse: interruptHistory.at(-1)?.response
|
|
@@ -3060,11 +3442,13 @@ var runReasonToolLoop = async (args) => {
|
|
|
3060
3442
|
toolCallCount: allToolCalls.length,
|
|
3061
3443
|
toolResultCount: allToolResults.length,
|
|
3062
3444
|
toolStepCount: steps.length,
|
|
3063
|
-
interruptCount: interruptHistory.length
|
|
3445
|
+
interruptCount: interruptHistory.length,
|
|
3446
|
+
emissionCount: emissions.length,
|
|
3447
|
+
...returned ? { returnContract: returned.contract } : {}
|
|
3064
3448
|
},
|
|
3065
3449
|
telemetry: {
|
|
3066
3450
|
...useReasonArgs.telemetry ?? {},
|
|
3067
|
-
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
3451
|
+
output: useReasonArgs.telemetry?.output ?? returned?.data ?? finalOutput ?? finalText
|
|
3068
3452
|
}
|
|
3069
3453
|
});
|
|
3070
3454
|
ctx.currentNodeState.byKey[checkpointKey] = { status: "completed", result };
|
|
@@ -3091,7 +3475,7 @@ var resolveEffectiveReasoningIncludeThoughts = (args) => args.reasoning?.include
|
|
|
3091
3475
|
var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
|
|
3092
3476
|
var assertReasoningThoughtsCompatibility = (args) => {
|
|
3093
3477
|
if (!resolveEffectiveReasoningIncludeThoughts(args)) return;
|
|
3094
|
-
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.interrupts) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
|
|
3478
|
+
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.interrupts) || Boolean(args.structured) || Boolean(args.outputs) || resolveEffectiveResponseFormatType(args) === "json";
|
|
3095
3479
|
if (!usesStructuredOutput) return;
|
|
3096
3480
|
throw new Error(
|
|
3097
3481
|
"useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
|
|
@@ -3099,6 +3483,15 @@ var assertReasoningThoughtsCompatibility = (args) => {
|
|
|
3099
3483
|
};
|
|
3100
3484
|
async function runReason(args) {
|
|
3101
3485
|
assertReasoningThoughtsCompatibility(args);
|
|
3486
|
+
normalizeReasonOutputs(
|
|
3487
|
+
args.outputs,
|
|
3488
|
+
Boolean(args.outputSchema || args.structured)
|
|
3489
|
+
);
|
|
3490
|
+
if (args.outputSchema || args.structured) warnLegacyReasonOutput();
|
|
3491
|
+
if (args.outputs && resolveEffectiveResponseFormatType(args) === "json")
|
|
3492
|
+
throw new Error(
|
|
3493
|
+
"useReason output contracts use tool calls and cannot be combined with JSON responseFormat."
|
|
3494
|
+
);
|
|
3102
3495
|
const interruptConfig = normalizeReasonInterrupts({
|
|
3103
3496
|
interrupt: args.interrupt,
|
|
3104
3497
|
interrupts: args.interrupts
|
|
@@ -3172,7 +3565,7 @@ async function runReason(args) {
|
|
|
3172
3565
|
}
|
|
3173
3566
|
},
|
|
3174
3567
|
async (traceSpan) => {
|
|
3175
|
-
if ((args.tools?.length || args.interrupts) && !existingCheckpoint) {
|
|
3568
|
+
if ((args.tools?.length || args.interrupts || args.outputs) && !existingCheckpoint) {
|
|
3176
3569
|
return runReasonToolLoop({
|
|
3177
3570
|
useReasonArgs: args,
|
|
3178
3571
|
allowValidatedToolOutput,
|
|
@@ -3435,6 +3828,53 @@ async function useReason(args) {
|
|
|
3435
3828
|
|
|
3436
3829
|
// src/hooks.ts
|
|
3437
3830
|
function useStructuredData(args) {
|
|
3831
|
+
if ("contract" in args) {
|
|
3832
|
+
const contract = defineOutputContract(args.contract);
|
|
3833
|
+
const base = {
|
|
3834
|
+
dataType: contract.schemaId,
|
|
3835
|
+
schemaId: contract.schemaId,
|
|
3836
|
+
schemaVersion: contract.schemaVersion,
|
|
3837
|
+
...args.id ? { id: args.id } : {},
|
|
3838
|
+
...args.streamId ? { streamId: args.streamId } : {}
|
|
3839
|
+
};
|
|
3840
|
+
if (args.kind === "set" || args.kind === "append" || args.kind === "text-delta") {
|
|
3841
|
+
if (contract.stream?.fields[args.path] !== args.kind)
|
|
3842
|
+
throw new Error(
|
|
3843
|
+
`Output contract does not allow ${args.kind} at "${args.path}".`
|
|
3844
|
+
);
|
|
3845
|
+
if (args.kind === "set")
|
|
3846
|
+
emitStructuredData({
|
|
3847
|
+
...base,
|
|
3848
|
+
kind: "set",
|
|
3849
|
+
path: args.path,
|
|
3850
|
+
value: args.value,
|
|
3851
|
+
...args.valueSchema ? { valueSchema: args.valueSchema } : {}
|
|
3852
|
+
});
|
|
3853
|
+
else if (args.kind === "append")
|
|
3854
|
+
emitStructuredData({
|
|
3855
|
+
...base,
|
|
3856
|
+
kind: "append",
|
|
3857
|
+
path: args.path,
|
|
3858
|
+
items: args.items,
|
|
3859
|
+
...args.itemSchema ? { itemSchema: args.itemSchema } : {}
|
|
3860
|
+
});
|
|
3861
|
+
else
|
|
3862
|
+
emitStructuredData({
|
|
3863
|
+
...base,
|
|
3864
|
+
kind: "text-delta",
|
|
3865
|
+
path: args.path,
|
|
3866
|
+
delta: args.delta
|
|
3867
|
+
});
|
|
3868
|
+
return;
|
|
3869
|
+
}
|
|
3870
|
+
emitStructuredData({
|
|
3871
|
+
...base,
|
|
3872
|
+
kind: "final",
|
|
3873
|
+
data: args.data,
|
|
3874
|
+
dataSchema: contract.schema
|
|
3875
|
+
});
|
|
3876
|
+
return;
|
|
3877
|
+
}
|
|
3438
3878
|
emitStructuredData(args);
|
|
3439
3879
|
}
|
|
3440
3880
|
function useReason2(args) {
|
|
@@ -3958,6 +4398,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3958
4398
|
completeResponse,
|
|
3959
4399
|
createWorkflowHooks,
|
|
3960
4400
|
defineInterruptContract,
|
|
4401
|
+
defineOutputContract,
|
|
3961
4402
|
emitWorkflowCall,
|
|
3962
4403
|
parallel,
|
|
3963
4404
|
reportError,
|