@axiom-lattice/core 2.1.97 → 2.1.99

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.
@@ -1,3 +1,9 @@
1
+ import {
2
+ parseYaml,
3
+ toSafeStateExpr,
4
+ validateExpression
5
+ } from "./chunk-VCEF7ZDP.mjs";
6
+
1
7
  // src/workflow/compile.ts
2
8
  import {
3
9
  StateGraph,
@@ -9,298 +15,28 @@ import {
9
15
  import {
10
16
  Annotation
11
17
  } from "@langchain/langgraph";
12
- import { HumanMessage, SystemMessage } from "@langchain/core/messages";
18
+ import { HumanMessage } from "@langchain/core/messages";
13
19
 
14
- // src/workflow/parse-yaml.ts
15
- import * as yaml from "js-yaml";
16
-
17
- // src/workflow/schema.ts
18
- function toJsonSchema(fields) {
19
- if (!fields || Object.keys(fields).length === 0) return void 0;
20
- const properties = {};
21
- const required = [];
22
- for (const [key, value] of Object.entries(fields)) {
23
- required.push(key);
24
- properties[key] = fieldToSchema(value);
25
- }
26
- return { type: "object", properties, required };
27
- }
28
- function fieldToSchema(value) {
29
- if (typeof value === "string") return stringTypeToSchema(value);
30
- if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object" && !Array.isArray(value[0])) {
31
- const nested = toJsonSchema(value[0]);
32
- return { type: "array", items: nested ?? { type: "object" } };
33
- }
34
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
35
- const nested = toJsonSchema(value);
36
- return nested ?? { type: "object" };
37
- }
38
- return { type: "string" };
39
- }
40
- function stringTypeToSchema(type) {
41
- if (type.endsWith("[]")) {
42
- const inner = type.slice(0, -2);
43
- return { type: "array", items: stringTypeToSchema(inner) };
44
- }
45
- if (type === "string") return { type: "string" };
46
- if (type === "number") return { type: "number" };
47
- if (type === "boolean") return { type: "boolean" };
48
- return { type };
49
- }
50
-
51
- // src/workflow/parse-yaml.ts
52
- function nodeId(label) {
53
- return `n_${label}`;
54
- }
55
- var _parallelSeq = 0;
56
- function nextParallelLabel() {
57
- return `parallel_${_parallelSeq++}`;
58
- }
59
- function translate(template) {
60
- return template.replace(/\{\{([^}]+)\}\}/g, (_m, expr) => {
61
- const t = expr.trim();
62
- if (t === "item") return "${item}";
63
- return `\${state.${t}}`;
64
- });
65
- }
66
- function toSafeStateExpr(expr) {
67
- const match = expr.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)/);
68
- if (!match) return `state.${expr}`;
69
- const field = match[0];
70
- const rest = expr.slice(field.length);
71
- return `state["${field}"]${rest}`;
72
- }
73
- function validateExpression(expr) {
74
- if (/;/.test(expr)) {
75
- throw new Error(`Condition expression must not contain semicolons: "${expr}"`);
76
- }
77
- if (/[{}]/.test(expr)) {
78
- throw new Error(`Condition expression must not contain braces: "${expr}"`);
79
- }
80
- if (/\bfunction\b/.test(expr)) {
81
- throw new Error(`Condition expression must not contain 'function': "${expr}"`);
82
- }
83
- if (/\brequire\b|\bimport\b|\bmodule\b/.test(expr)) {
84
- throw new Error(`Condition expression must not contain require/import: "${expr}"`);
85
- }
86
- if (/\bprocess\b|\bglobal\b|\bglobalThis\b/.test(expr)) {
87
- throw new Error(`Condition expression must not contain process/global: "${expr}"`);
88
- }
89
- if (/\beval\b|\bFunction\b/.test(expr)) {
90
- throw new Error(`Condition expression must not contain eval/Function: "${expr}"`);
91
- }
92
- if (/\bconstructor\b|\b__proto__\b|\bprototype\b/.test(expr)) {
93
- throw new Error(`Condition expression must not contain prototype access: "${expr}"`);
94
- }
95
- if (/\bsetTimeout\b|\bsetInterval\b|\bbuffer\b/i.test(expr)) {
96
- throw new Error(`Condition expression must not contain timer or buffer references: "${expr}"`);
97
- }
98
- }
99
- function parseYaml(yamlStr) {
100
- _parallelSeq = 0;
101
- let raw;
102
- try {
103
- raw = yaml.load(yamlStr);
104
- } catch (e) {
105
- const line = e?.mark?.line != null ? ` (line ${e.mark.line + 1})` : "";
106
- throw new Error(`YAML parse error${line}: ${e.message || e}`);
107
- }
108
- if (!raw || typeof raw !== "object" || !Array.isArray(raw.steps)) {
109
- throw new Error("Workflow YAML must have 'steps' (array)");
110
- }
111
- const name = typeof raw.name === "string" ? raw.name : "workflow";
112
- const wf = {
113
- name,
114
- steps: raw.steps.map((s, i) => parseStep(s, i))
115
- };
116
- return normalize(wf);
117
- }
118
- function parseStep(raw, index) {
119
- if (raw.parallel !== void 0) {
120
- if (!Array.isArray(raw.parallel)) {
121
- throw new Error(`Step at position ${index}: parallel must be an array`);
122
- }
123
- const children = raw.parallel.map((c, ci) => {
124
- const entries2 = Object.entries(c);
125
- if (entries2.length !== 1) {
126
- throw new Error(`Parallel child at position ${index}.${ci} must be a single-key mapping`);
127
- }
128
- const [label, config2] = entries2[0];
129
- return {
130
- label,
131
- if: config2.if !== void 0 ? String(config2.if) : void 0,
132
- prompt: String(config2.prompt ?? ""),
133
- output: config2.output,
134
- ask: config2.ask === true
135
- };
136
- });
137
- return {
138
- parallel: children,
139
- if: raw.if !== void 0 ? String(raw.if) : void 0,
140
- output: raw.output
141
- };
142
- }
143
- const entries = Object.entries(raw);
144
- if (entries.length !== 1) {
145
- throw new Error(`Step at position ${index} must be a single-key mapping`);
146
- }
147
- const [key, config] = entries[0];
148
- const mapConfig = config?.map;
149
- if (mapConfig && typeof mapConfig === "object") {
150
- return {
151
- map: {
152
- source: mapConfig.source,
153
- label: key,
154
- // always use the YAML key as the label
155
- if: mapConfig.if !== void 0 ? String(mapConfig.if) : void 0,
156
- each: {
157
- prompt: String(mapConfig.each?.prompt ?? ""),
158
- output: mapConfig.each?.output
159
- },
160
- output: mapConfig.output,
161
- batch: mapConfig.batch,
162
- concurrency: mapConfig.concurrency
163
- }
164
- };
165
- }
166
- if (key === "map") {
167
- throw new Error(`Map steps must use named format: - <label>: { map: { source, each, ... } }. Anonymous "- map:" is not supported.`);
168
- }
169
- return {
170
- label: key,
171
- if: config.if !== void 0 ? String(config.if) : void 0,
172
- prompt: String(config.prompt ?? ""),
173
- output: config.output,
174
- ask: config.ask === true
175
- };
176
- }
177
- function normalize(wf) {
178
- validate(wf);
179
- for (const step of wf.steps) {
180
- if ("label" in step) {
181
- const s = step;
182
- if (s.if) validateExpression(s.if);
183
- } else if ("parallel" in step) {
184
- const pb = step;
185
- if (pb.if) validateExpression(pb.if);
186
- for (const child of pb.parallel) {
187
- if (child.if) validateExpression(child.if);
188
- }
189
- }
190
- }
191
- const nodes = [];
192
- const edges = [];
193
- const fields = { input: { type: "string" } };
194
- nodes.push({ id: "n_input", type: "input", name: "input", output: { key: "input" } });
195
- edges.push({ from: "START", to: "n_input" });
196
- let prevNodeId = "n_input";
197
- for (const step of wf.steps) {
198
- if ("label" in step) {
199
- const s = step;
200
- const nid = nodeId(s.label);
201
- const schema = toJsonSchema(s.output);
202
- nodes.push({
203
- id: nid,
204
- type: "agent",
205
- name: s.label,
206
- input: { template: translate(s.prompt) },
207
- output: { key: s.label, ...schema ? { schema } : {} },
208
- ask: s.ask,
209
- condition: s.if
210
- });
211
- fields[s.label] = s.output ? { type: "object" } : { type: "string" };
212
- addPrevEdges(edges, prevNodeId, nid);
213
- prevNodeId = nid;
214
- } else if ("parallel" in step) {
215
- const pb = step;
216
- const groupId = nextParallelLabel();
217
- const groupNodeIds = [];
218
- for (const child of pb.parallel) {
219
- const nid = nodeId(child.label);
220
- const schema = toJsonSchema(child.output);
221
- nodes.push({
222
- id: nid,
223
- type: "agent",
224
- name: child.label,
225
- input: { template: translate(child.prompt) },
226
- output: { key: child.label, ...schema ? { schema } : {} },
227
- ask: child.ask,
228
- condition: child.if,
229
- parallelGroup: groupId
230
- });
231
- fields[child.label] = child.output ? { type: "object" } : { type: "string" };
232
- groupNodeIds.push(nid);
233
- }
234
- addPrevEdges(edges, prevNodeId, groupNodeIds);
235
- prevNodeId = groupNodeIds;
236
- } else if ("map" in step) {
237
- const ms = step.map;
238
- const label = ms.label;
239
- const nid = nodeId(label);
240
- const innerSchema = toJsonSchema(ms.each?.output);
241
- const mapSchema = toJsonSchema(ms.output);
242
- nodes.push({
243
- id: nid,
244
- type: "map",
245
- name: label,
246
- source: `state.${ms.source}`,
247
- itemKey: "item",
248
- config: {
249
- batchSize: ms.batch ?? 50,
250
- maxConcurrency: ms.concurrency ?? 5,
251
- innerConcurrency: ms.concurrency ?? 5
252
- },
253
- node: {
254
- type: "agent",
255
- input: ms.each?.prompt ? { template: translate(ms.each.prompt) } : void 0,
256
- ...innerSchema ? { schema: innerSchema } : {}
257
- },
258
- output: { key: label, ...mapSchema ? { schema: mapSchema } : {} },
259
- condition: ms.if
260
- });
261
- fields[label] = { type: "array", default: [] };
262
- addPrevEdges(edges, prevNodeId, nid);
263
- prevNodeId = nid;
264
- }
265
- }
266
- const termNid = nodeId("__end");
267
- nodes.push({ id: termNid, type: "terminal", name: "__end", status: "success" });
268
- addPrevEdges(edges, prevNodeId, termNid);
269
- return { version: "1.0", name: wf.name || "workflow", state: { fields }, nodes, edges };
270
- }
271
- function addPrevEdges(edges, fromNodes, toNodes) {
272
- const fromList = Array.isArray(fromNodes) ? fromNodes : [fromNodes];
273
- const toList = Array.isArray(toNodes) ? toNodes : [toNodes];
274
- for (const from of fromList) {
275
- for (const to of toList) {
276
- if (!edges.some((e) => e.from === from && e.to === to)) {
277
- edges.push({ from, to });
278
- }
279
- }
280
- }
281
- }
282
- function validate(wf) {
283
- const labels = /* @__PURE__ */ new Set();
284
- for (const step of wf.steps) {
285
- if ("label" in step) {
286
- const s = step;
287
- if (labels.has(s.label)) throw new Error(`Duplicate step label "${s.label}"`);
288
- labels.add(s.label);
289
- } else if ("parallel" in step) {
290
- const pb = step;
291
- if (pb.parallel.length === 0) {
292
- throw new Error("parallel block must contain at least one child step");
293
- }
294
- for (const child of pb.parallel) {
295
- if (labels.has(child.label)) throw new Error(`Duplicate step label "${child.label}"`);
296
- labels.add(child.label);
297
- }
298
- } else if ("map" in step) {
299
- const ms = step.map;
300
- if (labels.has(ms.label)) throw new Error(`Duplicate step label "${ms.label}" (map step)`);
301
- labels.add(ms.label);
302
- }
303
- }
20
+ // src/workflow/WorkflowAbortRegistry.ts
21
+ var controllers = /* @__PURE__ */ new Map();
22
+ function registerWorkflowRun(runId) {
23
+ const existing = controllers.get(runId);
24
+ if (existing) return existing;
25
+ const controller = new AbortController();
26
+ controllers.set(runId, controller);
27
+ return controller;
28
+ }
29
+ function getWorkflowSignal(runId) {
30
+ return controllers.get(runId)?.signal;
31
+ }
32
+ function abortWorkflowRun(runId) {
33
+ const controller = controllers.get(runId);
34
+ if (!controller) return false;
35
+ controller.abort();
36
+ return true;
37
+ }
38
+ function unregisterWorkflowRun(runId) {
39
+ controllers.delete(runId);
304
40
  }
305
41
 
306
42
  // src/workflow/utils.ts
@@ -402,7 +138,7 @@ function renderTemplate(template, state, item) {
402
138
  return formatValue(item);
403
139
  }
404
140
  const itemPath = trimmed;
405
- const val = resolvePath({ item }, itemPath);
141
+ const val = resolvePath(item, itemPath);
406
142
  return val === void 0 ? "" : formatValue(val);
407
143
  }
408
144
  if (item && typeof item === "object") {
@@ -631,7 +367,8 @@ function createAgentNode(node, resolveAgent, trackingStore) {
631
367
  }
632
368
  try {
633
369
  console.log(`[WF][${node.id}] START "${node.name}" | hasSchema=${!!node.output?.schema}`);
634
- const responseFormat = node.output?.schema;
370
+ const hasRef = !!node.ref;
371
+ const responseFormat = !hasRef && node.output?.schema ? node.output.schema : void 0;
635
372
  console.log(`[WF][${node.id}] resolving agent...`);
636
373
  const stepType = node.ask ? "ask" : node.type;
637
374
  const client = await resolveAgent(node.ref, responseFormat, stepType);
@@ -642,20 +379,13 @@ function createAgentNode(node, resolveAgent, trackingStore) {
642
379
  void 0,
643
380
  responseFormat ? void 0 : node.output?.schema
644
381
  );
645
- if (node.ask) {
646
- input.messages = [
647
- new SystemMessage(
648
- "Follow this exact procedure. Do NOT skip any step.\n\nSTEP 1: Read the prompt. It contains content to present to the user and a question to ask.\n\nSTEP 2: Call ask_user_to_clarify. Your question text MUST contain the ACTUAL content from the prompt.\n\nSTEP 3: The tool will pause. The user will see your questions and respond. You will receive their answers.\n\nSTEP 4: Based on the user's answers, produce your final output."
649
- ),
650
- ...input.messages
651
- ];
652
- }
653
382
  const renderedInput = input.messages[0]?.content ?? "";
654
383
  console.log(`[WF][${node.id}] === INPUT (full) ===
655
384
  ${renderedInput}
656
385
  === END INPUT ===`);
657
386
  const subConfig = {
658
387
  ...config,
388
+ signal: getWorkflowSignal(runId),
659
389
  configurable: {
660
390
  ...config?.configurable ?? {},
661
391
  thread_id: `${config?.configurable?.thread_id ?? "default"}:${node.id}`
@@ -705,7 +435,7 @@ ${renderedInput}
705
435
  console.log(`[WF][${node.id}] invoke FAILED: ${errMsg}`);
706
436
  if (responseFormat && (errMsg.includes("tool_choice") || errMsg.includes("thinking") || errMsg.includes("reasoning") || errMsg.includes("InvalidParameter"))) {
707
437
  console.log(`[WF][${node.id}] attempting fallback (text-based schema injection)...`);
708
- const fallbackClient = await resolveAgent(node.ref, void 0, node.type);
438
+ const fallbackClient = await resolveAgent(node.ref, void 0, stepType);
709
439
  const fallbackInput = buildInput(node.input, state, void 0, responseFormat);
710
440
  result = await invokeWithRetry(
711
441
  () => fallbackClient.invoke(fallbackInput, subConfig),
@@ -749,6 +479,26 @@ ${JSON.stringify(output, null, 2)}
749
479
  }
750
480
  return update;
751
481
  } catch (err) {
482
+ if (err?.name === "AbortError") {
483
+ if (trackingStore && runId) {
484
+ if (stepId) {
485
+ trackingStore.updateRunStep(runId, stepId, {
486
+ status: "cancelled",
487
+ errorMessage: "Aborted by user",
488
+ completedAt: /* @__PURE__ */ new Date(),
489
+ durationMs: Date.now() - startedAt
490
+ }).catch(() => {
491
+ });
492
+ }
493
+ trackingStore.updateWorkflowRun(runId, {
494
+ status: "cancelled",
495
+ errorMessage: "Aborted by user",
496
+ completedAt: /* @__PURE__ */ new Date()
497
+ }).catch(() => {
498
+ });
499
+ }
500
+ throw err;
501
+ }
752
502
  if (err?.name === "GraphInterrupt") {
753
503
  throw err;
754
504
  }
@@ -848,15 +598,17 @@ function createMapNode(node, resolveAgent, trackingStore) {
848
598
  const itemKey = node.itemKey ?? "item";
849
599
  const innerClient = await resolveAgent(node.node.ref, node.node.schema, node.type);
850
600
  const batches = chunk(items, batchSize);
851
- const batchResults = await parallelLimit(batches, maxConcurrency, async (batch) => {
601
+ const batchResults = await parallelLimit(batches, maxConcurrency, async (batch, batchIdx) => {
852
602
  const itemResults = await parallelLimit(batch, innerConcurrency, async (item, itemIdx) => {
603
+ const globalIdx = batchIdx * batchSize + itemIdx;
853
604
  const itemCtx = { [itemKey]: item };
854
605
  const input = buildInput(node.node.input, state, itemCtx);
855
606
  const subConfig = {
856
607
  ...config,
608
+ signal: getWorkflowSignal(runId),
857
609
  configurable: {
858
610
  ...config?.configurable ?? {},
859
- thread_id: `${config?.configurable?.thread_id ?? "default"}:${node.id}:item:${itemIdx}`
611
+ thread_id: `${config?.configurable?.thread_id ?? "default"}:${node.id}:item:${globalIdx}`
860
612
  }
861
613
  };
862
614
  const result = await invokeWithRetry(
@@ -870,26 +622,7 @@ function createMapNode(node, resolveAgent, trackingStore) {
870
622
  return itemResults;
871
623
  });
872
624
  const allResults = batchResults.flat();
873
- let finalOutput = allResults;
874
- if (node.reduce) {
875
- const reduceClient = await resolveAgent(node.reduce.ref, node.reduce.schema, node.type);
876
- const tempResultsKey = node.output?.key ?? `${node.id}_results`;
877
- const tempState = { ...state, [tempResultsKey]: allResults };
878
- const reduceInput = buildInput(node.reduce.input, tempState);
879
- const reduceResult = await invokeWithRetry(
880
- () => reduceClient.invoke(reduceInput, {
881
- ...config,
882
- configurable: {
883
- ...config?.configurable ?? {},
884
- thread_id: `${config?.configurable?.thread_id ?? "default"}:${node.id}:reduce`
885
- }
886
- }),
887
- node.config?.maxRetries ?? 0,
888
- node.config?.retryOn,
889
- node.config?.timeout
890
- );
891
- finalOutput = extractOutput(reduceResult);
892
- }
625
+ const finalOutput = allResults;
893
626
  const update = { phase: node.id };
894
627
  if (node.output?.key) {
895
628
  update[node.output.key] = finalOutput;
@@ -906,6 +639,26 @@ function createMapNode(node, resolveAgent, trackingStore) {
906
639
  }
907
640
  return update;
908
641
  } catch (err) {
642
+ if (err?.name === "AbortError") {
643
+ if (trackingStore && runId) {
644
+ if (stepId) {
645
+ trackingStore.updateRunStep(runId, stepId, {
646
+ status: "cancelled",
647
+ errorMessage: "Aborted by user",
648
+ completedAt: /* @__PURE__ */ new Date(),
649
+ durationMs: Date.now() - startedAt
650
+ }).catch(() => {
651
+ });
652
+ }
653
+ trackingStore.updateWorkflowRun(runId, {
654
+ status: "cancelled",
655
+ errorMessage: "Aborted by user",
656
+ completedAt: /* @__PURE__ */ new Date()
657
+ }).catch(() => {
658
+ });
659
+ }
660
+ throw err;
661
+ }
909
662
  if (err?.name === "GraphInterrupt") {
910
663
  if (trackingStore && runId && stepId) {
911
664
  trackingStore.updateRunStep(runId, stepId, { status: "interrupted" }).catch(() => {
@@ -950,6 +703,17 @@ function createInputNode(node, trackingStore) {
950
703
  console.log(`[WF][n_input] input captured: "${inputText.slice(0, 200)}"`);
951
704
  const update = { phase: node.id };
952
705
  if (node.output?.key) update[node.output.key] = inputText;
706
+ if (trackingStore) {
707
+ try {
708
+ const existingRuns = await trackingStore.getWorkflowRunsByThreadId(tenantId, threadId);
709
+ const wasAborted = existingRuns?.some((r) => r.status === "cancelled" && r.assistantId === assistantId);
710
+ if (wasAborted) {
711
+ console.log(`[WF][n_input] Workflow was aborted before restart, skipping execution`);
712
+ throw new Error("Workflow was aborted \u2014 execution cancelled");
713
+ }
714
+ } catch (e) {
715
+ }
716
+ }
953
717
  if (trackingStore && !state._runId) {
954
718
  try {
955
719
  const run = await trackingStore.createWorkflowRun({
@@ -960,6 +724,7 @@ function createInputNode(node, trackingStore) {
960
724
  metadata: { workflowName: node.name }
961
725
  });
962
726
  update._runId = run.id;
727
+ registerWorkflowRun(run.id);
963
728
  await trackingStore.createRunStep({
964
729
  runId: run.id,
965
730
  tenantId,
@@ -1012,6 +777,7 @@ function createTerminalNode(node, trackingStore) {
1012
777
  console.warn(`[WF][terminal] Failed to create terminal RunStep:`, e.message);
1013
778
  }
1014
779
  }
780
+ if (runId) unregisterWorkflowRun(runId);
1015
781
  return {
1016
782
  status: node.status,
1017
783
  phase: node.id
@@ -1120,9 +886,10 @@ function validateAndThrow(dsl) {
1120
886
  }
1121
887
 
1122
888
  export {
1123
- toJsonSchema,
1124
- toSafeStateExpr,
1125
- parseYaml,
889
+ registerWorkflowRun,
890
+ getWorkflowSignal,
891
+ abortWorkflowRun,
892
+ unregisterWorkflowRun,
1126
893
  buildStateAnnotation,
1127
894
  resolvePath,
1128
895
  renderTemplate,
@@ -1137,4 +904,4 @@ export {
1137
904
  compileInternal,
1138
905
  validateDSL
1139
906
  };
1140
- //# sourceMappingURL=chunk-SGRFQY3E.mjs.map
907
+ //# sourceMappingURL=chunk-BKUBS44O.mjs.map