@kortyx/hooks 0.25.3 → 0.26.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 +20 -0
- package/dist/index.d.ts +15 -4
- package/dist/index.js +911 -466
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +18 -3
- package/dist/internal.js +150 -5
- package/dist/internal.js.map +1 -1
- package/dist/{tracing-BI6YKHSf.d.ts → tracing-BUpf8RrV.d.ts} +7 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ __export(index_exports, {
|
|
|
32
32
|
useReason: () => useReason2,
|
|
33
33
|
useRuntimeContext: () => useRuntimeContext,
|
|
34
34
|
useStructuredData: () => useStructuredData,
|
|
35
|
+
useTool: () => useTool,
|
|
35
36
|
useWorkflow: () => useWorkflow,
|
|
36
37
|
useWorkflowState: () => useWorkflowState,
|
|
37
38
|
workflowCallFingerprint: () => workflowCallFingerprint
|
|
@@ -99,6 +100,7 @@ var createInternalContext = (ctx) => {
|
|
|
99
100
|
workflowContextClosed: false,
|
|
100
101
|
nodeStateIndex: 0,
|
|
101
102
|
reasonCallIndex: 0,
|
|
103
|
+
toolCallIndex: 0,
|
|
102
104
|
currentNodeState,
|
|
103
105
|
workflowState: cloneWorkflowState(hookRuntimeState.workflowState),
|
|
104
106
|
tokenUsage: cloneTokenUsage(ctx.state.runtime?.tokenUsage),
|
|
@@ -189,6 +191,12 @@ async function runWithHookContext(ctx, fn) {
|
|
|
189
191
|
}
|
|
190
192
|
});
|
|
191
193
|
cleanupCompletedReasonCheckpoints(internal);
|
|
194
|
+
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
195
|
+
if (key.startsWith("__useTool:")) {
|
|
196
|
+
delete internal.currentNodeState.byKey[key];
|
|
197
|
+
internal.stateDirty = true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
192
200
|
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
193
201
|
if (key.startsWith("__useWorkflow:") || key.startsWith("__parallel:") || key === "__useWorkflowActivation") {
|
|
194
202
|
internal.currentNodeState.byKey[key] = null;
|
|
@@ -341,7 +349,87 @@ var awaitInterruptInternal = (args) => {
|
|
|
341
349
|
};
|
|
342
350
|
|
|
343
351
|
// src/reason/use-reason.ts
|
|
344
|
-
var
|
|
352
|
+
var import_core6 = require("@kortyx/core");
|
|
353
|
+
|
|
354
|
+
// src/safe-tracing.ts
|
|
355
|
+
function safeSpan(span) {
|
|
356
|
+
const guard = (fn) => (...args) => {
|
|
357
|
+
try {
|
|
358
|
+
fn?.(...args);
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
let ended = false;
|
|
363
|
+
const finish = (fn) => (...args) => {
|
|
364
|
+
if (ended) return;
|
|
365
|
+
ended = true;
|
|
366
|
+
guard(fn)(...args);
|
|
367
|
+
};
|
|
368
|
+
return {
|
|
369
|
+
addEvent: guard(
|
|
370
|
+
(name, attributes) => span?.addEvent?.(name, attributes)
|
|
371
|
+
),
|
|
372
|
+
setAttributes: guard(
|
|
373
|
+
(attributes) => span?.setAttributes?.(attributes)
|
|
374
|
+
),
|
|
375
|
+
end: finish((args) => span?.end?.(args)),
|
|
376
|
+
fail: finish((error, args) => {
|
|
377
|
+
if (span?.fail) span.fail(error, args);
|
|
378
|
+
else span?.end?.(args);
|
|
379
|
+
})
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function safeStartSpan(adapter, args) {
|
|
383
|
+
try {
|
|
384
|
+
return safeSpan(adapter?.startSpan?.(args));
|
|
385
|
+
} catch {
|
|
386
|
+
return safeSpan(void 0);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
async function withSafeTraceSpan(adapter, args, fn) {
|
|
390
|
+
let execution;
|
|
391
|
+
const invoke = (span) => {
|
|
392
|
+
execution ??= Promise.resolve().then(async () => {
|
|
393
|
+
const observed = safeSpan(span);
|
|
394
|
+
try {
|
|
395
|
+
const result = await fn(observed);
|
|
396
|
+
observed.end?.();
|
|
397
|
+
return result;
|
|
398
|
+
} catch (error) {
|
|
399
|
+
observed.fail?.(error);
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
return execution;
|
|
404
|
+
};
|
|
405
|
+
let activeScope;
|
|
406
|
+
try {
|
|
407
|
+
activeScope = adapter?.withSpan?.bind(adapter);
|
|
408
|
+
} catch {
|
|
409
|
+
}
|
|
410
|
+
if (activeScope) {
|
|
411
|
+
let started;
|
|
412
|
+
const invoked = new Promise((resolve) => {
|
|
413
|
+
started = resolve;
|
|
414
|
+
});
|
|
415
|
+
const observe = async () => {
|
|
416
|
+
try {
|
|
417
|
+
await activeScope(args, (span) => {
|
|
418
|
+
const result = invoke(span);
|
|
419
|
+
started?.();
|
|
420
|
+
return result;
|
|
421
|
+
});
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
const observation = observe();
|
|
426
|
+
if (!execution) await Promise.race([invoked, observation]);
|
|
427
|
+
} else {
|
|
428
|
+
await invoke(safeStartSpan(adapter, args)).catch(() => {
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
return execution ?? invoke(safeSpan(void 0));
|
|
432
|
+
}
|
|
345
433
|
|
|
346
434
|
// src/structured.ts
|
|
347
435
|
var import_node_crypto = require("crypto");
|
|
@@ -435,18 +523,392 @@ var emitStructuredData = (args) => {
|
|
|
435
523
|
var shouldEmitStructured = (cfg) => Boolean(cfg);
|
|
436
524
|
var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
|
|
437
525
|
|
|
438
|
-
// src/
|
|
526
|
+
// src/tool.ts
|
|
527
|
+
var import_node_async_hooks2 = require("async_hooks");
|
|
439
528
|
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
529
|
var import_core2 = require("@kortyx/core");
|
|
530
|
+
var import_errors3 = require("@kortyx/core/errors");
|
|
531
|
+
|
|
532
|
+
// src/error-diagnostics.ts
|
|
445
533
|
var import_errors2 = require("@kortyx/core/errors");
|
|
534
|
+
function errorDiagnostics(error, project) {
|
|
535
|
+
try {
|
|
536
|
+
const name = (0, import_errors2.errorProperty)(error, "name") ?? (error instanceof Error ? error.name : void 0);
|
|
537
|
+
const message = (0, import_errors2.errorProperty)(error, "message");
|
|
538
|
+
const details = project ? project(error) : {
|
|
539
|
+
type: typeof name === "string" ? name : "Error",
|
|
540
|
+
message: typeof error === "string" ? error : typeof message === "string" ? message : "Execution reported a fault."
|
|
541
|
+
};
|
|
542
|
+
if (!details) return {};
|
|
543
|
+
const projectedMessage = details.message;
|
|
544
|
+
const projectedType = details.type;
|
|
545
|
+
if (typeof projectedMessage !== "string") return {};
|
|
546
|
+
return {
|
|
547
|
+
errorMessage: projectedMessage.slice(0, 8192),
|
|
548
|
+
...typeof projectedType === "string" ? { errorType: projectedType.slice(0, 256) } : {}
|
|
549
|
+
};
|
|
550
|
+
} catch {
|
|
551
|
+
return {};
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// src/workflow-telemetry.ts
|
|
556
|
+
var import_node_crypto2 = require("crypto");
|
|
557
|
+
function emitWorkflowCall(telemetry, type, payload) {
|
|
558
|
+
const base = telemetry?.correlation;
|
|
559
|
+
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
|
|
560
|
+
return;
|
|
561
|
+
try {
|
|
562
|
+
const active = telemetry.trace?.getActiveContext?.();
|
|
563
|
+
void Promise.resolve(
|
|
564
|
+
telemetry.reporter.emit([
|
|
565
|
+
{
|
|
566
|
+
schemaVersion: 1,
|
|
567
|
+
eventId: (0, import_node_crypto2.randomUUID)(),
|
|
568
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
569
|
+
environment: telemetry.environment,
|
|
570
|
+
service: telemetry.service,
|
|
571
|
+
correlation: {
|
|
572
|
+
...base,
|
|
573
|
+
runId: base.runId,
|
|
574
|
+
workflowId: base.workflowId,
|
|
575
|
+
...active
|
|
576
|
+
},
|
|
577
|
+
type,
|
|
578
|
+
payload
|
|
579
|
+
}
|
|
580
|
+
])
|
|
581
|
+
).catch(() => {
|
|
582
|
+
});
|
|
583
|
+
} catch {
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
function workflowCallContent(telemetry, side, value) {
|
|
587
|
+
const policy = telemetry?.captureContent;
|
|
588
|
+
if (!(policy === true || policy && typeof policy === "object" && policy[side]))
|
|
589
|
+
return {};
|
|
590
|
+
const serialized = JSON.stringify(value);
|
|
591
|
+
return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/tool.ts
|
|
595
|
+
var tools = new import_node_async_hooks2.AsyncLocalStorage();
|
|
596
|
+
function classification(tool, value, thrown, policy = tool.outcomes) {
|
|
597
|
+
const fallback = {
|
|
598
|
+
outcome: thrown ? "fault" : "success"
|
|
599
|
+
};
|
|
600
|
+
try {
|
|
601
|
+
if (value && typeof value === "object" && "isError" in value && value.isError === true)
|
|
602
|
+
fallback.outcome = "fault";
|
|
603
|
+
const result = thrown ? policy?.classifyError?.(value) : policy?.classifyResult?.(value);
|
|
604
|
+
if (!result || !["success", "denied", "fault"].includes(result.outcome))
|
|
605
|
+
return fallback;
|
|
606
|
+
if (result.outcome !== "denied")
|
|
607
|
+
return thrown || fallback.outcome === "fault" ? fallback : { outcome: result.outcome };
|
|
608
|
+
const code = result.code;
|
|
609
|
+
return {
|
|
610
|
+
outcome: "denied",
|
|
611
|
+
code: typeof code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(code) && policy?.denialCodes?.includes(code) ? code : "DENIED"
|
|
612
|
+
};
|
|
613
|
+
} catch {
|
|
614
|
+
return fallback;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
async function closeOwnedTools(owned) {
|
|
618
|
+
const closers = /* @__PURE__ */ new Map();
|
|
619
|
+
for (const tool of owned) {
|
|
620
|
+
if (tool.closeAfterUse !== false && tool.close && !closers.has(tool.close))
|
|
621
|
+
closers.set(tool.close, tool);
|
|
622
|
+
}
|
|
623
|
+
await Promise.all(
|
|
624
|
+
[...closers].map(async ([close, tool]) => {
|
|
625
|
+
try {
|
|
626
|
+
await close.call(tool);
|
|
627
|
+
} catch {
|
|
628
|
+
const ctx = getHookContext();
|
|
629
|
+
try {
|
|
630
|
+
ctx.reasonTrace?.startSpan({
|
|
631
|
+
name: "kortyx.tool.cleanup",
|
|
632
|
+
attributes: { cleanupFailed: true }
|
|
633
|
+
})?.end?.();
|
|
634
|
+
} catch {
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
})
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
function captureToolError(observation, error, policy, returned = false) {
|
|
641
|
+
try {
|
|
642
|
+
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;
|
|
643
|
+
Object.assign(
|
|
644
|
+
observation,
|
|
645
|
+
errorDiagnostics(policy?.error ? error : reported, policy?.error)
|
|
646
|
+
);
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
function ownership() {
|
|
651
|
+
const ctx = getHookContext();
|
|
652
|
+
const correlation = ctx.node.workflowCallTelemetry?.correlation;
|
|
653
|
+
return {
|
|
654
|
+
workflowId: ctx.node.graph.name,
|
|
655
|
+
nodeId: ctx.node.graph.node,
|
|
656
|
+
...correlation?.workflowRevisionId ? { workflowRevisionId: correlation.workflowRevisionId } : {},
|
|
657
|
+
...correlation?.parentInvocationId ? { parentInvocationId: correlation.parentInvocationId } : {},
|
|
658
|
+
...correlation?.runId ? { runId: correlation.runId } : {},
|
|
659
|
+
...correlation?.invocationId ? { invocationId: correlation.invocationId } : {},
|
|
660
|
+
...correlation?.branchId ? { branchId: correlation.branchId } : {}
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function rememberToolObservation(observation) {
|
|
664
|
+
const ctx = getHookContext();
|
|
665
|
+
const existing = ctx.workflowState.__kortyxToolObservations;
|
|
666
|
+
const observations = Array.isArray(existing) ? existing : [];
|
|
667
|
+
if (!observations.some((item) => item.attemptId === observation.attemptId))
|
|
668
|
+
observations.push({ ...observation });
|
|
669
|
+
ctx.workflowState.__kortyxToolObservations = observations;
|
|
670
|
+
ctx.stateDirty = true;
|
|
671
|
+
}
|
|
672
|
+
function toolTrace(observation) {
|
|
673
|
+
const ctx = getHookContext();
|
|
674
|
+
const adapter = ctx.reasonTrace ?? ctx.node.workflowCallTelemetry?.trace;
|
|
675
|
+
if (adapter) return adapter;
|
|
676
|
+
const telemetry = ctx.node.workflowCallTelemetry;
|
|
677
|
+
if (!telemetry?.reporter) return void 0;
|
|
678
|
+
const correlation = { ...telemetry.correlation, ...observation };
|
|
679
|
+
const spanId = (0, import_node_crypto3.randomUUID)();
|
|
680
|
+
const emit = (type, payload) => emitWorkflowCall(
|
|
681
|
+
{
|
|
682
|
+
...telemetry,
|
|
683
|
+
correlation,
|
|
684
|
+
trace: {
|
|
685
|
+
startSpan: () => void 0,
|
|
686
|
+
getActiveContext: () => ({
|
|
687
|
+
traceId: correlation.runId ?? spanId,
|
|
688
|
+
spanId
|
|
689
|
+
})
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
type,
|
|
693
|
+
payload
|
|
694
|
+
);
|
|
695
|
+
return {
|
|
696
|
+
startSpan: ({ attributes = {} }) => {
|
|
697
|
+
if (attributes.executed === true) emit("tool.started", attributes);
|
|
698
|
+
let ended = false;
|
|
699
|
+
return {
|
|
700
|
+
end: (args) => {
|
|
701
|
+
if (ended) return;
|
|
702
|
+
ended = true;
|
|
703
|
+
const payload = { ...attributes, ...args?.attributes };
|
|
704
|
+
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";
|
|
705
|
+
emit(type, payload);
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
async function executeObservedTool(args) {
|
|
712
|
+
const ctx = getHookContext();
|
|
713
|
+
const observation = {
|
|
714
|
+
version: 1,
|
|
715
|
+
name: args.tool.name,
|
|
716
|
+
toolCallId: args.toolCallId,
|
|
717
|
+
attemptId: (0, import_node_crypto3.randomUUID)(),
|
|
718
|
+
callingMode: args.callingMode,
|
|
719
|
+
executed: false,
|
|
720
|
+
...ownership()
|
|
721
|
+
};
|
|
722
|
+
const scope = {
|
|
723
|
+
tool: args.tool,
|
|
724
|
+
input: args.input,
|
|
725
|
+
signal: args.abortSignal,
|
|
726
|
+
mode: args.callingMode,
|
|
727
|
+
providerToolCallId: args.providerToolCallId ?? args.toolCallId,
|
|
728
|
+
adopted: false
|
|
729
|
+
};
|
|
730
|
+
try {
|
|
731
|
+
(0, import_core2.throwIfExecutionAborted)(args.abortSignal);
|
|
732
|
+
} catch (error) {
|
|
733
|
+
observation.outcome = "cancelled";
|
|
734
|
+
await observeToolFact(observation);
|
|
735
|
+
args.onObservation?.(observation);
|
|
736
|
+
throw error;
|
|
737
|
+
}
|
|
738
|
+
ctx.node.consumeExecution?.("maxToolCalls");
|
|
739
|
+
observation.executed = true;
|
|
740
|
+
const started = performance.now();
|
|
741
|
+
return withSafeTraceSpan(
|
|
742
|
+
toolTrace(observation),
|
|
743
|
+
{
|
|
744
|
+
name: "kortyx.tool",
|
|
745
|
+
attributes: { ...observation, nodeId: ctx.node.graph.node }
|
|
746
|
+
},
|
|
747
|
+
async (span) => tools.run(scope, async () => {
|
|
748
|
+
try {
|
|
749
|
+
const result = await args.tool.execute(args.input, {
|
|
750
|
+
toolCallId: args.providerToolCallId ?? args.toolCallId,
|
|
751
|
+
...args.abortSignal ? { abortSignal: args.abortSignal } : {}
|
|
752
|
+
});
|
|
753
|
+
(0, import_core2.throwIfExecutionAborted)(args.abortSignal);
|
|
754
|
+
const outcome = classification(
|
|
755
|
+
args.tool,
|
|
756
|
+
result,
|
|
757
|
+
false,
|
|
758
|
+
scope.classify ?? args.tool.outcomes
|
|
759
|
+
);
|
|
760
|
+
observation.outcome = outcome.outcome;
|
|
761
|
+
if (outcome.code) observation.denialCode = outcome.code;
|
|
762
|
+
if (observation.outcome === "fault") {
|
|
763
|
+
captureToolError(
|
|
764
|
+
observation,
|
|
765
|
+
result,
|
|
766
|
+
scope.telemetry ?? args.tool.telemetry,
|
|
767
|
+
true
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
return result;
|
|
771
|
+
} catch (error) {
|
|
772
|
+
if (args.abortSignal?.aborted) observation.outcome = "cancelled";
|
|
773
|
+
else if (!(0, import_errors3.isControlFlowError)(error)) {
|
|
774
|
+
const outcome = classification(
|
|
775
|
+
args.tool,
|
|
776
|
+
error,
|
|
777
|
+
true,
|
|
778
|
+
scope.classify ?? args.tool.outcomes
|
|
779
|
+
);
|
|
780
|
+
observation.outcome = outcome.outcome;
|
|
781
|
+
if (outcome.code) observation.denialCode = outcome.code;
|
|
782
|
+
if (observation.outcome === "fault")
|
|
783
|
+
captureToolError(
|
|
784
|
+
observation,
|
|
785
|
+
error,
|
|
786
|
+
scope.telemetry ?? args.tool.telemetry
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
throw error;
|
|
790
|
+
} finally {
|
|
791
|
+
observation.durationMs = Math.max(0, performance.now() - started);
|
|
792
|
+
span.end?.({
|
|
793
|
+
attributes: {
|
|
794
|
+
...observation,
|
|
795
|
+
...observation.outcome ? {} : { suspended: true }
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
rememberToolObservation(observation);
|
|
799
|
+
args.onObservation?.({ ...observation });
|
|
800
|
+
}
|
|
801
|
+
})
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
async function observeToolFact(observation, kind, preserveOwner = false) {
|
|
805
|
+
const ctx = getHookContext();
|
|
806
|
+
if (!preserveOwner) Object.assign(observation, ownership());
|
|
807
|
+
if (observation.outcome && kind !== "reused")
|
|
808
|
+
rememberToolObservation(observation);
|
|
809
|
+
await withSafeTraceSpan(
|
|
810
|
+
toolTrace(observation),
|
|
811
|
+
{
|
|
812
|
+
name: "kortyx.tool",
|
|
813
|
+
attributes: {
|
|
814
|
+
...observation,
|
|
815
|
+
executed: false,
|
|
816
|
+
...kind ? { observationKind: kind } : {},
|
|
817
|
+
nodeId: observation.nodeId ?? ctx.node.graph.node
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
async (span) => {
|
|
821
|
+
span.end?.({
|
|
822
|
+
attributes: {
|
|
823
|
+
...observation,
|
|
824
|
+
executed: false,
|
|
825
|
+
...kind ? { observationKind: kind } : {}
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
async function useTool(args) {
|
|
832
|
+
const ctx = getHookContext();
|
|
833
|
+
const scope = tools.getStore();
|
|
834
|
+
if (scope?.mode === "model" && !scope.adopted && scope.tool.name === args.tool.name && scope.input === args.input) {
|
|
835
|
+
scope.adopted = true;
|
|
836
|
+
scope.classify = args.tool.outcomes ?? scope.classify;
|
|
837
|
+
scope.telemetry = args.tool.telemetry ?? scope.telemetry;
|
|
838
|
+
return tools.run(
|
|
839
|
+
{ ...scope, mode: "direct" },
|
|
840
|
+
() => args.tool.execute(args.input, {
|
|
841
|
+
toolCallId: scope.providerToolCallId,
|
|
842
|
+
...scope.signal ? { abortSignal: scope.signal } : {}
|
|
843
|
+
})
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
const index = ctx.toolCallIndex++;
|
|
847
|
+
const key = `__useTool:${args.id ?? `auto:${index}`}`;
|
|
848
|
+
let id = ctx.currentNodeState.byKey[key];
|
|
849
|
+
if (typeof id !== "string") {
|
|
850
|
+
id = (0, import_node_crypto3.randomUUID)();
|
|
851
|
+
ctx.currentNodeState.byKey[key] = id;
|
|
852
|
+
ctx.stateDirty = true;
|
|
853
|
+
}
|
|
854
|
+
try {
|
|
855
|
+
return await executeObservedTool({
|
|
856
|
+
tool: args.tool,
|
|
857
|
+
input: args.input,
|
|
858
|
+
toolCallId: String(id),
|
|
859
|
+
callingMode: "direct",
|
|
860
|
+
abortSignal: (0, import_core2.combineAbortSignals)(ctx.node.abortSignal, args.abortSignal)
|
|
861
|
+
});
|
|
862
|
+
} finally {
|
|
863
|
+
await closeOwnedTools([args.tool]);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
async function replayToolObservations(observations, child = false) {
|
|
867
|
+
for (const original of observations) {
|
|
868
|
+
const source = original.source ?? {
|
|
869
|
+
...ownershipFrom(original),
|
|
870
|
+
toolCallId: original.toolCallId,
|
|
871
|
+
attemptId: original.attemptId
|
|
872
|
+
};
|
|
873
|
+
await observeToolFact(
|
|
874
|
+
{
|
|
875
|
+
...original,
|
|
876
|
+
...ownership(),
|
|
877
|
+
...child ? {
|
|
878
|
+
workflowId: original.workflowId,
|
|
879
|
+
nodeId: original.nodeId,
|
|
880
|
+
workflowRevisionId: original.workflowRevisionId,
|
|
881
|
+
invocationId: original.invocationId,
|
|
882
|
+
parentInvocationId: original.parentInvocationId
|
|
883
|
+
} : {},
|
|
884
|
+
attemptId: (0, import_node_crypto3.randomUUID)(),
|
|
885
|
+
executed: false,
|
|
886
|
+
source,
|
|
887
|
+
durationMs: void 0
|
|
888
|
+
},
|
|
889
|
+
"reused",
|
|
890
|
+
child
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
var ownershipFrom = (observation) => ({
|
|
895
|
+
runId: observation.runId,
|
|
896
|
+
invocationId: observation.invocationId,
|
|
897
|
+
branchId: observation.branchId
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
// src/reason/engine.ts
|
|
901
|
+
var import_node_crypto5 = require("crypto");
|
|
902
|
+
var import_core4 = require("@kortyx/core");
|
|
903
|
+
|
|
904
|
+
// src/reason-engine.ts
|
|
905
|
+
var import_node_crypto4 = require("crypto");
|
|
906
|
+
var import_core3 = require("@kortyx/core");
|
|
907
|
+
var import_errors4 = require("@kortyx/core/errors");
|
|
446
908
|
function assertModelFinish(result) {
|
|
447
909
|
if (result.finishReason?.unified !== "content-filter") return;
|
|
448
910
|
throw Object.assign(
|
|
449
|
-
new
|
|
911
|
+
new import_errors4.KortyxError("PROVIDER_REFUSAL", "The provider refused the request.", {
|
|
450
912
|
category: "provider",
|
|
451
913
|
retryable: false,
|
|
452
914
|
safeMessage: "The provider refused the request."
|
|
@@ -460,7 +922,7 @@ function assertModelFinish(result) {
|
|
|
460
922
|
var toError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
461
923
|
var createRuntimeId = () => {
|
|
462
924
|
try {
|
|
463
|
-
return (0,
|
|
925
|
+
return (0, import_node_crypto4.randomUUID)();
|
|
464
926
|
} catch {
|
|
465
927
|
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
466
928
|
}
|
|
@@ -495,8 +957,8 @@ async function runReasonEngine(args) {
|
|
|
495
957
|
args.model.options?.providerOptions,
|
|
496
958
|
args.providerOptions
|
|
497
959
|
);
|
|
498
|
-
const
|
|
499
|
-
(0,
|
|
960
|
+
const tools2 = args.tools ?? args.model.options?.tools;
|
|
961
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
500
962
|
const model = args.model.provider.getModel(args.model.modelId, {
|
|
501
963
|
...temperature !== void 0 ? { temperature } : {},
|
|
502
964
|
streaming: stream,
|
|
@@ -505,10 +967,10 @@ async function runReasonEngine(args) {
|
|
|
505
967
|
...abortSignal !== void 0 ? { abortSignal } : {},
|
|
506
968
|
...reasoning !== void 0 ? { reasoning } : {},
|
|
507
969
|
...responseFormat !== void 0 ? { responseFormat } : {},
|
|
508
|
-
...
|
|
970
|
+
...tools2 !== void 0 ? { tools: tools2 } : {},
|
|
509
971
|
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
510
972
|
});
|
|
511
|
-
if (
|
|
973
|
+
if (tools2?.length && !model.supportsToolStreaming) stream = false;
|
|
512
974
|
const messages = args.messages !== void 0 ? args.messages : [
|
|
513
975
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
514
976
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -520,7 +982,7 @@ async function runReasonEngine(args) {
|
|
|
520
982
|
opId,
|
|
521
983
|
segmentId
|
|
522
984
|
};
|
|
523
|
-
const traceSpan = args.reasonTrace
|
|
985
|
+
const traceSpan = safeStartSpan(args.reasonTrace, {
|
|
524
986
|
name: "runReasonEngine",
|
|
525
987
|
attributes: {
|
|
526
988
|
...commonMeta,
|
|
@@ -600,7 +1062,7 @@ async function runReasonEngine(args) {
|
|
|
600
1062
|
}
|
|
601
1063
|
const response2 = await model.stream(messages);
|
|
602
1064
|
for await (const chunk of response2) {
|
|
603
|
-
(0,
|
|
1065
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
604
1066
|
switch (chunk.type) {
|
|
605
1067
|
case "text-delta": {
|
|
606
1068
|
if (chunk.delta.length === 0) break;
|
|
@@ -666,7 +1128,7 @@ async function runReasonEngine(args) {
|
|
|
666
1128
|
}
|
|
667
1129
|
}
|
|
668
1130
|
}
|
|
669
|
-
(0,
|
|
1131
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
670
1132
|
assertModelFinish({ finishReason, usage });
|
|
671
1133
|
if (emit) {
|
|
672
1134
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
@@ -683,12 +1145,12 @@ async function runReasonEngine(args) {
|
|
|
683
1145
|
...providerMetadata !== void 0 ? { providerMetadata } : {},
|
|
684
1146
|
...warnings !== void 0 ? { warnings } : {}
|
|
685
1147
|
};
|
|
686
|
-
(0,
|
|
1148
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
687
1149
|
endTrace(traceSpan, result2);
|
|
688
1150
|
return result2;
|
|
689
1151
|
}
|
|
690
1152
|
const response = await model.invoke(messages);
|
|
691
|
-
(0,
|
|
1153
|
+
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
692
1154
|
assertModelFinish(response);
|
|
693
1155
|
if (response.content) args.onTextChunk?.(response.content);
|
|
694
1156
|
if (emit) {
|
|
@@ -718,7 +1180,7 @@ async function runReasonEngine(args) {
|
|
|
718
1180
|
endTrace(traceSpan, result);
|
|
719
1181
|
return result;
|
|
720
1182
|
} catch (error) {
|
|
721
|
-
const failure = abortSignal?.aborted ? (0,
|
|
1183
|
+
const failure = abortSignal?.aborted ? (0, import_core3.createExecutionCancelledError)() : error;
|
|
722
1184
|
failTrace(traceSpan, failure);
|
|
723
1185
|
throw failure;
|
|
724
1186
|
}
|
|
@@ -727,19 +1189,19 @@ async function runReasonEngine(args) {
|
|
|
727
1189
|
// src/reason/engine.ts
|
|
728
1190
|
var createRuntimeId2 = () => {
|
|
729
1191
|
try {
|
|
730
|
-
return (0,
|
|
1192
|
+
return (0, import_node_crypto5.randomUUID)();
|
|
731
1193
|
} catch {
|
|
732
1194
|
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
733
1195
|
}
|
|
734
1196
|
};
|
|
735
1197
|
async function reasonEngine(args, meta, inputOverride) {
|
|
736
1198
|
const ctx = getHookContext();
|
|
737
|
-
const abortSignal = (0,
|
|
1199
|
+
const abortSignal = (0, import_core4.combineAbortSignals)(
|
|
738
1200
|
ctx.node.abortSignal,
|
|
739
1201
|
args.abortSignal,
|
|
740
1202
|
args.model.options?.abortSignal
|
|
741
1203
|
);
|
|
742
|
-
(0,
|
|
1204
|
+
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
743
1205
|
ctx.node.consumeExecution?.("maxModelPasses");
|
|
744
1206
|
try {
|
|
745
1207
|
return await runReasonEngine({
|
|
@@ -1489,7 +1951,7 @@ function createStructuredOutputStreamer(args, id, opId) {
|
|
|
1489
1951
|
}
|
|
1490
1952
|
|
|
1491
1953
|
// src/reason/parsing.ts
|
|
1492
|
-
var
|
|
1954
|
+
var import_errors5 = require("@kortyx/core/errors");
|
|
1493
1955
|
var tryJsonParse = (value) => {
|
|
1494
1956
|
try {
|
|
1495
1957
|
return { ok: true, value: JSON.parse(value) };
|
|
@@ -1527,12 +1989,12 @@ var resolveOutputCandidate = (text) => {
|
|
|
1527
1989
|
}
|
|
1528
1990
|
return text;
|
|
1529
1991
|
};
|
|
1530
|
-
var createTruncatedStructuredOutputError = (label, cause) => new
|
|
1992
|
+
var createTruncatedStructuredOutputError = (label, cause) => new import_errors5.ValidationError(
|
|
1531
1993
|
"OUTPUT_TRUNCATED",
|
|
1532
1994
|
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`,
|
|
1533
1995
|
cause
|
|
1534
1996
|
);
|
|
1535
|
-
var createInvalidStructuredOutputError = (label, cause) => new
|
|
1997
|
+
var createInvalidStructuredOutputError = (label, cause) => new import_errors5.ValidationError(
|
|
1536
1998
|
"INVALID_MODEL_JSON",
|
|
1537
1999
|
`${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
2000
|
cause
|
|
@@ -1544,7 +2006,7 @@ var parseReasonOutputWithSchema = (args) => {
|
|
|
1544
2006
|
try {
|
|
1545
2007
|
return parseWithSchema(args.schema, candidate, args.label);
|
|
1546
2008
|
} catch (error) {
|
|
1547
|
-
if (!(error instanceof
|
|
2009
|
+
if (!(error instanceof import_errors5.ValidationError)) throw error;
|
|
1548
2010
|
const failure = args.finishReason?.unified === "length" ? createTruncatedStructuredOutputError(args.label, error) : typeof candidate === "string" && !validJson ? createInvalidStructuredOutputError(args.label, error) : error;
|
|
1549
2011
|
if (failure && typeof failure === "object")
|
|
1550
2012
|
Object.assign(failure, {
|
|
@@ -1564,7 +2026,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1564
2026
|
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1565
2027
|
throw createInvalidStructuredOutputError("useReason output");
|
|
1566
2028
|
}
|
|
1567
|
-
throw new
|
|
2029
|
+
throw new import_errors5.ValidationError(
|
|
1568
2030
|
"MODEL_OUTPUT_SCHEMA",
|
|
1569
2031
|
"useReason first pass with interrupt must return a JSON object."
|
|
1570
2032
|
);
|
|
@@ -1573,7 +2035,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1573
2035
|
let interruptRequired = true;
|
|
1574
2036
|
if (mode === "optional") {
|
|
1575
2037
|
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
1576
|
-
throw new
|
|
2038
|
+
throw new import_errors5.ValidationError(
|
|
1577
2039
|
"MODEL_OUTPUT_SCHEMA",
|
|
1578
2040
|
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
1579
2041
|
);
|
|
@@ -1586,7 +2048,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1586
2048
|
candidate.interrupt
|
|
1587
2049
|
].some((value) => value !== null && value !== void 0);
|
|
1588
2050
|
if (hasUnexpectedRequest) {
|
|
1589
|
-
throw new
|
|
2051
|
+
throw new import_errors5.ValidationError(
|
|
1590
2052
|
"MODEL_OUTPUT_SCHEMA",
|
|
1591
2053
|
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
1592
2054
|
);
|
|
@@ -1620,7 +2082,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1620
2082
|
"useReason output"
|
|
1621
2083
|
);
|
|
1622
2084
|
} catch (error) {
|
|
1623
|
-
if (!(error instanceof
|
|
2085
|
+
if (!(error instanceof import_errors5.ValidationError)) throw error;
|
|
1624
2086
|
if (args.finishReason?.unified === "length") {
|
|
1625
2087
|
throw createTruncatedStructuredOutputError("useReason output", error);
|
|
1626
2088
|
}
|
|
@@ -1629,7 +2091,7 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
1629
2091
|
}
|
|
1630
2092
|
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
2093
|
if (!args.outputSchema && !explicitDraft) {
|
|
1632
|
-
throw new
|
|
2094
|
+
throw new import_errors5.ValidationError(
|
|
1633
2095
|
"MODEL_OUTPUT_SCHEMA",
|
|
1634
2096
|
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
1635
2097
|
);
|
|
@@ -1819,6 +2281,7 @@ var mergeUsage = (left, right) => {
|
|
|
1819
2281
|
const reasoning = sum(left.reasoning, right.reasoning);
|
|
1820
2282
|
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
1821
2283
|
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
2284
|
+
const cacheWrite1h = sum(left.cacheWrite1h, right.cacheWrite1h);
|
|
1822
2285
|
const merged = {
|
|
1823
2286
|
...right.outputIncludesReasoning !== void 0 ? { outputIncludesReasoning: right.outputIncludesReasoning } : {},
|
|
1824
2287
|
...right.inputIncludesCacheWrite !== void 0 ? { inputIncludesCacheWrite: right.inputIncludesCacheWrite } : {},
|
|
@@ -1830,6 +2293,7 @@ var mergeUsage = (left, right) => {
|
|
|
1830
2293
|
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1831
2294
|
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1832
2295
|
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
2296
|
+
if (cacheWrite1h !== void 0) merged.cacheWrite1h = cacheWrite1h;
|
|
1833
2297
|
if (raw) merged.raw = raw;
|
|
1834
2298
|
return merged;
|
|
1835
2299
|
};
|
|
@@ -1868,9 +2332,9 @@ var emitReasonStructuredOutput = (args) => {
|
|
|
1868
2332
|
};
|
|
1869
2333
|
|
|
1870
2334
|
// src/reason/tools.ts
|
|
1871
|
-
var
|
|
1872
|
-
var
|
|
1873
|
-
var toToolDefinitions = (
|
|
2335
|
+
var import_core5 = require("@kortyx/core");
|
|
2336
|
+
var import_errors6 = require("@kortyx/core/errors");
|
|
2337
|
+
var toToolDefinitions = (tools2) => tools2.map((tool) => ({
|
|
1874
2338
|
name: tool.name,
|
|
1875
2339
|
...tool.title ? { title: tool.title } : {},
|
|
1876
2340
|
...tool.description ? { description: tool.description } : {},
|
|
@@ -1887,12 +2351,13 @@ var resolveToolExecutionBoolean = (value, toolName, fallback) => {
|
|
|
1887
2351
|
return fallback;
|
|
1888
2352
|
};
|
|
1889
2353
|
var normalizeToolResult = (toolCall, value) => {
|
|
1890
|
-
if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value) {
|
|
1891
|
-
|
|
2354
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value && typeof value.content === "string") {
|
|
2355
|
+
const result = value;
|
|
2356
|
+
return { ...result, toolCallId: toolCall.id, name: toolCall.name };
|
|
1892
2357
|
}
|
|
1893
2358
|
let content;
|
|
1894
2359
|
try {
|
|
1895
|
-
content = typeof value === "string" ? value : JSON.stringify(value);
|
|
2360
|
+
content = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
|
|
1896
2361
|
} catch {
|
|
1897
2362
|
content = String(value);
|
|
1898
2363
|
}
|
|
@@ -1903,28 +2368,6 @@ var normalizeToolResult = (toolCall, value) => {
|
|
|
1903
2368
|
...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
|
|
1904
2369
|
};
|
|
1905
2370
|
};
|
|
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
2371
|
var createInitialMessages = (args) => [
|
|
1929
2372
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
1930
2373
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -1932,7 +2375,7 @@ var createInitialMessages = (args) => [
|
|
|
1932
2375
|
var runReasonToolLoop = async (args) => {
|
|
1933
2376
|
const { useReasonArgs, id, traceSpan, checkpointKey } = args;
|
|
1934
2377
|
const ctx = getHookContext();
|
|
1935
|
-
const abortSignal = (0,
|
|
2378
|
+
const abortSignal = (0, import_core5.combineAbortSignals)(
|
|
1936
2379
|
ctx.node.abortSignal,
|
|
1937
2380
|
useReasonArgs.abortSignal,
|
|
1938
2381
|
useReasonArgs.model.options?.abortSignal
|
|
@@ -1940,43 +2383,32 @@ var runReasonToolLoop = async (args) => {
|
|
|
1940
2383
|
const saved = ctx.currentNodeState.byKey[checkpointKey];
|
|
1941
2384
|
const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
|
|
1942
2385
|
const opId = checkpoint?.opId ?? args.opId;
|
|
1943
|
-
|
|
1944
|
-
const tools = useReasonArgs.tools ?? [];
|
|
2386
|
+
const tools2 = useReasonArgs.tools ?? [];
|
|
1945
2387
|
const toolByName = /* @__PURE__ */ new Map();
|
|
1946
|
-
let validationCompleted = false;
|
|
1947
2388
|
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);
|
|
1956
|
-
}
|
|
1957
|
-
if (useReasonArgs.interrupt) {
|
|
1958
|
-
throw new Error(
|
|
1959
|
-
"useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
|
|
1960
|
-
);
|
|
2389
|
+
for (const tool of tools2) {
|
|
2390
|
+
if (toolByName.has(tool.name)) {
|
|
2391
|
+
throw new Error(`useReason received duplicate tool name "${tool.name}".`);
|
|
1961
2392
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
providerOptions: resolveProviderOptions(
|
|
1968
|
-
useReasonArgs.model.options?.providerOptions,
|
|
1969
|
-
useReasonArgs.providerOptions
|
|
1970
|
-
) ?? {}
|
|
1971
|
-
}).requiresSeparateStructuredOutput
|
|
2393
|
+
toolByName.set(tool.name, tool);
|
|
2394
|
+
}
|
|
2395
|
+
if (useReasonArgs.interrupt) {
|
|
2396
|
+
throw new Error(
|
|
2397
|
+
"useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
|
|
1972
2398
|
);
|
|
1973
|
-
validationCompleted = true;
|
|
1974
|
-
} finally {
|
|
1975
|
-
if (!validationCompleted) {
|
|
1976
|
-
await closeOwnedTools(tools, true, traceSpan);
|
|
1977
|
-
}
|
|
1978
2399
|
}
|
|
1979
|
-
|
|
2400
|
+
separateOutput = Boolean(
|
|
2401
|
+
useReasonArgs.model.provider.getModel(useReasonArgs.model.modelId, {
|
|
2402
|
+
...useReasonArgs.model.options,
|
|
2403
|
+
...useReasonArgs.reasoning !== void 0 ? { reasoning: useReasonArgs.reasoning } : {},
|
|
2404
|
+
...useReasonArgs.responseFormat ? { responseFormat: useReasonArgs.responseFormat } : {},
|
|
2405
|
+
providerOptions: resolveProviderOptions(
|
|
2406
|
+
useReasonArgs.model.options?.providerOptions,
|
|
2407
|
+
useReasonArgs.providerOptions
|
|
2408
|
+
) ?? {}
|
|
2409
|
+
}).requiresSeparateStructuredOutput
|
|
2410
|
+
);
|
|
2411
|
+
const toolDefinitions = toToolDefinitions(tools2);
|
|
1980
2412
|
let finalizing = checkpoint?.finalizing ?? false;
|
|
1981
2413
|
let completed = false;
|
|
1982
2414
|
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
@@ -2028,7 +2460,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
2028
2460
|
};
|
|
2029
2461
|
try {
|
|
2030
2462
|
for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
2031
|
-
(0,
|
|
2463
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2032
2464
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
2033
2465
|
stepIndex,
|
|
2034
2466
|
toolCount: toolDefinitions.length
|
|
@@ -2072,6 +2504,12 @@ var runReasonToolLoop = async (args) => {
|
|
|
2072
2504
|
);
|
|
2073
2505
|
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
2074
2506
|
const toolCalls = step.toolCalls ?? [];
|
|
2507
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2508
|
+
for (const call of toolCalls) {
|
|
2509
|
+
if (!call.id || ids.has(call.id) || !reused && allToolCalls.some((prior) => prior.id === call.id))
|
|
2510
|
+
throw new Error("Model returned duplicate or empty tool call IDs.");
|
|
2511
|
+
ids.add(call.id);
|
|
2512
|
+
}
|
|
2075
2513
|
const toolResults = reused ? steps[stepIndex]?.toolResults ?? [] : [];
|
|
2076
2514
|
if (!reused)
|
|
2077
2515
|
steps.push({
|
|
@@ -2140,9 +2578,15 @@ var runReasonToolLoop = async (args) => {
|
|
|
2140
2578
|
pending = step;
|
|
2141
2579
|
save();
|
|
2142
2580
|
for (const toolCall of toolCalls) {
|
|
2143
|
-
if (toolResults.some((result2) => result2.toolCallId === toolCall.id))
|
|
2581
|
+
if (toolResults.some((result2) => result2.toolCallId === toolCall.id)) {
|
|
2582
|
+
await replayToolObservations(
|
|
2583
|
+
steps[stepIndex]?.toolObservations?.filter(
|
|
2584
|
+
(observation) => observation.toolCallId === `${opId}:${toolCall.id}`
|
|
2585
|
+
) ?? []
|
|
2586
|
+
);
|
|
2144
2587
|
continue;
|
|
2145
|
-
|
|
2588
|
+
}
|
|
2589
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2146
2590
|
const tool = toolByName.get(toolCall.name);
|
|
2147
2591
|
if (!tool) {
|
|
2148
2592
|
throw new Error(
|
|
@@ -2159,11 +2603,6 @@ var runReasonToolLoop = async (args) => {
|
|
|
2159
2603
|
tool.name,
|
|
2160
2604
|
false
|
|
2161
2605
|
);
|
|
2162
|
-
traceSpan?.addEvent?.("useReason.tool-call.start", {
|
|
2163
|
-
stepIndex,
|
|
2164
|
-
tool: tool.name,
|
|
2165
|
-
toolCallId: toolCall.id
|
|
2166
|
-
});
|
|
2167
2606
|
if (shouldEmitTool) {
|
|
2168
2607
|
emitToolEvent("tool-call-start", {
|
|
2169
2608
|
tool: tool.name,
|
|
@@ -2172,6 +2611,17 @@ var runReasonToolLoop = async (args) => {
|
|
|
2172
2611
|
});
|
|
2173
2612
|
}
|
|
2174
2613
|
if (shouldApproveTool && !approvedCalls.includes(toolCall.id)) {
|
|
2614
|
+
await observeToolFact(
|
|
2615
|
+
{
|
|
2616
|
+
version: 1,
|
|
2617
|
+
name: tool.name,
|
|
2618
|
+
toolCallId: `${opId}:${toolCall.id}`,
|
|
2619
|
+
attemptId: `${opId}:${toolCall.id}:approval`,
|
|
2620
|
+
callingMode: "model",
|
|
2621
|
+
executed: false
|
|
2622
|
+
},
|
|
2623
|
+
"waiting"
|
|
2624
|
+
);
|
|
2175
2625
|
const approval = await awaitInterruptInternal({
|
|
2176
2626
|
id: `tool:${toolCall.id}`,
|
|
2177
2627
|
request: {
|
|
@@ -2190,6 +2640,22 @@ var runReasonToolLoop = async (args) => {
|
|
|
2190
2640
|
});
|
|
2191
2641
|
const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
|
|
2192
2642
|
if (!approved) {
|
|
2643
|
+
const deniedObservation = {
|
|
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
|
+
outcome: "denied",
|
|
2651
|
+
denialCode: "APPROVAL_DENIED"
|
|
2652
|
+
};
|
|
2653
|
+
await observeToolFact(deniedObservation);
|
|
2654
|
+
const entry = steps[stepIndex];
|
|
2655
|
+
if (entry) {
|
|
2656
|
+
entry.toolObservations ??= [];
|
|
2657
|
+
entry.toolObservations.push(deniedObservation);
|
|
2658
|
+
}
|
|
2193
2659
|
const denied = {
|
|
2194
2660
|
toolCallId: toolCall.id,
|
|
2195
2661
|
name: tool.name,
|
|
@@ -2220,13 +2686,24 @@ var runReasonToolLoop = async (args) => {
|
|
|
2220
2686
|
save();
|
|
2221
2687
|
}
|
|
2222
2688
|
try {
|
|
2223
|
-
(0,
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2689
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2690
|
+
const rawResult = await executeObservedTool({
|
|
2691
|
+
tool,
|
|
2692
|
+
input: toolCall.input,
|
|
2693
|
+
toolCallId: `${opId}:${toolCall.id}`,
|
|
2694
|
+
providerToolCallId: toolCall.id,
|
|
2695
|
+
callingMode: "model",
|
|
2696
|
+
abortSignal,
|
|
2697
|
+
onObservation: (observation) => {
|
|
2698
|
+
const entry = steps[stepIndex];
|
|
2699
|
+
if (entry) {
|
|
2700
|
+
entry.toolObservations ??= [];
|
|
2701
|
+
entry.toolObservations.push(observation);
|
|
2702
|
+
save();
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2228
2705
|
});
|
|
2229
|
-
(0,
|
|
2706
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2230
2707
|
const result2 = normalizeToolResult(toolCall, rawResult);
|
|
2231
2708
|
toolResults.push(result2);
|
|
2232
2709
|
allToolResults.push(result2);
|
|
@@ -2239,20 +2716,14 @@ var runReasonToolLoop = async (args) => {
|
|
|
2239
2716
|
...result2.isError !== void 0 ? { isError: result2.isError } : {}
|
|
2240
2717
|
});
|
|
2241
2718
|
}
|
|
2242
|
-
traceSpan?.addEvent?.("useReason.tool-call.complete", {
|
|
2243
|
-
stepIndex,
|
|
2244
|
-
tool: result2.name,
|
|
2245
|
-
toolCallId: result2.toolCallId,
|
|
2246
|
-
isError: Boolean(result2.isError)
|
|
2247
|
-
});
|
|
2248
2719
|
} catch (error) {
|
|
2249
|
-
(0,
|
|
2250
|
-
if ((0,
|
|
2720
|
+
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2721
|
+
if ((0, import_errors6.isControlFlowError)(error)) throw error;
|
|
2251
2722
|
const result2 = {
|
|
2252
2723
|
toolCallId: toolCall.id,
|
|
2253
2724
|
name: tool.name,
|
|
2254
|
-
content: (0,
|
|
2255
|
-
failure: (0,
|
|
2725
|
+
content: (0, import_errors6.serializeFailure)(error).message,
|
|
2726
|
+
failure: (0, import_errors6.serializeFailure)(error),
|
|
2256
2727
|
isError: true
|
|
2257
2728
|
};
|
|
2258
2729
|
toolResults.push(result2);
|
|
@@ -2265,13 +2736,6 @@ var runReasonToolLoop = async (args) => {
|
|
|
2265
2736
|
failure: result2.failure
|
|
2266
2737
|
});
|
|
2267
2738
|
}
|
|
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
2739
|
}
|
|
2276
2740
|
const completed2 = toolResults.at(-1);
|
|
2277
2741
|
if (completed2)
|
|
@@ -2344,7 +2808,6 @@ var runReasonToolLoop = async (args) => {
|
|
|
2344
2808
|
ctx.stateDirty = true;
|
|
2345
2809
|
return result;
|
|
2346
2810
|
} catch (error) {
|
|
2347
|
-
primaryFailure = true;
|
|
2348
2811
|
try {
|
|
2349
2812
|
traceSpan?.fail?.(error, {
|
|
2350
2813
|
attributes: {
|
|
@@ -2355,8 +2818,6 @@ var runReasonToolLoop = async (args) => {
|
|
|
2355
2818
|
} catch {
|
|
2356
2819
|
}
|
|
2357
2820
|
throw error;
|
|
2358
|
-
} finally {
|
|
2359
|
-
await closeOwnedTools(tools, primaryFailure, traceSpan);
|
|
2360
2821
|
}
|
|
2361
2822
|
};
|
|
2362
2823
|
|
|
@@ -2372,15 +2833,15 @@ var assertReasoningThoughtsCompatibility = (args) => {
|
|
|
2372
2833
|
"useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
|
|
2373
2834
|
);
|
|
2374
2835
|
};
|
|
2375
|
-
async function
|
|
2836
|
+
async function runReason(args) {
|
|
2376
2837
|
assertReasoningThoughtsCompatibility(args);
|
|
2377
2838
|
const ctx = getHookContext();
|
|
2378
|
-
const abortSignal = (0,
|
|
2839
|
+
const abortSignal = (0, import_core6.combineAbortSignals)(
|
|
2379
2840
|
ctx.node.abortSignal,
|
|
2380
2841
|
args.abortSignal,
|
|
2381
2842
|
args.model.options?.abortSignal
|
|
2382
2843
|
);
|
|
2383
|
-
(0,
|
|
2844
|
+
(0, import_core6.throwIfExecutionAborted)(abortSignal);
|
|
2384
2845
|
const allowValidatedToolOutput = Boolean(
|
|
2385
2846
|
args.outputSchema && !(args.responseFormat ?? args.model.options?.responseFormat)
|
|
2386
2847
|
);
|
|
@@ -2403,286 +2864,303 @@ async function useReason(args) {
|
|
|
2403
2864
|
ctx.currentNodeState.byKey[checkpointKey]
|
|
2404
2865
|
);
|
|
2405
2866
|
if (existingCompleted) {
|
|
2867
|
+
await replayToolObservations(
|
|
2868
|
+
existingCompleted.result.steps?.flatMap(
|
|
2869
|
+
(step) => step.toolObservations ?? []
|
|
2870
|
+
) ?? []
|
|
2871
|
+
);
|
|
2406
2872
|
return existingCompleted.result;
|
|
2407
2873
|
}
|
|
2408
2874
|
const existingCheckpoint = readReasonCheckpoint(
|
|
2409
2875
|
ctx.currentNodeState.byKey[checkpointKey]
|
|
2410
2876
|
);
|
|
2411
2877
|
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
|
-
} : {}
|
|
2878
|
+
return withSafeTraceSpan(
|
|
2879
|
+
getReasonTraceAdapter(),
|
|
2880
|
+
{
|
|
2881
|
+
name: "useReason",
|
|
2882
|
+
attributes: {
|
|
2883
|
+
...id ? { id } : {},
|
|
2884
|
+
opId,
|
|
2885
|
+
nodeId: ctx.node.graph.node,
|
|
2886
|
+
providerId: args.model.provider.id,
|
|
2887
|
+
modelId: args.model.modelId,
|
|
2888
|
+
stream: args.stream ?? args.model.options?.streaming ?? true,
|
|
2889
|
+
emit: args.emit ?? true,
|
|
2890
|
+
...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
|
|
2891
|
+
...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
|
|
2892
|
+
...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
|
|
2893
|
+
...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
|
|
2894
|
+
hasOutputSchema: Boolean(args.outputSchema),
|
|
2895
|
+
hasInterrupt: Boolean(args.interrupt),
|
|
2896
|
+
hasStructured: Boolean(args.structured),
|
|
2897
|
+
hasTools: Boolean(args.tools?.length)
|
|
2489
2898
|
},
|
|
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"
|
|
2899
|
+
telemetry: {
|
|
2900
|
+
...args.telemetry ?? {},
|
|
2901
|
+
input: args.telemetry?.input ?? args.input
|
|
2902
|
+
}
|
|
2903
|
+
},
|
|
2904
|
+
async (traceSpan) => {
|
|
2905
|
+
if (args.tools?.length) {
|
|
2906
|
+
return runReasonToolLoop({
|
|
2907
|
+
useReasonArgs: args,
|
|
2908
|
+
allowValidatedToolOutput,
|
|
2909
|
+
checkpointKey,
|
|
2910
|
+
initialWarnings: inferred.warnings,
|
|
2911
|
+
...id ? { id } : {},
|
|
2912
|
+
opId,
|
|
2913
|
+
...traceSpan ? { traceSpan } : {}
|
|
2544
2914
|
});
|
|
2545
2915
|
}
|
|
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."
|
|
2916
|
+
const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
|
|
2917
|
+
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
2918
|
+
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
2919
|
+
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
2920
|
+
const useStructuredIncrementalStreaming = Boolean(
|
|
2921
|
+
args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
|
|
2560
2922
|
);
|
|
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
|
-
|
|
2923
|
+
let firstText = "";
|
|
2924
|
+
let firstRaw;
|
|
2925
|
+
let firstOutput;
|
|
2926
|
+
let firstInterruptRequest;
|
|
2927
|
+
let firstInterruptRequired = false;
|
|
2928
|
+
let finalText = "";
|
|
2929
|
+
let finalRaw;
|
|
2930
|
+
let finalOutput;
|
|
2931
|
+
let aggregatedUsage;
|
|
2932
|
+
let finalFinishReason;
|
|
2933
|
+
let aggregatedProviderMetadata;
|
|
2934
|
+
let aggregatedWarnings = inferred.warnings;
|
|
2935
|
+
if (existingCheckpoint) {
|
|
2936
|
+
firstText = existingCheckpoint.firstText;
|
|
2937
|
+
firstRaw = existingCheckpoint.firstRaw;
|
|
2938
|
+
firstOutput = existingCheckpoint.firstOutput;
|
|
2939
|
+
finalText = firstText;
|
|
2940
|
+
finalRaw = firstRaw;
|
|
2941
|
+
finalOutput = firstOutput;
|
|
2942
|
+
aggregatedUsage = existingCheckpoint.firstUsage;
|
|
2943
|
+
finalFinishReason = existingCheckpoint.firstFinishReason;
|
|
2944
|
+
aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
|
|
2945
|
+
aggregatedWarnings = existingCheckpoint.firstWarnings;
|
|
2946
|
+
traceSpan?.addEvent?.("useReason.resume", {
|
|
2947
|
+
checkpointKey
|
|
2948
|
+
});
|
|
2949
|
+
} else {
|
|
2950
|
+
const first = await reasonEngine(
|
|
2951
|
+
{
|
|
2952
|
+
...args,
|
|
2953
|
+
emit: suppressTextStream ? false : args.emit,
|
|
2954
|
+
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2955
|
+
...useStructuredIncrementalStreaming ? {
|
|
2956
|
+
onTextChunk: createStructuredOutputStreamer(args, id, opId)
|
|
2957
|
+
} : {}
|
|
2958
|
+
},
|
|
2959
|
+
reasonMeta,
|
|
2960
|
+
args.interrupt ? defaultInterruptFirstPassInput({
|
|
2961
|
+
input: args.input,
|
|
2962
|
+
requestSchema: args.interrupt.requestSchema,
|
|
2963
|
+
mode: args.interrupt.mode ?? "required",
|
|
2964
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {}
|
|
2965
|
+
}) : args.outputSchema ? withOutputGuardrails(
|
|
2966
|
+
withStructuredStreamHints(args.input, {
|
|
2967
|
+
...setFieldPaths.length > 0 ? { setFieldPaths } : {},
|
|
2968
|
+
...appendFieldPaths.length > 0 ? { appendFieldPaths } : {},
|
|
2969
|
+
...textDeltaFieldPaths.length > 0 ? { textDeltaFieldPaths } : {}
|
|
2970
|
+
}),
|
|
2971
|
+
args.outputSchema
|
|
2972
|
+
) : void 0
|
|
2973
|
+
);
|
|
2974
|
+
firstRaw = first.raw;
|
|
2975
|
+
finalRaw = first.raw;
|
|
2976
|
+
accumulateTokenUsage(first.usage);
|
|
2977
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
|
|
2978
|
+
finalFinishReason = first.finishReason;
|
|
2979
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
2980
|
+
aggregatedProviderMetadata,
|
|
2981
|
+
first.providerMetadata
|
|
2982
|
+
);
|
|
2983
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
|
|
2984
|
+
traceSpan?.addEvent?.("useReason.first-pass.complete", {
|
|
2985
|
+
textLength: first.text.length,
|
|
2986
|
+
hasInterrupt: Boolean(args.interrupt)
|
|
2987
|
+
});
|
|
2988
|
+
if (args.interrupt) {
|
|
2989
|
+
const firstPass = parseInterruptFirstPassResult({
|
|
2990
|
+
text: first.text,
|
|
2991
|
+
requestSchema: args.interrupt.requestSchema,
|
|
2992
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2993
|
+
...first.usage ? { usage: first.usage } : {},
|
|
2994
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {},
|
|
2995
|
+
mode: args.interrupt.mode ?? "required"
|
|
2996
|
+
});
|
|
2997
|
+
firstText = firstPass.draftText;
|
|
2998
|
+
finalText = firstPass.draftText;
|
|
2999
|
+
firstInterruptRequired = firstPass.interruptRequired;
|
|
3000
|
+
firstInterruptRequest = firstPass.request;
|
|
3001
|
+
firstOutput = firstPass.output;
|
|
3002
|
+
finalOutput = firstPass.output;
|
|
3003
|
+
} else {
|
|
3004
|
+
firstText = first.text;
|
|
3005
|
+
finalText = first.text;
|
|
3006
|
+
if (args.outputSchema) {
|
|
3007
|
+
firstOutput = parseReasonOutputWithSchema({
|
|
3008
|
+
text: first.text,
|
|
3009
|
+
schema: args.outputSchema,
|
|
3010
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
3011
|
+
...first.usage ? { usage: first.usage } : {},
|
|
3012
|
+
label: "useReason output"
|
|
3013
|
+
});
|
|
3014
|
+
}
|
|
3015
|
+
finalOutput = firstOutput;
|
|
3016
|
+
}
|
|
2590
3017
|
}
|
|
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
|
-
|
|
3018
|
+
let interruptResponse;
|
|
3019
|
+
if (args.interrupt && (existingCheckpoint || firstInterruptRequired)) {
|
|
3020
|
+
const requestSchema = args.interrupt.requestSchema;
|
|
3021
|
+
const interruptRequest = existingCheckpoint ? parseWithSchema(
|
|
3022
|
+
requestSchema,
|
|
3023
|
+
existingCheckpoint.request,
|
|
3024
|
+
"useReason interrupt.request"
|
|
3025
|
+
) : firstInterruptRequest;
|
|
3026
|
+
if (!interruptRequest) {
|
|
3027
|
+
throw new Error(
|
|
3028
|
+
"useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
|
|
3029
|
+
);
|
|
3030
|
+
}
|
|
3031
|
+
if (!existingCheckpoint) {
|
|
3032
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3033
|
+
status: "awaiting_interrupt",
|
|
3034
|
+
request: interruptRequest,
|
|
3035
|
+
firstText,
|
|
3036
|
+
...firstRaw !== void 0 ? { firstRaw } : {},
|
|
3037
|
+
...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
|
|
3038
|
+
...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
|
|
3039
|
+
...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
|
|
3040
|
+
...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
|
|
3041
|
+
...firstOutput !== void 0 ? { firstOutput } : {}
|
|
3042
|
+
};
|
|
3043
|
+
ctx.stateDirty = true;
|
|
3044
|
+
}
|
|
3045
|
+
const resumeStatePatch = resolveHookStatePatch({
|
|
3046
|
+
nodeId: ctx.node.graph.node,
|
|
3047
|
+
currentNodeState: ctx.currentNodeState,
|
|
3048
|
+
workflowState: ctx.workflowState
|
|
3049
|
+
});
|
|
3050
|
+
interruptResponse = await awaitInterruptInternal({
|
|
3051
|
+
request: interruptRequest,
|
|
3052
|
+
requestSchema,
|
|
3053
|
+
responseSchema: args.interrupt.responseSchema,
|
|
3054
|
+
...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
|
|
3055
|
+
...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
|
|
3056
|
+
...id ? { id } : {},
|
|
3057
|
+
meta: {
|
|
3058
|
+
__kortyxResumeStatePatch: resumeStatePatch
|
|
3059
|
+
}
|
|
3060
|
+
});
|
|
3061
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
3062
|
+
checkpointKey
|
|
3063
|
+
});
|
|
3064
|
+
if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
|
|
3065
|
+
delete ctx.currentNodeState.byKey[checkpointKey];
|
|
3066
|
+
ctx.stateDirty = true;
|
|
3067
|
+
}
|
|
3068
|
+
const continuationInput = defaultContinuationInput({
|
|
3069
|
+
input: args.input,
|
|
3070
|
+
draftText: firstText,
|
|
3071
|
+
...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
|
|
3072
|
+
request: interruptRequest,
|
|
3073
|
+
response: interruptResponse
|
|
3074
|
+
});
|
|
3075
|
+
const second = await reasonEngine(
|
|
3076
|
+
{
|
|
3077
|
+
...args,
|
|
3078
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
3079
|
+
emit: suppressTextStream ? false : args.emit,
|
|
3080
|
+
stream: suppressTextStream ? false : args.stream
|
|
3081
|
+
},
|
|
3082
|
+
reasonMeta,
|
|
3083
|
+
args.outputSchema ? withOutputGuardrails(
|
|
3084
|
+
continuationInput,
|
|
3085
|
+
args.outputSchema
|
|
3086
|
+
) : continuationInput
|
|
3087
|
+
);
|
|
3088
|
+
finalText = second.text;
|
|
3089
|
+
finalRaw = second.raw;
|
|
3090
|
+
accumulateTokenUsage(second.usage);
|
|
3091
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
|
|
3092
|
+
finalFinishReason = second.finishReason;
|
|
3093
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
3094
|
+
aggregatedProviderMetadata,
|
|
3095
|
+
second.providerMetadata
|
|
3096
|
+
);
|
|
3097
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
|
|
3098
|
+
traceSpan?.addEvent?.("useReason.continuation.complete", {
|
|
3099
|
+
textLength: second.text.length
|
|
3100
|
+
});
|
|
3101
|
+
if (args.outputSchema) {
|
|
3102
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
3103
|
+
text: finalText,
|
|
3104
|
+
schema: args.outputSchema,
|
|
3105
|
+
...second.finishReason ? { finishReason: second.finishReason } : {},
|
|
3106
|
+
...aggregatedUsage ? { usage: aggregatedUsage } : {},
|
|
3107
|
+
label: "useReason output"
|
|
3108
|
+
});
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
if (finalOutput !== void 0) {
|
|
3112
|
+
emitReasonStructuredOutput({
|
|
3113
|
+
...id ? { id } : {},
|
|
3114
|
+
opId,
|
|
3115
|
+
output: finalOutput,
|
|
3116
|
+
structured: args.structured,
|
|
3117
|
+
emit: args.emit ?? true
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
3120
|
+
const result = {
|
|
3121
|
+
...id ? { id } : {},
|
|
3122
|
+
opId,
|
|
2634
3123
|
text: finalText,
|
|
2635
|
-
|
|
2636
|
-
...
|
|
2637
|
-
...
|
|
2638
|
-
|
|
3124
|
+
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
3125
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
3126
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
3127
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
3128
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
3129
|
+
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
3130
|
+
...interruptResponse !== void 0 ? { interruptResponse } : {}
|
|
3131
|
+
};
|
|
3132
|
+
if (args.interrupt) {
|
|
3133
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
3134
|
+
status: "completed",
|
|
3135
|
+
result
|
|
3136
|
+
};
|
|
3137
|
+
ctx.stateDirty = true;
|
|
3138
|
+
}
|
|
3139
|
+
traceSpan?.end?.({
|
|
3140
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
3141
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
3142
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
3143
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
3144
|
+
attributes: {
|
|
3145
|
+
textLength: finalText.length,
|
|
3146
|
+
resumedFromCheckpoint: Boolean(existingCheckpoint),
|
|
3147
|
+
interrupted: Boolean(interruptResponse !== void 0)
|
|
3148
|
+
},
|
|
3149
|
+
telemetry: {
|
|
3150
|
+
...args.telemetry ?? {},
|
|
3151
|
+
output: args.telemetry?.output ?? finalOutput ?? finalText
|
|
3152
|
+
}
|
|
2639
3153
|
});
|
|
3154
|
+
return result;
|
|
2640
3155
|
}
|
|
3156
|
+
);
|
|
3157
|
+
}
|
|
3158
|
+
async function useReason(args) {
|
|
3159
|
+
try {
|
|
3160
|
+
return await runReason(args);
|
|
3161
|
+
} finally {
|
|
3162
|
+
await closeOwnedTools(args.tools ?? []);
|
|
2641
3163
|
}
|
|
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
3164
|
}
|
|
2687
3165
|
|
|
2688
3166
|
// src/hooks.ts
|
|
@@ -2741,8 +3219,8 @@ function useWorkflowState(key, ...rest) {
|
|
|
2741
3219
|
}
|
|
2742
3220
|
|
|
2743
3221
|
// src/parallel.ts
|
|
2744
|
-
var
|
|
2745
|
-
var
|
|
3222
|
+
var import_core7 = require("@kortyx/core");
|
|
3223
|
+
var import_errors7 = require("@kortyx/core/errors");
|
|
2746
3224
|
var ParallelChildWaiting = class extends Error {
|
|
2747
3225
|
name = "ParallelChildWaiting";
|
|
2748
3226
|
};
|
|
@@ -2756,14 +3234,14 @@ var ParallelError = class extends AggregateError {
|
|
|
2756
3234
|
);
|
|
2757
3235
|
this.results = results;
|
|
2758
3236
|
this.name = "ParallelError";
|
|
2759
|
-
this.failure = (0,
|
|
3237
|
+
this.failure = (0, import_errors7.serializeFailure)({
|
|
2760
3238
|
version: 1,
|
|
2761
3239
|
code: this.code,
|
|
2762
3240
|
category: "workflow",
|
|
2763
3241
|
message: this.message,
|
|
2764
3242
|
retryable: null,
|
|
2765
3243
|
results: results.map(
|
|
2766
|
-
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0,
|
|
3244
|
+
(result) => result.status === "fulfilled" ? { status: "fulfilled" } : { status: "rejected", failure: (0, import_errors7.serializeFailure)(result.reason) }
|
|
2767
3245
|
)
|
|
2768
3246
|
});
|
|
2769
3247
|
}
|
|
@@ -2771,7 +3249,7 @@ var ParallelError = class extends AggregateError {
|
|
|
2771
3249
|
code = "PARALLEL_FAILED";
|
|
2772
3250
|
failure;
|
|
2773
3251
|
toJSON() {
|
|
2774
|
-
return (0,
|
|
3252
|
+
return (0, import_errors7.serializeFailure)(this);
|
|
2775
3253
|
}
|
|
2776
3254
|
};
|
|
2777
3255
|
function registerWorkflowTask(id) {
|
|
@@ -2867,13 +3345,13 @@ function parallel(values) {
|
|
|
2867
3345
|
);
|
|
2868
3346
|
}
|
|
2869
3347
|
if (ready[0]?.status === "rejected") throw ready[0].reason;
|
|
2870
|
-
(0,
|
|
3348
|
+
(0, import_core7.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2871
3349
|
if (ctx.workflowControlError) throw ctx.workflowControlError;
|
|
2872
3350
|
const rejected = outcomes.filter(
|
|
2873
3351
|
(outcome) => outcome.status === "rejected"
|
|
2874
3352
|
);
|
|
2875
3353
|
const control = rejected.find(
|
|
2876
|
-
(outcome) => (0,
|
|
3354
|
+
(outcome) => (0, import_core7.isExecutionCancelled)(outcome.reason) || (0, import_core7.isExecutionLimitReached)(outcome.reason)
|
|
2877
3355
|
);
|
|
2878
3356
|
if (control) {
|
|
2879
3357
|
ctx.workflowControlError = control.reason;
|
|
@@ -2910,54 +3388,13 @@ function parallel(values) {
|
|
|
2910
3388
|
}
|
|
2911
3389
|
|
|
2912
3390
|
// src/workflow.ts
|
|
2913
|
-
var
|
|
2914
|
-
var
|
|
2915
|
-
var
|
|
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;
|
|
2923
|
-
try {
|
|
2924
|
-
const active = telemetry.trace?.getActiveContext?.();
|
|
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
|
-
});
|
|
2945
|
-
} catch {
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
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
|
-
|
|
2956
|
-
// src/workflow.ts
|
|
2957
|
-
var WorkflowCallError = class extends import_errors6.KortyxError {
|
|
3391
|
+
var import_node_crypto6 = require("crypto");
|
|
3392
|
+
var import_core8 = require("@kortyx/core");
|
|
3393
|
+
var import_errors8 = require("@kortyx/core/errors");
|
|
3394
|
+
var WorkflowCallError = class extends import_errors8.KortyxError {
|
|
2958
3395
|
constructor(workflow, message, cause, failure) {
|
|
2959
|
-
const detail = (0,
|
|
2960
|
-
failure ?? cause ?? new
|
|
3396
|
+
const detail = (0, import_errors8.serializeFailure)(
|
|
3397
|
+
failure ?? cause ?? new import_errors8.WorkflowContractError(message)
|
|
2961
3398
|
);
|
|
2962
3399
|
super(detail.code, `Workflow '${workflow}': ${message}`, {
|
|
2963
3400
|
...detail,
|
|
@@ -2978,12 +3415,12 @@ function workflowCallFingerprint(value) {
|
|
|
2978
3415
|
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
2979
3416
|
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
2980
3417
|
if (typeof v !== "object" || !v || seen.has(v)) {
|
|
2981
|
-
throw new
|
|
3418
|
+
throw new import_errors8.WorkflowContractError(
|
|
2982
3419
|
"Workflow input and output must be JSON serializable."
|
|
2983
3420
|
);
|
|
2984
3421
|
}
|
|
2985
3422
|
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
|
|
2986
|
-
throw new
|
|
3423
|
+
throw new import_errors8.WorkflowContractError(
|
|
2987
3424
|
"Workflow input and output must contain plain JSON objects."
|
|
2988
3425
|
);
|
|
2989
3426
|
}
|
|
@@ -3004,7 +3441,7 @@ function createWorkflowHooks(workflows) {
|
|
|
3004
3441
|
useWorkflow: (args) => {
|
|
3005
3442
|
const workflow = workflows[args.workflow];
|
|
3006
3443
|
if (!workflow || workflow.id !== args.workflow)
|
|
3007
|
-
throw new
|
|
3444
|
+
throw new import_errors8.WorkflowContractError(
|
|
3008
3445
|
"Workflow registry keys must match definition ids."
|
|
3009
3446
|
);
|
|
3010
3447
|
return useWorkflow({ ...args, workflow });
|
|
@@ -3018,7 +3455,7 @@ async function useWorkflow(args) {
|
|
|
3018
3455
|
if (task.group) await task.group.ready;
|
|
3019
3456
|
return await runWorkflowCall(args, task);
|
|
3020
3457
|
} catch (error) {
|
|
3021
|
-
if ((0,
|
|
3458
|
+
if ((0, import_core8.isExecutionCancelled)(error) || (0, import_core8.isExecutionLimitReached)(error))
|
|
3022
3459
|
getHookContext().workflowControlError = error;
|
|
3023
3460
|
throw error;
|
|
3024
3461
|
} finally {
|
|
@@ -3027,26 +3464,26 @@ async function useWorkflow(args) {
|
|
|
3027
3464
|
}
|
|
3028
3465
|
async function runWorkflowCall(args, task) {
|
|
3029
3466
|
const ctx = getHookContext();
|
|
3030
|
-
(0,
|
|
3467
|
+
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
3031
3468
|
const service = ctx.node.callWorkflow;
|
|
3032
3469
|
if (!service)
|
|
3033
|
-
throw new
|
|
3470
|
+
throw new import_errors8.WorkflowContractError(
|
|
3034
3471
|
"useWorkflow requires an agent workflow registry."
|
|
3035
3472
|
);
|
|
3036
3473
|
if (!args.id.trim())
|
|
3037
|
-
throw new
|
|
3474
|
+
throw new import_errors8.WorkflowContractError(
|
|
3038
3475
|
"useWorkflow requires a stable, nonempty id."
|
|
3039
3476
|
);
|
|
3040
3477
|
if (ctx.workflowCallActive && !task.group)
|
|
3041
|
-
throw new
|
|
3478
|
+
throw new import_errors8.WorkflowContractError(
|
|
3042
3479
|
"Concurrent workflow calls require parallel([...]); otherwise await each call."
|
|
3043
3480
|
);
|
|
3044
3481
|
if (ctx.workflowCallIds.has(args.id))
|
|
3045
|
-
throw new
|
|
3482
|
+
throw new import_errors8.WorkflowContractError(
|
|
3046
3483
|
`Duplicate workflow call id '${args.id}' in one node activation.`
|
|
3047
3484
|
);
|
|
3048
3485
|
if (ctx.workflowCallIds.size >= 64)
|
|
3049
|
-
throw new
|
|
3486
|
+
throw new import_errors8.WorkflowContractError(
|
|
3050
3487
|
"A node may call at most 64 child workflows per activation."
|
|
3051
3488
|
);
|
|
3052
3489
|
ctx.workflowCallIds.add(args.id);
|
|
@@ -3073,7 +3510,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3073
3510
|
if (!record) ctx.node.consumeExecution?.("maxChildInvocations");
|
|
3074
3511
|
record ??= {
|
|
3075
3512
|
fingerprint,
|
|
3076
|
-
invocationId: (0,
|
|
3513
|
+
invocationId: (0, import_node_crypto6.randomUUID)(),
|
|
3077
3514
|
status: "running",
|
|
3078
3515
|
interrupts: [],
|
|
3079
3516
|
...task.group ? { parallelKey: task.group.key } : {}
|
|
@@ -3083,7 +3520,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3083
3520
|
if (!task.group) ctx.workflowCallActive = true;
|
|
3084
3521
|
const telemetry = ctx.node.workflowCallTelemetry;
|
|
3085
3522
|
const activationKey = "__useWorkflowActivation";
|
|
3086
|
-
ctx.currentNodeState.byKey[activationKey] ??= (0,
|
|
3523
|
+
ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto6.randomUUID)();
|
|
3087
3524
|
const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
|
|
3088
3525
|
const metadata = {
|
|
3089
3526
|
invocationId: record.invocationId,
|
|
@@ -3127,7 +3564,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3127
3564
|
workflow,
|
|
3128
3565
|
record.error ?? "child failed",
|
|
3129
3566
|
void 0,
|
|
3130
|
-
record.failure ?? (0,
|
|
3567
|
+
record.failure ?? (0, import_errors8.serializeFailure)(void 0)
|
|
3131
3568
|
);
|
|
3132
3569
|
if (task.group && record.status === "interrupted" && record.interrupts.at(-1)?.response === void 0)
|
|
3133
3570
|
throw new ParallelChildWaiting();
|
|
@@ -3151,9 +3588,9 @@ async function runWorkflowCall(args, task) {
|
|
|
3151
3588
|
} catch (error) {
|
|
3152
3589
|
const child = error;
|
|
3153
3590
|
recordUsage(child?.__kortyxChildUsage);
|
|
3154
|
-
(0,
|
|
3155
|
-
if ((0,
|
|
3156
|
-
if ((0,
|
|
3591
|
+
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
3592
|
+
if ((0, import_core8.isExecutionCancelled)(error)) throw error;
|
|
3593
|
+
if ((0, import_core8.isExecutionLimitReached)(error)) {
|
|
3157
3594
|
if (child?.__kortyxChildSnapshot)
|
|
3158
3595
|
record.snapshot = child.__kortyxChildSnapshot;
|
|
3159
3596
|
report("suspended", {
|
|
@@ -3163,7 +3600,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3163
3600
|
throw error;
|
|
3164
3601
|
}
|
|
3165
3602
|
record.status = "failed";
|
|
3166
|
-
record.failure = (0,
|
|
3603
|
+
record.failure = (0, import_errors8.serializeFailure)(error);
|
|
3167
3604
|
record.error = record.failure.message;
|
|
3168
3605
|
report("failed", { error: "Child workflow failed" });
|
|
3169
3606
|
throw new WorkflowCallError(
|
|
@@ -3173,12 +3610,15 @@ async function runWorkflowCall(args, task) {
|
|
|
3173
3610
|
record.failure
|
|
3174
3611
|
);
|
|
3175
3612
|
}
|
|
3176
|
-
(0,
|
|
3613
|
+
(0, import_core8.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
3177
3614
|
recordUsage(outcome.usage);
|
|
3178
3615
|
if (outcome.status === "completed") {
|
|
3179
3616
|
record.data = outcome.data;
|
|
3180
3617
|
workflowCallFingerprint(record.data);
|
|
3181
3618
|
record.status = "completed";
|
|
3619
|
+
record.toolObservations = outcome.toolObservations ?? [];
|
|
3620
|
+
for (const observation of record.toolObservations)
|
|
3621
|
+
rememberToolObservation(observation);
|
|
3182
3622
|
delete record.snapshot;
|
|
3183
3623
|
report(
|
|
3184
3624
|
"completed",
|
|
@@ -3201,8 +3641,12 @@ async function runWorkflowCall(args, task) {
|
|
|
3201
3641
|
});
|
|
3202
3642
|
}
|
|
3203
3643
|
}
|
|
3204
|
-
if (cached)
|
|
3644
|
+
if (cached) {
|
|
3205
3645
|
report("reused", workflowCallContent(telemetry, "output", record.data));
|
|
3646
|
+
if (record.restoredToolBranch !== telemetry?.correlation?.branchId)
|
|
3647
|
+
await replayToolObservations(record.toolObservations ?? [], true);
|
|
3648
|
+
delete record.restoredToolBranch;
|
|
3649
|
+
}
|
|
3206
3650
|
const data = record.data ?? {};
|
|
3207
3651
|
return {
|
|
3208
3652
|
data: JSON.parse(JSON.stringify(data))
|
|
@@ -3226,6 +3670,7 @@ async function runWorkflowCall(args, task) {
|
|
|
3226
3670
|
useReason,
|
|
3227
3671
|
useRuntimeContext,
|
|
3228
3672
|
useStructuredData,
|
|
3673
|
+
useTool,
|
|
3229
3674
|
useWorkflow,
|
|
3230
3675
|
useWorkflowState,
|
|
3231
3676
|
workflowCallFingerprint
|