@kortyx/hooks 0.25.3 → 0.27.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 +34 -0
- package/dist/index.d.ts +18 -4
- package/dist/index.js +943 -461
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +20 -3
- package/dist/internal.js +179 -5
- package/dist/internal.js.map +1 -1
- package/dist/{tracing-BI6YKHSf.d.ts → tracing-BsxYlgEw.d.ts} +16 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
createWorkflowHooks: () => createWorkflowHooks,
|
|
26
26
|
emitWorkflowCall: () => emitWorkflowCall,
|
|
27
27
|
parallel: () => parallel,
|
|
28
|
+
reportError: () => reportError,
|
|
28
29
|
runWithHookContext: () => runWithHookContext,
|
|
29
30
|
useAbortSignal: () => useAbortSignal,
|
|
30
31
|
useInterrupt: () => useInterrupt,
|
|
@@ -32,6 +33,7 @@ __export(index_exports, {
|
|
|
32
33
|
useReason: () => useReason2,
|
|
33
34
|
useRuntimeContext: () => useRuntimeContext,
|
|
34
35
|
useStructuredData: () => useStructuredData,
|
|
36
|
+
useTool: () => useTool,
|
|
35
37
|
useWorkflow: () => useWorkflow,
|
|
36
38
|
useWorkflowState: () => useWorkflowState,
|
|
37
39
|
workflowCallFingerprint: () => workflowCallFingerprint
|
|
@@ -99,6 +101,7 @@ var createInternalContext = (ctx) => {
|
|
|
99
101
|
workflowContextClosed: false,
|
|
100
102
|
nodeStateIndex: 0,
|
|
101
103
|
reasonCallIndex: 0,
|
|
104
|
+
toolCallIndex: 0,
|
|
102
105
|
currentNodeState,
|
|
103
106
|
workflowState: cloneWorkflowState(hookRuntimeState.workflowState),
|
|
104
107
|
tokenUsage: cloneTokenUsage(ctx.state.runtime?.tokenUsage),
|
|
@@ -189,6 +192,12 @@ async function runWithHookContext(ctx, fn) {
|
|
|
189
192
|
}
|
|
190
193
|
});
|
|
191
194
|
cleanupCompletedReasonCheckpoints(internal);
|
|
195
|
+
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
196
|
+
if (key.startsWith("__useTool:")) {
|
|
197
|
+
delete internal.currentNodeState.byKey[key];
|
|
198
|
+
internal.stateDirty = true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
192
201
|
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
193
202
|
if (key.startsWith("__useWorkflow:") || key.startsWith("__parallel:") || key === "__useWorkflowActivation") {
|
|
194
203
|
internal.currentNodeState.byKey[key] = null;
|
|
@@ -341,7 +350,87 @@ var awaitInterruptInternal = (args) => {
|
|
|
341
350
|
};
|
|
342
351
|
|
|
343
352
|
// src/reason/use-reason.ts
|
|
344
|
-
var
|
|
353
|
+
var import_core6 = require("@kortyx/core");
|
|
354
|
+
|
|
355
|
+
// src/safe-tracing.ts
|
|
356
|
+
function safeSpan(span) {
|
|
357
|
+
const guard = (fn) => (...args) => {
|
|
358
|
+
try {
|
|
359
|
+
fn?.(...args);
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
let ended = false;
|
|
364
|
+
const finish = (fn) => (...args) => {
|
|
365
|
+
if (ended) return;
|
|
366
|
+
ended = true;
|
|
367
|
+
guard(fn)(...args);
|
|
368
|
+
};
|
|
369
|
+
return {
|
|
370
|
+
addEvent: guard(
|
|
371
|
+
(name, attributes) => span?.addEvent?.(name, attributes)
|
|
372
|
+
),
|
|
373
|
+
setAttributes: guard(
|
|
374
|
+
(attributes) => span?.setAttributes?.(attributes)
|
|
375
|
+
),
|
|
376
|
+
end: finish((args) => span?.end?.(args)),
|
|
377
|
+
fail: finish((error, args) => {
|
|
378
|
+
if (span?.fail) span.fail(error, args);
|
|
379
|
+
else span?.end?.(args);
|
|
380
|
+
})
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function safeStartSpan(adapter, args) {
|
|
384
|
+
try {
|
|
385
|
+
return safeSpan(adapter?.startSpan?.(args));
|
|
386
|
+
} catch {
|
|
387
|
+
return safeSpan(void 0);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async function withSafeTraceSpan(adapter, args, fn) {
|
|
391
|
+
let execution;
|
|
392
|
+
const invoke = (span) => {
|
|
393
|
+
execution ??= Promise.resolve().then(async () => {
|
|
394
|
+
const observed = safeSpan(span);
|
|
395
|
+
try {
|
|
396
|
+
const result = await fn(observed);
|
|
397
|
+
observed.end?.();
|
|
398
|
+
return result;
|
|
399
|
+
} catch (error) {
|
|
400
|
+
observed.fail?.(error);
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
return execution;
|
|
405
|
+
};
|
|
406
|
+
let activeScope;
|
|
407
|
+
try {
|
|
408
|
+
activeScope = adapter?.withSpan?.bind(adapter);
|
|
409
|
+
} catch {
|
|
410
|
+
}
|
|
411
|
+
if (activeScope) {
|
|
412
|
+
let started;
|
|
413
|
+
const invoked = new Promise((resolve) => {
|
|
414
|
+
started = resolve;
|
|
415
|
+
});
|
|
416
|
+
const observe = async () => {
|
|
417
|
+
try {
|
|
418
|
+
await activeScope(args, (span) => {
|
|
419
|
+
const result = invoke(span);
|
|
420
|
+
started?.();
|
|
421
|
+
return result;
|
|
422
|
+
});
|
|
423
|
+
} catch {
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
const observation = observe();
|
|
427
|
+
if (!execution) await Promise.race([invoked, observation]);
|
|
428
|
+
} else {
|
|
429
|
+
await invoke(safeStartSpan(adapter, args)).catch(() => {
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return execution ?? invoke(safeSpan(void 0));
|
|
433
|
+
}
|
|
345
434
|
|
|
346
435
|
// src/structured.ts
|
|
347
436
|
var import_node_crypto = require("crypto");
|
|
@@ -435,18 +524,419 @@ var emitStructuredData = (args) => {
|
|
|
435
524
|
var shouldEmitStructured = (cfg) => Boolean(cfg);
|
|
436
525
|
var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
|
|
437
526
|
|
|
438
|
-
// src/
|
|
527
|
+
// src/tool.ts
|
|
528
|
+
var import_node_async_hooks2 = require("async_hooks");
|
|
439
529
|
var import_node_crypto3 = require("crypto");
|
|
440
|
-
var import_core3 = require("@kortyx/core");
|
|
441
|
-
|
|
442
|
-
// src/reason-engine.ts
|
|
443
|
-
var import_node_crypto2 = require("crypto");
|
|
444
530
|
var import_core2 = require("@kortyx/core");
|
|
531
|
+
var import_errors3 = require("@kortyx/core/errors");
|
|
532
|
+
|
|
533
|
+
// src/error-diagnostics.ts
|
|
445
534
|
var import_errors2 = require("@kortyx/core/errors");
|
|
535
|
+
var MAX_STACK_LENGTH = 32768;
|
|
536
|
+
var MAX_CAUSE_DEPTH = 4;
|
|
537
|
+
var projectDetails = (value, depth, seen) => {
|
|
538
|
+
const objectValue = value && typeof value === "object" ? value : void 0;
|
|
539
|
+
if (objectValue) {
|
|
540
|
+
if (seen.has(objectValue))
|
|
541
|
+
return { type: "Error", message: "Circular error cause." };
|
|
542
|
+
seen.add(objectValue);
|
|
543
|
+
}
|
|
544
|
+
const name = (0, import_errors2.errorProperty)(value, "name") ?? (value instanceof Error ? value.name : void 0);
|
|
545
|
+
const message = (0, import_errors2.errorProperty)(value, "message");
|
|
546
|
+
const stack = (0, import_errors2.errorProperty)(value, "stack") ?? (value instanceof Error ? (() => {
|
|
547
|
+
try {
|
|
548
|
+
return value.stack;
|
|
549
|
+
} catch {
|
|
550
|
+
return void 0;
|
|
551
|
+
}
|
|
552
|
+
})() : void 0);
|
|
553
|
+
const cause = depth < MAX_CAUSE_DEPTH ? (0, import_errors2.errorProperty)(value, "cause") : null;
|
|
554
|
+
return {
|
|
555
|
+
type: typeof name === "string" ? name.slice(0, 256) : "Error",
|
|
556
|
+
message: (typeof value === "string" ? value : typeof message === "string" ? message : "Execution reported a fault.").slice(0, 8192),
|
|
557
|
+
...typeof stack === "string" ? { stack: stack.slice(0, MAX_STACK_LENGTH) } : {},
|
|
558
|
+
...cause !== null && cause !== void 0 ? { cause: projectDetails(cause, depth + 1, seen) } : {}
|
|
559
|
+
};
|
|
560
|
+
};
|
|
561
|
+
var sanitizeProjectedDetails = (details, depth = 0) => ({
|
|
562
|
+
...typeof details.type === "string" ? { type: details.type.slice(0, 256) } : {},
|
|
563
|
+
message: typeof details.message === "string" ? details.message.slice(0, 8192) : "Execution reported a fault.",
|
|
564
|
+
...typeof details.stack === "string" ? { stack: details.stack.slice(0, MAX_STACK_LENGTH) } : {},
|
|
565
|
+
...depth < MAX_CAUSE_DEPTH && details.cause ? { cause: sanitizeProjectedDetails(details.cause, depth + 1) } : {}
|
|
566
|
+
});
|
|
567
|
+
function exceptionDiagnostics(error, project) {
|
|
568
|
+
try {
|
|
569
|
+
const details = project ? project(error) : projectDetails(error, 0, /* @__PURE__ */ new WeakSet());
|
|
570
|
+
return details ? sanitizeProjectedDetails(details) : null;
|
|
571
|
+
} catch {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function errorDiagnostics(error, project) {
|
|
576
|
+
const details = exceptionDiagnostics(error, project);
|
|
577
|
+
return details ? {
|
|
578
|
+
errorMessage: details.message,
|
|
579
|
+
...details.type ? { errorType: details.type } : {}
|
|
580
|
+
} : {};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/workflow-telemetry.ts
|
|
584
|
+
var import_node_crypto2 = require("crypto");
|
|
585
|
+
function emitWorkflowCall(telemetry, type, payload) {
|
|
586
|
+
const base = telemetry?.correlation;
|
|
587
|
+
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
|
|
588
|
+
return;
|
|
589
|
+
try {
|
|
590
|
+
const active = telemetry.trace?.getActiveContext?.();
|
|
591
|
+
void Promise.resolve(
|
|
592
|
+
telemetry.reporter.emit([
|
|
593
|
+
{
|
|
594
|
+
schemaVersion: 1,
|
|
595
|
+
eventId: (0, import_node_crypto2.randomUUID)(),
|
|
596
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
597
|
+
environment: telemetry.environment,
|
|
598
|
+
service: telemetry.service,
|
|
599
|
+
correlation: {
|
|
600
|
+
...base,
|
|
601
|
+
runId: base.runId,
|
|
602
|
+
workflowId: base.workflowId,
|
|
603
|
+
...active
|
|
604
|
+
},
|
|
605
|
+
type,
|
|
606
|
+
payload
|
|
607
|
+
}
|
|
608
|
+
])
|
|
609
|
+
).catch(() => {
|
|
610
|
+
});
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function workflowCallContent(telemetry, side, value) {
|
|
615
|
+
const policy = telemetry?.captureContent;
|
|
616
|
+
if (!(policy === true || policy && typeof policy === "object" && policy[side]))
|
|
617
|
+
return {};
|
|
618
|
+
const serialized = JSON.stringify(value);
|
|
619
|
+
return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// src/tool.ts
|
|
623
|
+
var tools = new import_node_async_hooks2.AsyncLocalStorage();
|
|
624
|
+
function classification(tool, value, thrown, policy = tool.outcomes) {
|
|
625
|
+
const fallback = {
|
|
626
|
+
outcome: thrown ? "fault" : "success"
|
|
627
|
+
};
|
|
628
|
+
try {
|
|
629
|
+
if (value && typeof value === "object" && "isError" in value && value.isError === true)
|
|
630
|
+
fallback.outcome = "fault";
|
|
631
|
+
const result = thrown ? policy?.classifyError?.(value) : policy?.classifyResult?.(value);
|
|
632
|
+
if (!result || !["success", "denied", "fault"].includes(result.outcome))
|
|
633
|
+
return fallback;
|
|
634
|
+
if (result.outcome !== "denied")
|
|
635
|
+
return thrown || fallback.outcome === "fault" ? fallback : { outcome: result.outcome };
|
|
636
|
+
const code = result.code;
|
|
637
|
+
return {
|
|
638
|
+
outcome: "denied",
|
|
639
|
+
code: typeof code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(code) && policy?.denialCodes?.includes(code) ? code : "DENIED"
|
|
640
|
+
};
|
|
641
|
+
} catch {
|
|
642
|
+
return fallback;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
async function closeOwnedTools(owned) {
|
|
646
|
+
const closers = /* @__PURE__ */ new Map();
|
|
647
|
+
for (const tool of owned) {
|
|
648
|
+
if (tool.closeAfterUse !== false && tool.close && !closers.has(tool.close))
|
|
649
|
+
closers.set(tool.close, tool);
|
|
650
|
+
}
|
|
651
|
+
await Promise.all(
|
|
652
|
+
[...closers].map(async ([close, tool]) => {
|
|
653
|
+
try {
|
|
654
|
+
await close.call(tool);
|
|
655
|
+
} catch {
|
|
656
|
+
const ctx = getHookContext();
|
|
657
|
+
try {
|
|
658
|
+
ctx.reasonTrace?.startSpan({
|
|
659
|
+
name: "kortyx.tool.cleanup",
|
|
660
|
+
attributes: { cleanupFailed: true }
|
|
661
|
+
})?.end?.();
|
|
662
|
+
} catch {
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
})
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
function captureToolError(observation, error, policy, returned = false) {
|
|
669
|
+
try {
|
|
670
|
+
const reported = returned && error && typeof error === "object" && "isError" in error && error.isError === true && "content" in error && typeof error.content === "string" ? { name: "ToolError", message: error.content } : error;
|
|
671
|
+
Object.assign(
|
|
672
|
+
observation,
|
|
673
|
+
errorDiagnostics(policy?.error ? error : reported, policy?.error)
|
|
674
|
+
);
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
function ownership() {
|
|
679
|
+
const ctx = getHookContext();
|
|
680
|
+
const correlation = ctx.node.workflowCallTelemetry?.correlation;
|
|
681
|
+
return {
|
|
682
|
+
workflowId: ctx.node.graph.name,
|
|
683
|
+
nodeId: ctx.node.graph.node,
|
|
684
|
+
...correlation?.workflowRevisionId ? { workflowRevisionId: correlation.workflowRevisionId } : {},
|
|
685
|
+
...correlation?.parentInvocationId ? { parentInvocationId: correlation.parentInvocationId } : {},
|
|
686
|
+
...correlation?.runId ? { runId: correlation.runId } : {},
|
|
687
|
+
...correlation?.invocationId ? { invocationId: correlation.invocationId } : {},
|
|
688
|
+
...correlation?.branchId ? { branchId: correlation.branchId } : {}
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function rememberToolObservation(observation) {
|
|
692
|
+
const ctx = getHookContext();
|
|
693
|
+
const existing = ctx.workflowState.__kortyxToolObservations;
|
|
694
|
+
const observations = Array.isArray(existing) ? existing : [];
|
|
695
|
+
if (!observations.some((item) => item.attemptId === observation.attemptId))
|
|
696
|
+
observations.push({ ...observation });
|
|
697
|
+
ctx.workflowState.__kortyxToolObservations = observations;
|
|
698
|
+
ctx.stateDirty = true;
|
|
699
|
+
}
|
|
700
|
+
function toolTrace(observation) {
|
|
701
|
+
const ctx = getHookContext();
|
|
702
|
+
const adapter = ctx.reasonTrace ?? ctx.node.workflowCallTelemetry?.trace;
|
|
703
|
+
if (adapter) return adapter;
|
|
704
|
+
const telemetry = ctx.node.workflowCallTelemetry;
|
|
705
|
+
if (!telemetry?.reporter) return void 0;
|
|
706
|
+
const correlation = { ...telemetry.correlation, ...observation };
|
|
707
|
+
const spanId = (0, import_node_crypto3.randomUUID)();
|
|
708
|
+
const emit = (type, payload) => emitWorkflowCall(
|
|
709
|
+
{
|
|
710
|
+
...telemetry,
|
|
711
|
+
correlation,
|
|
712
|
+
trace: {
|
|
713
|
+
startSpan: () => void 0,
|
|
714
|
+
getActiveContext: () => ({
|
|
715
|
+
traceId: correlation.runId ?? spanId,
|
|
716
|
+
spanId
|
|
717
|
+
})
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
type,
|
|
721
|
+
payload
|
|
722
|
+
);
|
|
723
|
+
return {
|
|
724
|
+
startSpan: ({ attributes = {} }) => {
|
|
725
|
+
if (attributes.executed === true) emit("tool.started", attributes);
|
|
726
|
+
let ended = false;
|
|
727
|
+
return {
|
|
728
|
+
end: (args) => {
|
|
729
|
+
if (ended) return;
|
|
730
|
+
ended = true;
|
|
731
|
+
const payload = { ...attributes, ...args?.attributes };
|
|
732
|
+
const type = payload.observationKind === "reused" ? "tool.reused" : payload.observationKind === "waiting" ? "tool.waiting" : payload.suspended ? "tool.suspended" : payload.outcome === "denied" ? "tool.denied" : payload.outcome === "fault" ? "tool.failed" : payload.outcome === "cancelled" ? "tool.cancelled" : "tool.completed";
|
|
733
|
+
emit(type, payload);
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
async function executeObservedTool(args) {
|
|
740
|
+
const ctx = getHookContext();
|
|
741
|
+
const observation = {
|
|
742
|
+
version: 1,
|
|
743
|
+
name: args.tool.name,
|
|
744
|
+
toolCallId: args.toolCallId,
|
|
745
|
+
attemptId: (0, import_node_crypto3.randomUUID)(),
|
|
746
|
+
callingMode: args.callingMode,
|
|
747
|
+
executed: false,
|
|
748
|
+
...ownership()
|
|
749
|
+
};
|
|
750
|
+
const scope = {
|
|
751
|
+
tool: args.tool,
|
|
752
|
+
input: args.input,
|
|
753
|
+
signal: args.abortSignal,
|
|
754
|
+
mode: args.callingMode,
|
|
755
|
+
providerToolCallId: args.providerToolCallId ?? args.toolCallId,
|
|
756
|
+
adopted: false
|
|
757
|
+
};
|
|
758
|
+
try {
|
|
759
|
+
(0, import_core2.throwIfExecutionAborted)(args.abortSignal);
|
|
760
|
+
} catch (error) {
|
|
761
|
+
observation.outcome = "cancelled";
|
|
762
|
+
await observeToolFact(observation);
|
|
763
|
+
args.onObservation?.(observation);
|
|
764
|
+
throw error;
|
|
765
|
+
}
|
|
766
|
+
ctx.node.consumeExecution?.("maxToolCalls");
|
|
767
|
+
observation.executed = true;
|
|
768
|
+
const started = performance.now();
|
|
769
|
+
return withSafeTraceSpan(
|
|
770
|
+
toolTrace(observation),
|
|
771
|
+
{
|
|
772
|
+
name: "kortyx.tool",
|
|
773
|
+
attributes: { ...observation, nodeId: ctx.node.graph.node }
|
|
774
|
+
},
|
|
775
|
+
async (span) => tools.run(scope, async () => {
|
|
776
|
+
try {
|
|
777
|
+
const result = await args.tool.execute(args.input, {
|
|
778
|
+
toolCallId: args.providerToolCallId ?? args.toolCallId,
|
|
779
|
+
...args.abortSignal ? { abortSignal: args.abortSignal } : {}
|
|
780
|
+
});
|
|
781
|
+
(0, import_core2.throwIfExecutionAborted)(args.abortSignal);
|
|
782
|
+
const outcome = classification(
|
|
783
|
+
args.tool,
|
|
784
|
+
result,
|
|
785
|
+
false,
|
|
786
|
+
scope.classify ?? args.tool.outcomes
|
|
787
|
+
);
|
|
788
|
+
observation.outcome = outcome.outcome;
|
|
789
|
+
if (outcome.code) observation.denialCode = outcome.code;
|
|
790
|
+
if (observation.outcome === "fault") {
|
|
791
|
+
captureToolError(
|
|
792
|
+
observation,
|
|
793
|
+
result,
|
|
794
|
+
scope.telemetry ?? args.tool.telemetry,
|
|
795
|
+
true
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
return result;
|
|
799
|
+
} catch (error) {
|
|
800
|
+
if (args.abortSignal?.aborted) observation.outcome = "cancelled";
|
|
801
|
+
else if (!(0, import_errors3.isControlFlowError)(error)) {
|
|
802
|
+
const outcome = classification(
|
|
803
|
+
args.tool,
|
|
804
|
+
error,
|
|
805
|
+
true,
|
|
806
|
+
scope.classify ?? args.tool.outcomes
|
|
807
|
+
);
|
|
808
|
+
observation.outcome = outcome.outcome;
|
|
809
|
+
if (outcome.code) observation.denialCode = outcome.code;
|
|
810
|
+
if (observation.outcome === "fault")
|
|
811
|
+
captureToolError(
|
|
812
|
+
observation,
|
|
813
|
+
error,
|
|
814
|
+
scope.telemetry ?? args.tool.telemetry
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
throw error;
|
|
818
|
+
} finally {
|
|
819
|
+
observation.durationMs = Math.max(0, performance.now() - started);
|
|
820
|
+
span.end?.({
|
|
821
|
+
attributes: {
|
|
822
|
+
...observation,
|
|
823
|
+
...observation.outcome ? {} : { suspended: true }
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
rememberToolObservation(observation);
|
|
827
|
+
args.onObservation?.({ ...observation });
|
|
828
|
+
}
|
|
829
|
+
})
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
async function observeToolFact(observation, kind, preserveOwner = false) {
|
|
833
|
+
const ctx = getHookContext();
|
|
834
|
+
if (!preserveOwner) Object.assign(observation, ownership());
|
|
835
|
+
if (observation.outcome && kind !== "reused")
|
|
836
|
+
rememberToolObservation(observation);
|
|
837
|
+
await withSafeTraceSpan(
|
|
838
|
+
toolTrace(observation),
|
|
839
|
+
{
|
|
840
|
+
name: "kortyx.tool",
|
|
841
|
+
attributes: {
|
|
842
|
+
...observation,
|
|
843
|
+
executed: false,
|
|
844
|
+
...kind ? { observationKind: kind } : {},
|
|
845
|
+
nodeId: observation.nodeId ?? ctx.node.graph.node
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
async (span) => {
|
|
849
|
+
span.end?.({
|
|
850
|
+
attributes: {
|
|
851
|
+
...observation,
|
|
852
|
+
executed: false,
|
|
853
|
+
...kind ? { observationKind: kind } : {}
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
async function useTool(args) {
|
|
860
|
+
const ctx = getHookContext();
|
|
861
|
+
const scope = tools.getStore();
|
|
862
|
+
if (scope?.mode === "model" && !scope.adopted && scope.tool.name === args.tool.name && scope.input === args.input) {
|
|
863
|
+
scope.adopted = true;
|
|
864
|
+
scope.classify = args.tool.outcomes ?? scope.classify;
|
|
865
|
+
scope.telemetry = args.tool.telemetry ?? scope.telemetry;
|
|
866
|
+
return tools.run(
|
|
867
|
+
{ ...scope, mode: "direct" },
|
|
868
|
+
() => args.tool.execute(args.input, {
|
|
869
|
+
toolCallId: scope.providerToolCallId,
|
|
870
|
+
...scope.signal ? { abortSignal: scope.signal } : {}
|
|
871
|
+
})
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
const index = ctx.toolCallIndex++;
|
|
875
|
+
const key = `__useTool:${args.id ?? `auto:${index}`}`;
|
|
876
|
+
let id = ctx.currentNodeState.byKey[key];
|
|
877
|
+
if (typeof id !== "string") {
|
|
878
|
+
id = (0, import_node_crypto3.randomUUID)();
|
|
879
|
+
ctx.currentNodeState.byKey[key] = id;
|
|
880
|
+
ctx.stateDirty = true;
|
|
881
|
+
}
|
|
882
|
+
try {
|
|
883
|
+
return await executeObservedTool({
|
|
884
|
+
tool: args.tool,
|
|
885
|
+
input: args.input,
|
|
886
|
+
toolCallId: String(id),
|
|
887
|
+
callingMode: "direct",
|
|
888
|
+
abortSignal: (0, import_core2.combineAbortSignals)(ctx.node.abortSignal, args.abortSignal)
|
|
889
|
+
});
|
|
890
|
+
} finally {
|
|
891
|
+
await closeOwnedTools([args.tool]);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
async function replayToolObservations(observations, child = false) {
|
|
895
|
+
for (const original of observations) {
|
|
896
|
+
const source = original.source ?? {
|
|
897
|
+
...ownershipFrom(original),
|
|
898
|
+
toolCallId: original.toolCallId,
|
|
899
|
+
attemptId: original.attemptId
|
|
900
|
+
};
|
|
901
|
+
await observeToolFact(
|
|
902
|
+
{
|
|
903
|
+
...original,
|
|
904
|
+
...ownership(),
|
|
905
|
+
...child ? {
|
|
906
|
+
workflowId: original.workflowId,
|
|
907
|
+
nodeId: original.nodeId,
|
|
908
|
+
workflowRevisionId: original.workflowRevisionId,
|
|
909
|
+
invocationId: original.invocationId,
|
|
910
|
+
parentInvocationId: original.parentInvocationId
|
|
911
|
+
} : {},
|
|
912
|
+
attemptId: (0, import_node_crypto3.randomUUID)(),
|
|
913
|
+
executed: false,
|
|
914
|
+
source,
|
|
915
|
+
durationMs: void 0
|
|
916
|
+
},
|
|
917
|
+
"reused",
|
|
918
|
+
child
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
var ownershipFrom = (observation) => ({
|
|
923
|
+
runId: observation.runId,
|
|
924
|
+
invocationId: observation.invocationId,
|
|
925
|
+
branchId: observation.branchId
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
// src/reason/engine.ts
|
|
929
|
+
var import_node_crypto5 = require("crypto");
|
|
930
|
+
var import_core4 = require("@kortyx/core");
|
|
931
|
+
|
|
932
|
+
// src/reason-engine.ts
|
|
933
|
+
var import_node_crypto4 = require("crypto");
|
|
934
|
+
var import_core3 = require("@kortyx/core");
|
|
935
|
+
var import_errors4 = require("@kortyx/core/errors");
|
|
446
936
|
function assertModelFinish(result) {
|
|
447
937
|
if (result.finishReason?.unified !== "content-filter") return;
|
|
448
938
|
throw Object.assign(
|
|
449
|
-
new
|
|
939
|
+
new import_errors4.KortyxError("PROVIDER_REFUSAL", "The provider refused the request.", {
|
|
450
940
|
category: "provider",
|
|
451
941
|
retryable: false,
|
|
452
942
|
safeMessage: "The provider refused the request."
|
|
@@ -460,7 +950,7 @@ function assertModelFinish(result) {
|
|
|
460
950
|
var toError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
461
951
|
var createRuntimeId = () => {
|
|
462
952
|
try {
|
|
463
|
-
return (0,
|
|
953
|
+
return (0, import_node_crypto4.randomUUID)();
|
|
464
954
|
} catch {
|
|
465
955
|
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
466
956
|
}
|
|
@@ -495,8 +985,8 @@ async function runReasonEngine(args) {
|
|
|
495
985
|
args.model.options?.providerOptions,
|
|
496
986
|
args.providerOptions
|
|
497
987
|
);
|
|
498
|
-
const
|
|
499
|
-
(0,
|
|
988
|
+
const tools2 = args.tools ?? args.model.options?.tools;
|
|
989
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
500
990
|
const model = args.model.provider.getModel(args.model.modelId, {
|
|
501
991
|
...temperature !== void 0 ? { temperature } : {},
|
|
502
992
|
streaming: stream,
|
|
@@ -505,10 +995,10 @@ async function runReasonEngine(args) {
|
|
|
505
995
|
...abortSignal !== void 0 ? { abortSignal } : {},
|
|
506
996
|
...reasoning !== void 0 ? { reasoning } : {},
|
|
507
997
|
...responseFormat !== void 0 ? { responseFormat } : {},
|
|
508
|
-
...
|
|
998
|
+
...tools2 !== void 0 ? { tools: tools2 } : {},
|
|
509
999
|
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
510
1000
|
});
|
|
511
|
-
if (
|
|
1001
|
+
if (tools2?.length && !model.supportsToolStreaming) stream = false;
|
|
512
1002
|
const messages = args.messages !== void 0 ? args.messages : [
|
|
513
1003
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
514
1004
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -520,7 +1010,7 @@ async function runReasonEngine(args) {
|
|
|
520
1010
|
opId,
|
|
521
1011
|
segmentId
|
|
522
1012
|
};
|
|
523
|
-
const traceSpan = args.reasonTrace
|
|
1013
|
+
const traceSpan = safeStartSpan(args.reasonTrace, {
|
|
524
1014
|
name: "runReasonEngine",
|
|
525
1015
|
attributes: {
|
|
526
1016
|
...commonMeta,
|
|
@@ -600,7 +1090,7 @@ async function runReasonEngine(args) {
|
|
|
600
1090
|
}
|
|
601
1091
|
const response2 = await model.stream(messages);
|
|
602
1092
|
for await (const chunk of response2) {
|
|
603
|
-
(0,
|
|
1093
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
604
1094
|
switch (chunk.type) {
|
|
605
1095
|
case "text-delta": {
|
|
606
1096
|
if (chunk.delta.length === 0) break;
|
|
@@ -666,7 +1156,7 @@ async function runReasonEngine(args) {
|
|
|
666
1156
|
}
|
|
667
1157
|
}
|
|
668
1158
|
}
|
|
669
|
-
(0,
|
|
1159
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
670
1160
|
assertModelFinish({ finishReason, usage });
|
|
671
1161
|
if (emit) {
|
|
672
1162
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
@@ -683,12 +1173,12 @@ async function runReasonEngine(args) {
|
|
|
683
1173
|
...providerMetadata !== void 0 ? { providerMetadata } : {},
|
|
684
1174
|
...warnings !== void 0 ? { warnings } : {}
|
|
685
1175
|
};
|
|
686
|
-
(0,
|
|
1176
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
687
1177
|
endTrace(traceSpan, result2);
|
|
688
1178
|
return result2;
|
|
689
1179
|
}
|
|
690
1180
|
const response = await model.invoke(messages);
|
|
691
|
-
(0,
|
|
1181
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
692
1182
|
assertModelFinish(response);
|
|
693
1183
|
if (response.content) args.onTextChunk?.(response.content);
|
|
694
1184
|
if (emit) {
|
|
@@ -718,7 +1208,7 @@ async function runReasonEngine(args) {
|
|
|
718
1208
|
endTrace(traceSpan, result);
|
|
719
1209
|
return result;
|
|
720
1210
|
} catch (error) {
|
|
721
|
-
const failure = abortSignal?.aborted ? (0,
|
|
1211
|
+
const failure = abortSignal?.aborted ? (0, import_core3.createExecutionCancelledError)() : error;
|
|
722
1212
|
failTrace(traceSpan, failure);
|
|
723
1213
|
throw failure;
|
|
724
1214
|
}
|
|
@@ -727,19 +1217,19 @@ async function runReasonEngine(args) {
|
|
|
727
1217
|
// src/reason/engine.ts
|
|
728
1218
|
var createRuntimeId2 = () => {
|
|
729
1219
|
try {
|
|
730
|
-
return (0,
|
|
1220
|
+
return (0, import_node_crypto5.randomUUID)();
|
|
731
1221
|
} catch {
|
|
732
1222
|
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
733
1223
|
}
|
|
734
1224
|
};
|
|
735
1225
|
async function reasonEngine(args, meta, inputOverride) {
|
|
736
1226
|
const ctx = getHookContext();
|
|
737
|
-
const abortSignal = (0,
|
|
1227
|
+
const abortSignal = (0, import_core4.combineAbortSignals)(
|
|
738
1228
|
ctx.node.abortSignal,
|
|
739
1229
|
args.abortSignal,
|
|
740
1230
|
args.model.options?.abortSignal
|
|
741
1231
|
);
|
|
742
|
-
(0,
|
|
1232
|
+
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
743
1233
|
ctx.node.consumeExecution?.("maxModelPasses");
|
|
744
1234
|
try {
|
|
745
1235
|
return await runReasonEngine({
|
|
@@ -1489,7 +1979,7 @@ function createStructuredOutputStreamer(args, id, opId) {
|
|
|
1489
1979
|
}
|
|
1490
1980
|
|
|
1491
1981
|
// src/reason/parsing.ts
|
|
1492
|
-
var
|
|
1982
|
+
var import_errors5 = require("@kortyx/core/errors");
|
|
1493
1983
|
var tryJsonParse = (value) => {
|
|
1494
1984
|
try {
|
|
1495
1985
|
return { ok: true, value: JSON.parse(value) };
|
|
@@ -1527,12 +2017,12 @@ var resolveOutputCandidate = (text) => {
|
|
|
1527
2017
|
}
|
|
1528
2018
|
return text;
|
|
1529
2019
|
};
|
|
1530
|
-
var createTruncatedStructuredOutputError = (label, cause) => new
|
|
2020
|
+
var createTruncatedStructuredOutputError = (label, cause) => new import_errors5.ValidationError(
|
|
1531
2021
|
"OUTPUT_TRUNCATED",
|
|
1532
2022
|
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`,
|
|
1533
2023
|
cause
|
|
1534
2024
|
);
|
|
1535
|
-
var createInvalidStructuredOutputError = (label, cause) => new
|
|
2025
|
+
var createInvalidStructuredOutputError = (label, cause) => new import_errors5.ValidationError(
|
|
1536
2026
|
"INVALID_MODEL_JSON",
|
|
1537
2027
|
`${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. Check outputSchema and provider structured-output settings.`,
|
|
1538
2028
|
cause
|
|
@@ -1544,7 +2034,7 @@ var parseReasonOutputWithSchema = (args) => {
|
|
|
1544
2034
|
try {
|
|
1545
2035
|
return parseWithSchema(args.schema, candidate, args.label);
|
|
1546
2036
|
} catch (error) {
|
|
1547
|
-
if (!(error instanceof
|
|
2037
|
+
if (!(error instanceof import_errors5.ValidationError)) throw error;
|
|
1548
2038
|
const failure = args.finishReason?.unified === "length" ? createTruncatedStructuredOutputError(args.label, error) : typeof candidate === "string" && !validJson ? createInvalidStructuredOutputError(args.label, error) : error;
|
|
1549
2039
|
if (failure && typeof failure === "object")
|
|
1550
2040
|
Object.assign(failure, {
|
|
@@ -1564,7 +2054,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1564
2054
|
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1565
2055
|
throw createInvalidStructuredOutputError("useReason output");
|
|
1566
2056
|
}
|
|
1567
|
-
throw new
|
|
2057
|
+
throw new import_errors5.ValidationError(
|
|
1568
2058
|
"MODEL_OUTPUT_SCHEMA",
|
|
1569
2059
|
"useReason first pass with interrupt must return a JSON object."
|
|
1570
2060
|
);
|
|
@@ -1573,7 +2063,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1573
2063
|
let interruptRequired = true;
|
|
1574
2064
|
if (mode === "optional") {
|
|
1575
2065
|
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
1576
|
-
throw new
|
|
2066
|
+
throw new import_errors5.ValidationError(
|
|
1577
2067
|
"MODEL_OUTPUT_SCHEMA",
|
|
1578
2068
|
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
1579
2069
|
);
|
|
@@ -1586,7 +2076,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1586
2076
|
candidate.interrupt
|
|
1587
2077
|
].some((value) => value !== null && value !== void 0);
|
|
1588
2078
|
if (hasUnexpectedRequest) {
|
|
1589
|
-
throw new
|
|
2079
|
+
throw new import_errors5.ValidationError(
|
|
1590
2080
|
"MODEL_OUTPUT_SCHEMA",
|
|
1591
2081
|
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
1592
2082
|
);
|
|
@@ -1620,7 +2110,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1620
2110
|
"useReason output"
|
|
1621
2111
|
);
|
|
1622
2112
|
} catch (error) {
|
|
1623
|
-
if (!(error instanceof
|
|
2113
|
+
if (!(error instanceof import_errors5.ValidationError)) throw error;
|
|
1624
2114
|
if (args.finishReason?.unified === "length") {
|
|
1625
2115
|
throw createTruncatedStructuredOutputError("useReason output", error);
|
|
1626
2116
|
}
|
|
@@ -1629,7 +2119,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1629
2119
|
}
|
|
1630
2120
|
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
1631
2121
|
if (!args.outputSchema && !explicitDraft) {
|
|
1632
|
-
throw new
|
|
2122
|
+
throw new import_errors5.ValidationError(
|
|
1633
2123
|
"MODEL_OUTPUT_SCHEMA",
|
|
1634
2124
|
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
1635
2125
|
);
|
|
@@ -1819,6 +2309,7 @@ var mergeUsage = (left, right) => {
|
|
|
1819
2309
|
const reasoning = sum(left.reasoning, right.reasoning);
|
|
1820
2310
|
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
1821
2311
|
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
2312
|
+
const cacheWrite1h = sum(left.cacheWrite1h, right.cacheWrite1h);
|
|
1822
2313
|
const merged = {
|
|
1823
2314
|
...right.outputIncludesReasoning !== void 0 ? { outputIncludesReasoning: right.outputIncludesReasoning } : {},
|
|
1824
2315
|
...right.inputIncludesCacheWrite !== void 0 ? { inputIncludesCacheWrite: right.inputIncludesCacheWrite } : {},
|
|
@@ -1830,6 +2321,7 @@ var mergeUsage = (left, right) => {
|
|
|
1830
2321
|
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1831
2322
|
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1832
2323
|
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
2324
|
+
if (cacheWrite1h !== void 0) merged.cacheWrite1h = cacheWrite1h;
|
|
1833
2325
|
if (raw) merged.raw = raw;
|
|
1834
2326
|
return merged;
|
|
1835
2327
|
};
|
|
@@ -1868,9 +2360,9 @@ var emitReasonStructuredOutput = (args) => {
|
|
|
1868
2360
|
};
|
|
1869
2361
|
|
|
1870
2362
|
// src/reason/tools.ts
|
|
1871
|
-
var
|
|
1872
|
-
var
|
|
1873
|
-
var toToolDefinitions = (
|
|
2363
|
+
var import_core5 = require("@kortyx/core");
|
|
2364
|
+
var import_errors6 = require("@kortyx/core/errors");
|
|
2365
|
+
var toToolDefinitions = (tools2) => tools2.map((tool) => ({
|
|
1874
2366
|
name: tool.name,
|
|
1875
2367
|
...tool.title ? { title: tool.title } : {},
|
|
1876
2368
|
...tool.description ? { description: tool.description } : {},
|
|
@@ -1887,12 +2379,13 @@ var resolveToolExecutionBoolean = (value, toolName, fallback) => {
|
|
|
1887
2379
|
return fallback;
|
|
1888
2380
|
};
|
|
1889
2381
|
var normalizeToolResult = (toolCall, value) => {
|
|
1890
|
-
if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value) {
|
|
1891
|
-
|
|
2382
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value && typeof value.content === "string") {
|
|
2383
|
+
const result = value;
|
|
2384
|
+
return { ...result, toolCallId: toolCall.id, name: toolCall.name };
|
|
1892
2385
|
}
|
|
1893
2386
|
let content;
|
|
1894
2387
|
try {
|
|
1895
|
-
content = typeof value === "string" ? value : JSON.stringify(value);
|
|
2388
|
+
content = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
|
|
1896
2389
|
} catch {
|
|
1897
2390
|
content = String(value);
|
|
1898
2391
|
}
|
|
@@ -1903,28 +2396,6 @@ var normalizeToolResult = (toolCall, value) => {
|
|
|
1903
2396
|
...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
|
|
1904
2397
|
};
|
|
1905
2398
|
};
|
|
1906
|
-
var closeOwnedTools = async (tools, preserveFailure = false, traceSpan) => {
|
|
1907
|
-
const closeFns = /* @__PURE__ */ new Set();
|
|
1908
|
-
for (const tool of tools) {
|
|
1909
|
-
if (tool.closeAfterUse === false || !tool.close) continue;
|
|
1910
|
-
closeFns.add(tool.close);
|
|
1911
|
-
}
|
|
1912
|
-
try {
|
|
1913
|
-
await Promise.all(
|
|
1914
|
-
[...closeFns].map(async (close) => {
|
|
1915
|
-
await close();
|
|
1916
|
-
})
|
|
1917
|
-
);
|
|
1918
|
-
} catch (error) {
|
|
1919
|
-
if (!preserveFailure) throw error;
|
|
1920
|
-
try {
|
|
1921
|
-
traceSpan?.addEvent?.("useReason.cleanup.failed", {
|
|
1922
|
-
failure: (0, import_errors4.serializeFailure)(error)
|
|
1923
|
-
});
|
|
1924
|
-
} catch {
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
};
|
|
1928
2399
|
var createInitialMessages = (args) => [
|
|
1929
2400
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
1930
2401
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -1932,7 +2403,7 @@ var createInitialMessages = (args) => [
|
|
|
1932
2403
|
var runReasonToolLoop = async (args) => {
|
|
1933
2404
|
const { useReasonArgs, id, traceSpan, checkpointKey } = args;
|
|
1934
2405
|
const ctx = getHookContext();
|
|
1935
|
-
const abortSignal = (0,
|
|
2406
|
+
const abortSignal = (0, import_core5.combineAbortSignals)(
|
|
1936
2407
|
ctx.node.abortSignal,
|
|
1937
2408
|
useReasonArgs.abortSignal,
|
|
1938
2409
|
useReasonArgs.model.options?.abortSignal
|
|
@@ -1940,43 +2411,32 @@ var runReasonToolLoop = async (args) => {
|
|
|
1940
2411
|
const saved = ctx.currentNodeState.byKey[checkpointKey];
|
|
1941
2412
|
const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
|
|
1942
2413
|
const opId = checkpoint?.opId ?? args.opId;
|
|
1943
|
-
|
|
1944
|
-
const tools = useReasonArgs.tools ?? [];
|
|
2414
|
+
const tools2 = useReasonArgs.tools ?? [];
|
|
1945
2415
|
const toolByName = /* @__PURE__ */ new Map();
|
|
1946
|
-
let validationCompleted = false;
|
|
1947
2416
|
let separateOutput = false;
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
throw new Error(
|
|
1952
|
-
`useReason received duplicate tool name "${tool.name}".`
|
|
1953
|
-
);
|
|
1954
|
-
}
|
|
1955
|
-
toolByName.set(tool.name, tool);
|
|
2417
|
+
for (const tool of tools2) {
|
|
2418
|
+
if (toolByName.has(tool.name)) {
|
|
2419
|
+
throw new Error(`useReason received duplicate tool name "${tool.name}".`);
|
|
1956
2420
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
separateOutput = Boolean(
|
|
1963
|
-
useReasonArgs.model.provider.getModel(useReasonArgs.model.modelId, {
|
|
1964
|
-
...useReasonArgs.model.options,
|
|
1965
|
-
...useReasonArgs.reasoning !== void 0 ? { reasoning: useReasonArgs.reasoning } : {},
|
|
1966
|
-
...useReasonArgs.responseFormat ? { responseFormat: useReasonArgs.responseFormat } : {},
|
|
1967
|
-
providerOptions: resolveProviderOptions(
|
|
1968
|
-
useReasonArgs.model.options?.providerOptions,
|
|
1969
|
-
useReasonArgs.providerOptions
|
|
1970
|
-
) ?? {}
|
|
1971
|
-
}).requiresSeparateStructuredOutput
|
|
2421
|
+
toolByName.set(tool.name, tool);
|
|
2422
|
+
}
|
|
2423
|
+
if (useReasonArgs.interrupt) {
|
|
2424
|
+
throw new Error(
|
|
2425
|
+
"useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
|
|
1972
2426
|
);
|
|
1973
|
-
validationCompleted = true;
|
|
1974
|
-
} finally {
|
|
1975
|
-
if (!validationCompleted) {
|
|
1976
|
-
await closeOwnedTools(tools, true, traceSpan);
|
|
1977
|
-
}
|
|
1978
2427
|
}
|
|
1979
|
-
|
|
2428
|
+
separateOutput = Boolean(
|
|
2429
|
+
useReasonArgs.model.provider.getModel(useReasonArgs.model.modelId, {
|
|
2430
|
+
...useReasonArgs.model.options,
|
|
2431
|
+
...useReasonArgs.reasoning !== void 0 ? { reasoning: useReasonArgs.reasoning } : {},
|
|
2432
|
+
...useReasonArgs.responseFormat ? { responseFormat: useReasonArgs.responseFormat } : {},
|
|
2433
|
+
providerOptions: resolveProviderOptions(
|
|
2434
|
+
useReasonArgs.model.options?.providerOptions,
|
|
2435
|
+
useReasonArgs.providerOptions
|
|
2436
|
+
) ?? {}
|
|
2437
|
+
}).requiresSeparateStructuredOutput
|
|
2438
|
+
);
|
|
2439
|
+
const toolDefinitions = toToolDefinitions(tools2);
|
|
1980
2440
|
let finalizing = checkpoint?.finalizing ?? false;
|
|
1981
2441
|
let completed = false;
|
|
1982
2442
|
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
@@ -2028,7 +2488,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2028
2488
|
};
|
|
2029
2489
|
try {
|
|
2030
2490
|
for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
2031
|
-
(0,
|
|
2491
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2032
2492
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
2033
2493
|
stepIndex,
|
|
2034
2494
|
toolCount: toolDefinitions.length
|
|
@@ -2072,6 +2532,12 @@ var runReasonToolLoop = async (args) => {
|
|
|
2072
2532
|
);
|
|
2073
2533
|
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
2074
2534
|
const toolCalls = step.toolCalls ?? [];
|
|
2535
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2536
|
+
for (const call of toolCalls) {
|
|
2537
|
+
if (!call.id || ids.has(call.id) || !reused && allToolCalls.some((prior) => prior.id === call.id))
|
|
2538
|
+
throw new Error("Model returned duplicate or empty tool call IDs.");
|
|
2539
|
+
ids.add(call.id);
|
|
2540
|
+
}
|
|
2075
2541
|
const toolResults = reused ? steps[stepIndex]?.toolResults ?? [] : [];
|
|
2076
2542
|
if (!reused)
|
|
2077
2543
|
steps.push({
|
|
@@ -2140,9 +2606,15 @@ var runReasonToolLoop = async (args) => {
|
|
|
2140
2606
|
pending = step;
|
|
2141
2607
|
save();
|
|
2142
2608
|
for (const toolCall of toolCalls) {
|
|
2143
|
-
if (toolResults.some((result2) => result2.toolCallId === toolCall.id))
|
|
2609
|
+
if (toolResults.some((result2) => result2.toolCallId === toolCall.id)) {
|
|
2610
|
+
await replayToolObservations(
|
|
2611
|
+
steps[stepIndex]?.toolObservations?.filter(
|
|
2612
|
+
(observation) => observation.toolCallId === `${opId}:${toolCall.id}`
|
|
2613
|
+
) ?? []
|
|
2614
|
+
);
|
|
2144
2615
|
continue;
|
|
2145
|
-
|
|
2616
|
+
}
|
|
2617
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2146
2618
|
const tool = toolByName.get(toolCall.name);
|
|
2147
2619
|
if (!tool) {
|
|
2148
2620
|
throw new Error(
|
|
@@ -2159,11 +2631,6 @@ var runReasonToolLoop = async (args) => {
|
|
|
2159
2631
|
tool.name,
|
|
2160
2632
|
false
|
|
2161
2633
|
);
|
|
2162
|
-
traceSpan?.addEvent?.("useReason.tool-call.start", {
|
|
2163
|
-
stepIndex,
|
|
2164
|
-
tool: tool.name,
|
|
2165
|
-
toolCallId: toolCall.id
|
|
2166
|
-
});
|
|
2167
2634
|
if (shouldEmitTool) {
|
|
2168
2635
|
emitToolEvent("tool-call-start", {
|
|
2169
2636
|
tool: tool.name,
|
|
@@ -2172,6 +2639,17 @@ var runReasonToolLoop = async (args) => {
|
|
|
2172
2639
|
});
|
|
2173
2640
|
}
|
|
2174
2641
|
if (shouldApproveTool && !approvedCalls.includes(toolCall.id)) {
|
|
2642
|
+
await observeToolFact(
|
|
2643
|
+
{
|
|
2644
|
+
version: 1,
|
|
2645
|
+
name: tool.name,
|
|
2646
|
+
toolCallId: `${opId}:${toolCall.id}`,
|
|
2647
|
+
attemptId: `${opId}:${toolCall.id}:approval`,
|
|
2648
|
+
callingMode: "model",
|
|
2649
|
+
executed: false
|
|
2650
|
+
},
|
|
2651
|
+
"waiting"
|
|
2652
|
+
);
|
|
2175
2653
|
const approval = await awaitInterruptInternal({
|
|
2176
2654
|
id: `tool:${toolCall.id}`,
|
|
2177
2655
|
request: {
|
|
@@ -2190,6 +2668,22 @@ var runReasonToolLoop = async (args) => {
|
|
|
2190
2668
|
});
|
|
2191
2669
|
const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
|
|
2192
2670
|
if (!approved) {
|
|
2671
|
+
const deniedObservation = {
|
|
2672
|
+
version: 1,
|
|
2673
|
+
name: tool.name,
|
|
2674
|
+
toolCallId: `${opId}:${toolCall.id}`,
|
|
2675
|
+
attemptId: `${opId}:${toolCall.id}:approval`,
|
|
2676
|
+
callingMode: "model",
|
|
2677
|
+
executed: false,
|
|
2678
|
+
outcome: "denied",
|
|
2679
|
+
denialCode: "APPROVAL_DENIED"
|
|
2680
|
+
};
|
|
2681
|
+
await observeToolFact(deniedObservation);
|
|
2682
|
+
const entry = steps[stepIndex];
|
|
2683
|
+
if (entry) {
|
|
2684
|
+
entry.toolObservations ??= [];
|
|
2685
|
+
entry.toolObservations.push(deniedObservation);
|
|
2686
|
+
}
|
|
2193
2687
|
const denied = {
|
|
2194
2688
|
toolCallId: toolCall.id,
|
|
2195
2689
|
name: tool.name,
|
|
@@ -2220,13 +2714,24 @@ var runReasonToolLoop = async (args) => {
|
|
|
2220
2714
|
save();
|
|
2221
2715
|
}
|
|
2222
2716
|
try {
|
|
2223
|
-
(0,
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2717
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2718
|
+
const rawResult = await executeObservedTool({
|
|
2719
|
+
tool,
|
|
2720
|
+
input: toolCall.input,
|
|
2721
|
+
toolCallId: `${opId}:${toolCall.id}`,
|
|
2722
|
+
providerToolCallId: toolCall.id,
|
|
2723
|
+
callingMode: "model",
|
|
2724
|
+
abortSignal,
|
|
2725
|
+
onObservation: (observation) => {
|
|
2726
|
+
const entry = steps[stepIndex];
|
|
2727
|
+
if (entry) {
|
|
2728
|
+
entry.toolObservations ??= [];
|
|
2729
|
+
entry.toolObservations.push(observation);
|
|
2730
|
+
save();
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2228
2733
|
});
|
|
2229
|
-
(0,
|
|
2734
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2230
2735
|
const result2 = normalizeToolResult(toolCall, rawResult);
|
|
2231
2736
|
toolResults.push(result2);
|
|
2232
2737
|
allToolResults.push(result2);
|
|
@@ -2239,20 +2744,14 @@ var runReasonToolLoop = async (args) => {
|
|
|
2239
2744
|
...result2.isError !== void 0 ? { isError: result2.isError } : {}
|
|
2240
2745
|
});
|
|
2241
2746
|
}
|
|
2242
|
-
traceSpan?.addEvent?.("useReason.tool-call.complete", {
|
|
2243
|
-
stepIndex,
|
|
2244
|
-
tool: result2.name,
|
|
2245
|
-
toolCallId: result2.toolCallId,
|
|
2246
|
-
isError: Boolean(result2.isError)
|
|
2247
|
-
});
|
|
2248
2747
|
} catch (error) {
|
|
2249
|
-
(0,
|
|
2250
|
-
if ((0,
|
|
2748
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2749
|
+
if ((0, import_errors6.isControlFlowError)(error)) throw error;
|
|
2251
2750
|
const result2 = {
|
|
2252
2751
|
toolCallId: toolCall.id,
|
|
2253
2752
|
name: tool.name,
|
|
2254
|
-
content: (0,
|
|
2255
|
-
failure: (0,
|
|
2753
|
+
content: (0, import_errors6.serializeFailure)(error).message,
|
|
2754
|
+
failure: (0, import_errors6.serializeFailure)(error),
|
|
2256
2755
|
isError: true
|
|
2257
2756
|
};
|
|
2258
2757
|
toolResults.push(result2);
|
|
@@ -2265,13 +2764,6 @@ var runReasonToolLoop = async (args) => {
|
|
|
2265
2764
|
failure: result2.failure
|
|
2266
2765
|
});
|
|
2267
2766
|
}
|
|
2268
|
-
traceSpan?.addEvent?.("useReason.tool-call.error", {
|
|
2269
|
-
stepIndex,
|
|
2270
|
-
tool: tool.name,
|
|
2271
|
-
toolCallId: toolCall.id,
|
|
2272
|
-
message: result2.content,
|
|
2273
|
-
failure: result2.failure
|
|
2274
|
-
});
|
|
2275
2767
|
}
|
|
2276
2768
|
const completed2 = toolResults.at(-1);
|
|
2277
2769
|
if (completed2)
|
|
@@ -2344,7 +2836,6 @@ var runReasonToolLoop = async (args) => {
|
|
|
2344
2836
|
ctx.stateDirty = true;
|
|
2345
2837
|
return result;
|
|
2346
2838
|
} catch (error) {
|
|
2347
|
-
primaryFailure = true;
|
|
2348
2839
|
try {
|
|
2349
2840
|
traceSpan?.fail?.(error, {
|
|
2350
2841
|
attributes: {
|
|
@@ -2355,8 +2846,6 @@ var runReasonToolLoop = async (args) => {
|
|
|
2355
2846
|
} catch {
|
|
2356
2847
|
}
|
|
2357
2848
|
throw error;
|
|
2358
|
-
} finally {
|
|
2359
|
-
await closeOwnedTools(tools, primaryFailure, traceSpan);
|
|
2360
2849
|
}
|
|
2361
2850
|
};
|
|
2362
2851
|
|
|
@@ -2372,15 +2861,15 @@ var assertReasoningThoughtsCompatibility = (args) => {
|
|
|
2372
2861
|
"useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
|
|
2373
2862
|
);
|
|
2374
2863
|
};
|
|
2375
|
-
async function
|
|
2864
|
+
async function runReason(args) {
|
|
2376
2865
|
assertReasoningThoughtsCompatibility(args);
|
|
2377
2866
|
const ctx = getHookContext();
|
|
2378
|
-
const abortSignal = (0,
|
|
2867
|
+
const abortSignal = (0, import_core6.combineAbortSignals)(
|
|
2379
2868
|
ctx.node.abortSignal,
|
|
2380
2869
|
args.abortSignal,
|
|
2381
2870
|
args.model.options?.abortSignal
|
|
2382
2871
|
);
|
|
2383
|
-
(0,
|
|
2872
|
+
(0, import_core6.throwIfExecutionAborted)(abortSignal);
|
|
2384
2873
|
const allowValidatedToolOutput = Boolean(
|
|
2385
2874
|
args.outputSchema && !(args.responseFormat ?? args.model.options?.responseFormat)
|
|
2386
2875
|
);
|
|
@@ -2403,286 +2892,303 @@ async function useReason(args) {
|
|
|
2403
2892
|
ctx.currentNodeState.byKey[checkpointKey]
|
|
2404
2893
|
);
|
|
2405
2894
|
if (existingCompleted) {
|
|
2895
|
+
await replayToolObservations(
|
|
2896
|
+
existingCompleted.result.steps?.flatMap(
|
|
2897
|
+
(step) => step.toolObservations ?? []
|
|
2898
|
+
) ?? []
|
|
2899
|
+
);
|
|
2406
2900
|
return existingCompleted.result;
|
|
2407
2901
|
}
|
|
2408
2902
|
const existingCheckpoint = readReasonCheckpoint(
|
|
2409
2903
|
ctx.currentNodeState.byKey[checkpointKey]
|
|
2410
2904
|
);
|
|
2411
2905
|
const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
...args.telemetry ?? {},
|
|
2433
|
-
input: args.telemetry?.input ?? args.input
|
|
2434
|
-
}
|
|
2435
|
-
});
|
|
2436
|
-
if (args.tools?.length) {
|
|
2437
|
-
return runReasonToolLoop({
|
|
2438
|
-
useReasonArgs: args,
|
|
2439
|
-
allowValidatedToolOutput,
|
|
2440
|
-
checkpointKey,
|
|
2441
|
-
initialWarnings: inferred.warnings,
|
|
2442
|
-
...id ? { id } : {},
|
|
2443
|
-
opId,
|
|
2444
|
-
...traceSpan ? { traceSpan } : {}
|
|
2445
|
-
});
|
|
2446
|
-
}
|
|
2447
|
-
const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
|
|
2448
|
-
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
2449
|
-
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
2450
|
-
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
2451
|
-
const useStructuredIncrementalStreaming = Boolean(
|
|
2452
|
-
args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
|
|
2453
|
-
);
|
|
2454
|
-
let firstText = "";
|
|
2455
|
-
let firstRaw;
|
|
2456
|
-
let firstOutput;
|
|
2457
|
-
let firstInterruptRequest;
|
|
2458
|
-
let firstInterruptRequired = false;
|
|
2459
|
-
let finalText = "";
|
|
2460
|
-
let finalRaw;
|
|
2461
|
-
let finalOutput;
|
|
2462
|
-
let aggregatedUsage;
|
|
2463
|
-
let finalFinishReason;
|
|
2464
|
-
let aggregatedProviderMetadata;
|
|
2465
|
-
let aggregatedWarnings = inferred.warnings;
|
|
2466
|
-
if (existingCheckpoint) {
|
|
2467
|
-
firstText = existingCheckpoint.firstText;
|
|
2468
|
-
firstRaw = existingCheckpoint.firstRaw;
|
|
2469
|
-
firstOutput = existingCheckpoint.firstOutput;
|
|
2470
|
-
finalText = firstText;
|
|
2471
|
-
finalRaw = firstRaw;
|
|
2472
|
-
finalOutput = firstOutput;
|
|
2473
|
-
aggregatedUsage = existingCheckpoint.firstUsage;
|
|
2474
|
-
finalFinishReason = existingCheckpoint.firstFinishReason;
|
|
2475
|
-
aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
|
|
2476
|
-
aggregatedWarnings = existingCheckpoint.firstWarnings;
|
|
2477
|
-
traceSpan?.addEvent?.("useReason.resume", {
|
|
2478
|
-
checkpointKey
|
|
2479
|
-
});
|
|
2480
|
-
} else {
|
|
2481
|
-
const first = await reasonEngine(
|
|
2482
|
-
{
|
|
2483
|
-
...args,
|
|
2484
|
-
emit: suppressTextStream ? false : args.emit,
|
|
2485
|
-
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2486
|
-
...useStructuredIncrementalStreaming ? {
|
|
2487
|
-
onTextChunk: createStructuredOutputStreamer(args, id, opId)
|
|
2488
|
-
} : {}
|
|
2906
|
+
return withSafeTraceSpan(
|
|
2907
|
+
getReasonTraceAdapter(),
|
|
2908
|
+
{
|
|
2909
|
+
name: "useReason",
|
|
2910
|
+
attributes: {
|
|
2911
|
+
...id ? { id } : {},
|
|
2912
|
+
opId,
|
|
2913
|
+
nodeId: ctx.node.graph.node,
|
|
2914
|
+
providerId: args.model.provider.id,
|
|
2915
|
+
modelId: args.model.modelId,
|
|
2916
|
+
stream: args.stream ?? args.model.options?.streaming ?? true,
|
|
2917
|
+
emit: args.emit ?? true,
|
|
2918
|
+
...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
|
|
2919
|
+
...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
|
|
2920
|
+
...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
|
|
2921
|
+
...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
|
|
2922
|
+
hasOutputSchema: Boolean(args.outputSchema),
|
|
2923
|
+
hasInterrupt: Boolean(args.interrupt),
|
|
2924
|
+
hasStructured: Boolean(args.structured),
|
|
2925
|
+
hasTools: Boolean(args.tools?.length)
|
|
2489
2926
|
},
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
input: args.input
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
firstRaw = first.raw;
|
|
2506
|
-
finalRaw = first.raw;
|
|
2507
|
-
accumulateTokenUsage(first.usage);
|
|
2508
|
-
aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
|
|
2509
|
-
finalFinishReason = first.finishReason;
|
|
2510
|
-
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
2511
|
-
aggregatedProviderMetadata,
|
|
2512
|
-
first.providerMetadata
|
|
2513
|
-
);
|
|
2514
|
-
aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
|
|
2515
|
-
traceSpan?.addEvent?.("useReason.first-pass.complete", {
|
|
2516
|
-
textLength: first.text.length,
|
|
2517
|
-
hasInterrupt: Boolean(args.interrupt)
|
|
2518
|
-
});
|
|
2519
|
-
if (args.interrupt) {
|
|
2520
|
-
const firstPass = parseInterruptFirstPassResult({
|
|
2521
|
-
text: first.text,
|
|
2522
|
-
requestSchema: args.interrupt.requestSchema,
|
|
2523
|
-
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2524
|
-
...first.usage ? { usage: first.usage } : {},
|
|
2525
|
-
...args.outputSchema ? { outputSchema: args.outputSchema } : {},
|
|
2526
|
-
mode: args.interrupt.mode ?? "required"
|
|
2527
|
-
});
|
|
2528
|
-
firstText = firstPass.draftText;
|
|
2529
|
-
finalText = firstPass.draftText;
|
|
2530
|
-
firstInterruptRequired = firstPass.interruptRequired;
|
|
2531
|
-
firstInterruptRequest = firstPass.request;
|
|
2532
|
-
firstOutput = firstPass.output;
|
|
2533
|
-
finalOutput = firstPass.output;
|
|
2534
|
-
} else {
|
|
2535
|
-
firstText = first.text;
|
|
2536
|
-
finalText = first.text;
|
|
2537
|
-
if (args.outputSchema) {
|
|
2538
|
-
firstOutput = parseReasonOutputWithSchema({
|
|
2539
|
-
text: first.text,
|
|
2540
|
-
schema: args.outputSchema,
|
|
2541
|
-
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2542
|
-
...first.usage ? { usage: first.usage } : {},
|
|
2543
|
-
label: "useReason output"
|
|
2927
|
+
telemetry: {
|
|
2928
|
+
...args.telemetry ?? {},
|
|
2929
|
+
input: args.telemetry?.input ?? args.input
|
|
2930
|
+
}
|
|
2931
|
+
},
|
|
2932
|
+
async (traceSpan) => {
|
|
2933
|
+
if (args.tools?.length) {
|
|
2934
|
+
return runReasonToolLoop({
|
|
2935
|
+
useReasonArgs: args,
|
|
2936
|
+
allowValidatedToolOutput,
|
|
2937
|
+
checkpointKey,
|
|
2938
|
+
initialWarnings: inferred.warnings,
|
|
2939
|
+
...id ? { id } : {},
|
|
2940
|
+
opId,
|
|
2941
|
+
...traceSpan ? { traceSpan } : {}
|
|
2544
2942
|
});
|
|
2545
2943
|
}
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
const interruptRequest = existingCheckpoint ? parseWithSchema(
|
|
2553
|
-
requestSchema,
|
|
2554
|
-
existingCheckpoint.request,
|
|
2555
|
-
"useReason interrupt.request"
|
|
2556
|
-
) : firstInterruptRequest;
|
|
2557
|
-
if (!interruptRequest) {
|
|
2558
|
-
throw new Error(
|
|
2559
|
-
"useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
|
|
2944
|
+
const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
|
|
2945
|
+
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
2946
|
+
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
2947
|
+
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
2948
|
+
const useStructuredIncrementalStreaming = Boolean(
|
|
2949
|
+
args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
|
|
2560
2950
|
);
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2951
|
+
let firstText = "";
|
|
2952
|
+
let firstRaw;
|
|
2953
|
+
let firstOutput;
|
|
2954
|
+
let firstInterruptRequest;
|
|
2955
|
+
let firstInterruptRequired = false;
|
|
2956
|
+
let finalText = "";
|
|
2957
|
+
let finalRaw;
|
|
2958
|
+
let finalOutput;
|
|
2959
|
+
let aggregatedUsage;
|
|
2960
|
+
let finalFinishReason;
|
|
2961
|
+
let aggregatedProviderMetadata;
|
|
2962
|
+
let aggregatedWarnings = inferred.warnings;
|
|
2963
|
+
if (existingCheckpoint) {
|
|
2964
|
+
firstText = existingCheckpoint.firstText;
|
|
2965
|
+
firstRaw = existingCheckpoint.firstRaw;
|
|
2966
|
+
firstOutput = existingCheckpoint.firstOutput;
|
|
2967
|
+
finalText = firstText;
|
|
2968
|
+
finalRaw = firstRaw;
|
|
2969
|
+
finalOutput = firstOutput;
|
|
2970
|
+
aggregatedUsage = existingCheckpoint.firstUsage;
|
|
2971
|
+
finalFinishReason = existingCheckpoint.firstFinishReason;
|
|
2972
|
+
aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
|
|
2973
|
+
aggregatedWarnings = existingCheckpoint.firstWarnings;
|
|
2974
|
+
traceSpan?.addEvent?.("useReason.resume", {
|
|
2975
|
+
checkpointKey
|
|
2976
|
+
});
|
|
2977
|
+
} else {
|
|
2978
|
+
const first = await reasonEngine(
|
|
2979
|
+
{
|
|
2980
|
+
...args,
|
|
2981
|
+
emit: suppressTextStream ? false : args.emit,
|
|
2982
|
+
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2983
|
+
...useStructuredIncrementalStreaming ? {
|
|
2984
|
+
onTextChunk: createStructuredOutputStreamer(args, id, opId)
|
|
2985
|
+
} : {}
|
|
2986
|
+
},
|
|
2987
|
+
reasonMeta,
|
|
2988
|
+
args.interrupt ? defaultInterruptFirstPassInput({
|
|
2989
|
+
input: args.input,
|
|
2990
|
+
requestSchema: args.interrupt.requestSchema,
|
|
2991
|
+
mode: args.interrupt.mode ?? "required",
|
|
2992
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {}
|
|
2993
|
+
}) : args.outputSchema ? withOutputGuardrails(
|
|
2994
|
+
withStructuredStreamHints(args.input, {
|
|
2995
|
+
...setFieldPaths.length > 0 ? { setFieldPaths } : {},
|
|
2996
|
+
...appendFieldPaths.length > 0 ? { appendFieldPaths } : {},
|
|
2997
|
+
...textDeltaFieldPaths.length > 0 ? { textDeltaFieldPaths } : {}
|
|
2998
|
+
}),
|
|
2999
|
+
args.outputSchema
|
|
3000
|
+
) : void 0
|
|
3001
|
+
);
|
|
3002
|
+
firstRaw = first.raw;
|
|
3003
|
+
finalRaw = first.raw;
|
|
3004
|
+
accumulateTokenUsage(first.usage);
|
|
3005
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
|
|
3006
|
+
finalFinishReason = first.finishReason;
|
|
3007
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
3008
|
+
aggregatedProviderMetadata,
|
|
3009
|
+
first.providerMetadata
|
|
3010
|
+
);
|
|
3011
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
|
|
3012
|
+
traceSpan?.addEvent?.("useReason.first-pass.complete", {
|
|
3013
|
+
textLength: first.text.length,
|
|
3014
|
+
hasInterrupt: Boolean(args.interrupt)
|
|
3015
|
+
});
|
|
3016
|
+
if (args.interrupt) {
|
|
3017
|
+
const firstPass = parseInterruptFirstPassResult({
|
|
3018
|
+
text: first.text,
|
|
3019
|
+
requestSchema: args.interrupt.requestSchema,
|
|
3020
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
3021
|
+
...first.usage ? { usage: first.usage } : {},
|
|
3022
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {},
|
|
3023
|
+
mode: args.interrupt.mode ?? "required"
|
|
3024
|
+
});
|
|
3025
|
+
firstText = firstPass.draftText;
|
|
3026
|
+
finalText = firstPass.draftText;
|
|
3027
|
+
firstInterruptRequired = firstPass.interruptRequired;
|
|
3028
|
+
firstInterruptRequest = firstPass.request;
|
|
3029
|
+
firstOutput = firstPass.output;
|
|
3030
|
+
finalOutput = firstPass.output;
|
|
3031
|
+
} else {
|
|
3032
|
+
firstText = first.text;
|
|
3033
|
+
finalText = first.text;
|
|
3034
|
+
if (args.outputSchema) {
|
|
3035
|
+
firstOutput = parseReasonOutputWithSchema({
|
|
3036
|
+
text: first.text,
|
|
3037
|
+
schema: args.outputSchema,
|
|
3038
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
3039
|
+
...first.usage ? { usage: first.usage } : {},
|
|
3040
|
+
label: "useReason output"
|
|
3041
|
+
});
|
|
3042
|
+
}
|
|
3043
|
+
finalOutput = firstOutput;
|
|
3044
|
+
}
|
|
2590
3045
|
}
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
3046
|
+
let interruptResponse;
|
|
3047
|
+
if (args.interrupt && (existingCheckpoint || firstInterruptRequired)) {
|
|
3048
|
+
const requestSchema = args.interrupt.requestSchema;
|
|
3049
|
+
const interruptRequest = existingCheckpoint ? parseWithSchema(
|
|
3050
|
+
requestSchema,
|
|
3051
|
+
existingCheckpoint.request,
|
|
3052
|
+
"useReason interrupt.request"
|
|
3053
|
+
) : firstInterruptRequest;
|
|
3054
|
+
if (!interruptRequest) {
|
|
3055
|
+
throw new Error(
|
|
3056
|
+
"useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
|
|
3057
|
+
);
|
|
3058
|
+
}
|
|
3059
|
+
if (!existingCheckpoint) {
|
|
3060
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3061
|
+
status: "awaiting_interrupt",
|
|
3062
|
+
request: interruptRequest,
|
|
3063
|
+
firstText,
|
|
3064
|
+
...firstRaw !== void 0 ? { firstRaw } : {},
|
|
3065
|
+
...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
|
|
3066
|
+
...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
|
|
3067
|
+
...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
|
|
3068
|
+
...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
|
|
3069
|
+
...firstOutput !== void 0 ? { firstOutput } : {}
|
|
3070
|
+
};
|
|
3071
|
+
ctx.stateDirty = true;
|
|
3072
|
+
}
|
|
3073
|
+
const resumeStatePatch = resolveHookStatePatch({
|
|
3074
|
+
nodeId: ctx.node.graph.node,
|
|
3075
|
+
currentNodeState: ctx.currentNodeState,
|
|
3076
|
+
workflowState: ctx.workflowState
|
|
3077
|
+
});
|
|
3078
|
+
interruptResponse = await awaitInterruptInternal({
|
|
3079
|
+
request: interruptRequest,
|
|
3080
|
+
requestSchema,
|
|
3081
|
+
responseSchema: args.interrupt.responseSchema,
|
|
3082
|
+
...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
|
|
3083
|
+
...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
|
|
3084
|
+
...id ? { id } : {},
|
|
3085
|
+
meta: {
|
|
3086
|
+
__kortyxResumeStatePatch: resumeStatePatch
|
|
3087
|
+
}
|
|
3088
|
+
});
|
|
3089
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
3090
|
+
checkpointKey
|
|
3091
|
+
});
|
|
3092
|
+
if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
|
|
3093
|
+
delete ctx.currentNodeState.byKey[checkpointKey];
|
|
3094
|
+
ctx.stateDirty = true;
|
|
3095
|
+
}
|
|
3096
|
+
const continuationInput = defaultContinuationInput({
|
|
3097
|
+
input: args.input,
|
|
3098
|
+
draftText: firstText,
|
|
3099
|
+
...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
|
|
3100
|
+
request: interruptRequest,
|
|
3101
|
+
response: interruptResponse
|
|
3102
|
+
});
|
|
3103
|
+
const second = await reasonEngine(
|
|
3104
|
+
{
|
|
3105
|
+
...args,
|
|
3106
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
3107
|
+
emit: suppressTextStream ? false : args.emit,
|
|
3108
|
+
stream: suppressTextStream ? false : args.stream
|
|
3109
|
+
},
|
|
3110
|
+
reasonMeta,
|
|
3111
|
+
args.outputSchema ? withOutputGuardrails(
|
|
3112
|
+
continuationInput,
|
|
3113
|
+
args.outputSchema
|
|
3114
|
+
) : continuationInput
|
|
3115
|
+
);
|
|
3116
|
+
finalText = second.text;
|
|
3117
|
+
finalRaw = second.raw;
|
|
3118
|
+
accumulateTokenUsage(second.usage);
|
|
3119
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
|
|
3120
|
+
finalFinishReason = second.finishReason;
|
|
3121
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
3122
|
+
aggregatedProviderMetadata,
|
|
3123
|
+
second.providerMetadata
|
|
3124
|
+
);
|
|
3125
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
|
|
3126
|
+
traceSpan?.addEvent?.("useReason.continuation.complete", {
|
|
3127
|
+
textLength: second.text.length
|
|
3128
|
+
});
|
|
3129
|
+
if (args.outputSchema) {
|
|
3130
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
3131
|
+
text: finalText,
|
|
3132
|
+
schema: args.outputSchema,
|
|
3133
|
+
...second.finishReason ? { finishReason: second.finishReason } : {},
|
|
3134
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
3135
|
+
label: "useReason output"
|
|
3136
|
+
});
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
if (finalOutput !== void 0) {
|
|
3140
|
+
emitReasonStructuredOutput({
|
|
3141
|
+
...id ? { id } : {},
|
|
3142
|
+
opId,
|
|
3143
|
+
output: finalOutput,
|
|
3144
|
+
structured: args.structured,
|
|
3145
|
+
emit: args.emit ?? true
|
|
3146
|
+
});
|
|
3147
|
+
}
|
|
3148
|
+
const result = {
|
|
3149
|
+
...id ? { id } : {},
|
|
3150
|
+
opId,
|
|
2634
3151
|
text: finalText,
|
|
2635
|
-
|
|
2636
|
-
...
|
|
2637
|
-
...
|
|
2638
|
-
|
|
3152
|
+
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
3153
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
3154
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
3155
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
3156
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
3157
|
+
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
3158
|
+
...interruptResponse !== void 0 ? { interruptResponse } : {}
|
|
3159
|
+
};
|
|
3160
|
+
if (args.interrupt) {
|
|
3161
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3162
|
+
status: "completed",
|
|
3163
|
+
result
|
|
3164
|
+
};
|
|
3165
|
+
ctx.stateDirty = true;
|
|
3166
|
+
}
|
|
3167
|
+
traceSpan?.end?.({
|
|
3168
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
3169
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
3170
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
3171
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
3172
|
+
attributes: {
|
|
3173
|
+
textLength: finalText.length,
|
|
3174
|
+
resumedFromCheckpoint: Boolean(existingCheckpoint),
|
|
3175
|
+
interrupted: Boolean(interruptResponse !== void 0)
|
|
3176
|
+
},
|
|
3177
|
+
telemetry: {
|
|
3178
|
+
...args.telemetry ?? {},
|
|
3179
|
+
output: args.telemetry?.output ?? finalOutput ?? finalText
|
|
3180
|
+
}
|
|
2639
3181
|
});
|
|
3182
|
+
return result;
|
|
2640
3183
|
}
|
|
3184
|
+
);
|
|
3185
|
+
}
|
|
3186
|
+
async function useReason(args) {
|
|
3187
|
+
try {
|
|
3188
|
+
return await runReason(args);
|
|
3189
|
+
} finally {
|
|
3190
|
+
await closeOwnedTools(args.tools ?? []);
|
|
2641
3191
|
}
|
|
2642
|
-
if (finalOutput !== void 0) {
|
|
2643
|
-
emitReasonStructuredOutput({
|
|
2644
|
-
...id ? { id } : {},
|
|
2645
|
-
opId,
|
|
2646
|
-
output: finalOutput,
|
|
2647
|
-
structured: args.structured,
|
|
2648
|
-
emit: args.emit ?? true
|
|
2649
|
-
});
|
|
2650
|
-
}
|
|
2651
|
-
const result = {
|
|
2652
|
-
...id ? { id } : {},
|
|
2653
|
-
opId,
|
|
2654
|
-
text: finalText,
|
|
2655
|
-
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
2656
|
-
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
2657
|
-
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
2658
|
-
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
2659
|
-
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
2660
|
-
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
2661
|
-
...interruptResponse !== void 0 ? { interruptResponse } : {}
|
|
2662
|
-
};
|
|
2663
|
-
if (args.interrupt) {
|
|
2664
|
-
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
2665
|
-
status: "completed",
|
|
2666
|
-
result
|
|
2667
|
-
};
|
|
2668
|
-
ctx.stateDirty = true;
|
|
2669
|
-
}
|
|
2670
|
-
traceSpan?.end?.({
|
|
2671
|
-
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
2672
|
-
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
2673
|
-
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
2674
|
-
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
2675
|
-
attributes: {
|
|
2676
|
-
textLength: finalText.length,
|
|
2677
|
-
resumedFromCheckpoint: Boolean(existingCheckpoint),
|
|
2678
|
-
interrupted: Boolean(interruptResponse !== void 0)
|
|
2679
|
-
},
|
|
2680
|
-
telemetry: {
|
|
2681
|
-
...args.telemetry ?? {},
|
|
2682
|
-
output: args.telemetry?.output ?? finalOutput ?? finalText
|
|
2683
|
-
}
|
|
2684
|
-
});
|
|
2685
|
-
return result;
|
|
2686
3192
|
}
|
|
2687
3193
|
|
|
2688
3194
|
// src/hooks.ts
|
|
@@ -2741,8 +3247,8 @@ function useWorkflowState(key, ...rest) {
|
|
|
2741
3247
|
}
|
|
2742
3248
|
|
|
2743
3249
|
// src/parallel.ts
|
|
2744
|
-
var
|
|
2745
|
-
var
|
|
3250
|
+
var import_core7 = require("@kortyx/core");
|
|
3251
|
+
var import_errors7 = require("@kortyx/core/errors");
|
|
2746
3252
|
var ParallelChildWaiting = class extends Error {
|
|
2747
3253
|
name = "ParallelChildWaiting";
|
|
2748
3254
|
};
|
|
@@ -2756,14 +3262,14 @@ var ParallelError = class extends AggregateError {
|
|
|
2756
3262
|
);
|
|
2757
3263
|
this.results = results;
|
|
2758
3264
|
this.name = "ParallelError";
|
|
2759
|
-
this.failure = (0,
|
|
3265
|
+
this.failure = (0, import_errors7.serializeFailure)({
|
|
2760
3266
|
version: 1,
|
|
2761
3267
|
code: this.code,
|
|
2762
3268
|
category: "workflow",
|
|
2763
3269
|
message: this.message,
|
|
2764
3270
|
retryable: null,
|
|
2765
3271
|
results: results.map(
|
|
2766
|
-
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0,
|
|
3272
|
+
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0, import_errors7.serializeFailure)(result.reason) }
|
|
2767
3273
|
)
|
|
2768
3274
|
});
|
|
2769
3275
|
}
|
|
@@ -2771,7 +3277,7 @@ var ParallelError = class extends AggregateError {
|
|
|
2771
3277
|
code = "PARALLEL_FAILED";
|
|
2772
3278
|
failure;
|
|
2773
3279
|
toJSON() {
|
|
2774
|
-
return (0,
|
|
3280
|
+
return (0, import_errors7.serializeFailure)(this);
|
|
2775
3281
|
}
|
|
2776
3282
|
};
|
|
2777
3283
|
function registerWorkflowTask(id) {
|
|
@@ -2867,13 +3373,13 @@ function parallel(values) {
|
|
|
2867
3373
|
);
|
|
2868
3374
|
}
|
|
2869
3375
|
if (ready[0]?.status === "rejected") throw ready[0].reason;
|
|
2870
|
-
(0,
|
|
3376
|
+
(0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2871
3377
|
if (ctx.workflowControlError) throw ctx.workflowControlError;
|
|
2872
3378
|
const rejected = outcomes.filter(
|
|
2873
3379
|
(outcome) => outcome.status === "rejected"
|
|
2874
3380
|
);
|
|
2875
3381
|
const control = rejected.find(
|
|
2876
|
-
(outcome) => (0,
|
|
3382
|
+
(outcome) => (0, import_core7.isExecutionCancelled)(outcome.reason) || (0, import_core7.isExecutionLimitReached)(outcome.reason)
|
|
2877
3383
|
);
|
|
2878
3384
|
if (control) {
|
|
2879
3385
|
ctx.workflowControlError = control.reason;
|
|
@@ -2909,55 +3415,22 @@ function parallel(values) {
|
|
|
2909
3415
|
return promise;
|
|
2910
3416
|
}
|
|
2911
3417
|
|
|
2912
|
-
// src/
|
|
2913
|
-
|
|
2914
|
-
var import_core7 = require("@kortyx/core");
|
|
2915
|
-
var import_errors6 = require("@kortyx/core/errors");
|
|
2916
|
-
|
|
2917
|
-
// src/workflow-telemetry.ts
|
|
2918
|
-
var import_node_crypto4 = require("crypto");
|
|
2919
|
-
function emitWorkflowCall(telemetry, type, payload) {
|
|
2920
|
-
const base = telemetry?.correlation;
|
|
2921
|
-
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
|
|
2922
|
-
return;
|
|
3418
|
+
// src/report-error.ts
|
|
3419
|
+
function reportError(error, options = {}) {
|
|
2923
3420
|
try {
|
|
2924
|
-
|
|
2925
|
-
void Promise.resolve(
|
|
2926
|
-
telemetry.reporter.emit([
|
|
2927
|
-
{
|
|
2928
|
-
schemaVersion: 1,
|
|
2929
|
-
eventId: (0, import_node_crypto4.randomUUID)(),
|
|
2930
|
-
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2931
|
-
environment: telemetry.environment,
|
|
2932
|
-
service: telemetry.service,
|
|
2933
|
-
correlation: {
|
|
2934
|
-
...base,
|
|
2935
|
-
runId: base.runId,
|
|
2936
|
-
workflowId: base.workflowId,
|
|
2937
|
-
...active
|
|
2938
|
-
},
|
|
2939
|
-
type,
|
|
2940
|
-
payload
|
|
2941
|
-
}
|
|
2942
|
-
])
|
|
2943
|
-
).catch(() => {
|
|
2944
|
-
});
|
|
3421
|
+
getReasonTraceAdapter()?.reportError?.(error, options);
|
|
2945
3422
|
} catch {
|
|
2946
3423
|
}
|
|
2947
3424
|
}
|
|
2948
|
-
function workflowCallContent(telemetry, side, value) {
|
|
2949
|
-
const policy = telemetry?.captureContent;
|
|
2950
|
-
if (!(policy === true || policy && typeof policy === "object" && policy[side]))
|
|
2951
|
-
return {};
|
|
2952
|
-
const serialized = JSON.stringify(value);
|
|
2953
|
-
return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
|
|
2954
|
-
}
|
|
2955
3425
|
|
|
2956
3426
|
// src/workflow.ts
|
|
2957
|
-
var
|
|
3427
|
+
var import_node_crypto6 = require("crypto");
|
|
3428
|
+
var import_core8 = require("@kortyx/core");
|
|
3429
|
+
var import_errors8 = require("@kortyx/core/errors");
|
|
3430
|
+
var WorkflowCallError = class extends import_errors8.KortyxError {
|
|
2958
3431
|
constructor(workflow, message, cause, failure) {
|
|
2959
|
-
const detail = (0,
|
|
2960
|
-
failure ?? cause ?? new
|
|
3432
|
+
const detail = (0, import_errors8.serializeFailure)(
|
|
3433
|
+
failure ?? cause ?? new import_errors8.WorkflowContractError(message)
|
|
2961
3434
|
);
|
|
2962
3435
|
super(detail.code, `Workflow '${workflow}': ${message}`, {
|
|
2963
3436
|
...detail,
|
|
@@ -2978,12 +3451,12 @@ function workflowCallFingerprint(value) {
|
|
|
2978
3451
|
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
2979
3452
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
2980
3453
|
if (typeof v !== "object" || !v || seen.has(v)) {
|
|
2981
|
-
throw new
|
|
3454
|
+
throw new import_errors8.WorkflowContractError(
|
|
2982
3455
|
"Workflow input and output must be JSON serializable."
|
|
2983
3456
|
);
|
|
2984
3457
|
}
|
|
2985
3458
|
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
|
|
2986
|
-
throw new
|
|
3459
|
+
throw new import_errors8.WorkflowContractError(
|
|
2987
3460
|
"Workflow input and output must contain plain JSON objects."
|
|
2988
3461
|
);
|
|
2989
3462
|
}
|
|
@@ -3004,7 +3477,7 @@ function createWorkflowHooks(workflows) {
|
|
|
3004
3477
|
useWorkflow: (args) => {
|
|
3005
3478
|
const workflow = workflows[args.workflow];
|
|
3006
3479
|
if (!workflow || workflow.id !== args.workflow)
|
|
3007
|
-
throw new
|
|
3480
|
+
throw new import_errors8.WorkflowContractError(
|
|
3008
3481
|
"Workflow registry keys must match definition ids."
|
|
3009
3482
|
);
|
|
3010
3483
|
return useWorkflow({ ...args, workflow });
|
|
@@ -3018,7 +3491,7 @@ async function useWorkflow(args) {
|
|
|
3018
3491
|
if (task.group) await task.group.ready;
|
|
3019
3492
|
return await runWorkflowCall(args, task);
|
|
3020
3493
|
} catch (error) {
|
|
3021
|
-
if ((0,
|
|
3494
|
+
if ((0, import_core8.isExecutionCancelled)(error) || (0, import_core8.isExecutionLimitReached)(error))
|
|
3022
3495
|
getHookContext().workflowControlError = error;
|
|
3023
3496
|
throw error;
|
|
3024
3497
|
} finally {
|
|
@@ -3027,26 +3500,26 @@ async function useWorkflow(args) {
|
|
|
3027
3500
|
}
|
|
3028
3501
|
async function runWorkflowCall(args, task) {
|
|
3029
3502
|
const ctx = getHookContext();
|
|
3030
|
-
(0,
|
|
3503
|
+
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
3031
3504
|
const service = ctx.node.callWorkflow;
|
|
3032
3505
|
if (!service)
|
|
3033
|
-
throw new
|
|
3506
|
+
throw new import_errors8.WorkflowContractError(
|
|
3034
3507
|
"useWorkflow requires an agent workflow registry."
|
|
3035
3508
|
);
|
|
3036
3509
|
if (!args.id.trim())
|
|
3037
|
-
throw new
|
|
3510
|
+
throw new import_errors8.WorkflowContractError(
|
|
3038
3511
|
"useWorkflow requires a stable, nonempty id."
|
|
3039
3512
|
);
|
|
3040
3513
|
if (ctx.workflowCallActive && !task.group)
|
|
3041
|
-
throw new
|
|
3514
|
+
throw new import_errors8.WorkflowContractError(
|
|
3042
3515
|
"Concurrent workflow calls require parallel([...]); otherwise await each call."
|
|
3043
3516
|
);
|
|
3044
3517
|
if (ctx.workflowCallIds.has(args.id))
|
|
3045
|
-
throw new
|
|
3518
|
+
throw new import_errors8.WorkflowContractError(
|
|
3046
3519
|
`Duplicate workflow call id '${args.id}' in one node activation.`
|
|
3047
3520
|
);
|
|
3048
3521
|
if (ctx.workflowCallIds.size >= 64)
|
|
3049
|
-
throw new
|
|
3522
|
+
throw new import_errors8.WorkflowContractError(
|
|
3050
3523
|
"A node may call at most 64 child workflows per activation."
|
|
3051
3524
|
);
|
|
3052
3525
|
ctx.workflowCallIds.add(args.id);
|
|
@@ -3073,7 +3546,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3073
3546
|
if (!record) ctx.node.consumeExecution?.("maxChildInvocations");
|
|
3074
3547
|
record ??= {
|
|
3075
3548
|
fingerprint,
|
|
3076
|
-
invocationId: (0,
|
|
3549
|
+
invocationId: (0, import_node_crypto6.randomUUID)(),
|
|
3077
3550
|
status: "running",
|
|
3078
3551
|
interrupts: [],
|
|
3079
3552
|
...task.group ? { parallelKey: task.group.key } : {}
|
|
@@ -3083,7 +3556,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3083
3556
|
if (!task.group) ctx.workflowCallActive = true;
|
|
3084
3557
|
const telemetry = ctx.node.workflowCallTelemetry;
|
|
3085
3558
|
const activationKey = "__useWorkflowActivation";
|
|
3086
|
-
ctx.currentNodeState.byKey[activationKey] ??= (0,
|
|
3559
|
+
ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto6.randomUUID)();
|
|
3087
3560
|
const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
|
|
3088
3561
|
const metadata = {
|
|
3089
3562
|
invocationId: record.invocationId,
|
|
@@ -3127,7 +3600,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3127
3600
|
workflow,
|
|
3128
3601
|
record.error ?? "child failed",
|
|
3129
3602
|
void 0,
|
|
3130
|
-
record.failure ?? (0,
|
|
3603
|
+
record.failure ?? (0, import_errors8.serializeFailure)(void 0)
|
|
3131
3604
|
);
|
|
3132
3605
|
if (task.group && record.status === "interrupted" && record.interrupts.at(-1)?.response === void 0)
|
|
3133
3606
|
throw new ParallelChildWaiting();
|
|
@@ -3151,9 +3624,9 @@ async function runWorkflowCall(args, task) {
|
|
|
3151
3624
|
} catch (error) {
|
|
3152
3625
|
const child = error;
|
|
3153
3626
|
recordUsage(child?.__kortyxChildUsage);
|
|
3154
|
-
(0,
|
|
3155
|
-
if ((0,
|
|
3156
|
-
if ((0,
|
|
3627
|
+
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
3628
|
+
if ((0, import_core8.isExecutionCancelled)(error)) throw error;
|
|
3629
|
+
if ((0, import_core8.isExecutionLimitReached)(error)) {
|
|
3157
3630
|
if (child?.__kortyxChildSnapshot)
|
|
3158
3631
|
record.snapshot = child.__kortyxChildSnapshot;
|
|
3159
3632
|
report("suspended", {
|
|
@@ -3163,7 +3636,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3163
3636
|
throw error;
|
|
3164
3637
|
}
|
|
3165
3638
|
record.status = "failed";
|
|
3166
|
-
record.failure = (0,
|
|
3639
|
+
record.failure = (0, import_errors8.serializeFailure)(error);
|
|
3167
3640
|
record.error = record.failure.message;
|
|
3168
3641
|
report("failed", { error: "Child workflow failed" });
|
|
3169
3642
|
throw new WorkflowCallError(
|
|
@@ -3173,12 +3646,15 @@ async function runWorkflowCall(args, task) {
|
|
|
3173
3646
|
record.failure
|
|
3174
3647
|
);
|
|
3175
3648
|
}
|
|
3176
|
-
(0,
|
|
3649
|
+
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
3177
3650
|
recordUsage(outcome.usage);
|
|
3178
3651
|
if (outcome.status === "completed") {
|
|
3179
3652
|
record.data = outcome.data;
|
|
3180
3653
|
workflowCallFingerprint(record.data);
|
|
3181
3654
|
record.status = "completed";
|
|
3655
|
+
record.toolObservations = outcome.toolObservations ?? [];
|
|
3656
|
+
for (const observation of record.toolObservations)
|
|
3657
|
+
rememberToolObservation(observation);
|
|
3182
3658
|
delete record.snapshot;
|
|
3183
3659
|
report(
|
|
3184
3660
|
"completed",
|
|
@@ -3201,8 +3677,12 @@ async function runWorkflowCall(args, task) {
|
|
|
3201
3677
|
});
|
|
3202
3678
|
}
|
|
3203
3679
|
}
|
|
3204
|
-
if (cached)
|
|
3680
|
+
if (cached) {
|
|
3205
3681
|
report("reused", workflowCallContent(telemetry, "output", record.data));
|
|
3682
|
+
if (record.restoredToolBranch !== telemetry?.correlation?.branchId)
|
|
3683
|
+
await replayToolObservations(record.toolObservations ?? [], true);
|
|
3684
|
+
delete record.restoredToolBranch;
|
|
3685
|
+
}
|
|
3206
3686
|
const data = record.data ?? {};
|
|
3207
3687
|
return {
|
|
3208
3688
|
data: JSON.parse(JSON.stringify(data))
|
|
@@ -3219,6 +3699,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3219
3699
|
createWorkflowHooks,
|
|
3220
3700
|
emitWorkflowCall,
|
|
3221
3701
|
parallel,
|
|
3702
|
+
reportError,
|
|
3222
3703
|
runWithHookContext,
|
|
3223
3704
|
useAbortSignal,
|
|
3224
3705
|
useInterrupt,
|
|
@@ -3226,6 +3707,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3226
3707
|
useReason,
|
|
3227
3708
|
useRuntimeContext,
|
|
3228
3709
|
useStructuredData,
|
|
3710
|
+
useTool,
|
|
3229
3711
|
useWorkflow,
|
|
3230
3712
|
useWorkflowState,
|
|
3231
3713
|
workflowCallFingerprint
|