graphai 0.6.3 → 0.6.5

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/lib/graphai.js CHANGED
@@ -16,15 +16,13 @@ class GraphAI {
16
16
  createNodes(data) {
17
17
  const nodes = Object.keys(data.nodes).reduce((_nodes, nodeId) => {
18
18
  const nodeData = data.nodes[nodeId];
19
- if ("value" in nodeData) {
20
- _nodes[nodeId] = new node_1.StaticNode(nodeId, nodeData, this);
21
- }
22
- else if ("agent" in nodeData) {
19
+ if ("agent" in nodeData) {
23
20
  _nodes[nodeId] = new node_1.ComputedNode(this.graphId, nodeId, nodeData, this);
24
21
  }
25
22
  else {
26
- throw new Error("Unknown node type (neither value nor agent): " + nodeId);
23
+ _nodes[nodeId] = new node_1.StaticNode(nodeId, nodeData, this);
27
24
  }
25
+ // throw new Error("Unknown node type (neither value nor agent): " + nodeId);
28
26
  return _nodes;
29
27
  }, {});
30
28
  // Generate the waitlist for each node.
@@ -193,6 +191,11 @@ class GraphAI {
193
191
  }
194
192
  // Public API
195
193
  async run(all = false) {
194
+ if (Object.values(this.nodes)
195
+ .filter((node) => node.isStaticNode)
196
+ .some((node) => node.result === undefined && node.update === undefined)) {
197
+ throw new Error("Static node must have value. Set value or injectValue or set update");
198
+ }
196
199
  if (this.isRunning()) {
197
200
  throw new Error("This GraphUI instance is already running");
198
201
  }
package/lib/node.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { GraphAI, GraphData } from "./index";
2
- import { NodeDataParams, ResultData, DataSource, ComputedNodeData, StaticNodeData, NodeState } from "./type";
2
+ import { NodeDataParams, ResultData, DataSource, ComputedNodeData, StaticNodeData, NodeState, ConsoleElement } from "./type";
3
3
  import { TransactionLog } from "./transaction_log";
4
4
  export declare class Node {
5
5
  readonly nodeId: string;
@@ -8,7 +8,7 @@ export declare class Node {
8
8
  result: ResultData | undefined;
9
9
  protected graph: GraphAI;
10
10
  protected log: TransactionLog;
11
- protected console: Record<string, string | boolean>;
11
+ protected console: ConsoleElement;
12
12
  constructor(nodeId: string, graph: GraphAI);
13
13
  asString(): string;
14
14
  protected onSetResult(): void;
@@ -19,7 +19,6 @@ export declare class ComputedNode extends Node {
19
19
  readonly isResult: boolean;
20
20
  readonly params: NodeDataParams;
21
21
  private readonly filterParams;
22
- private readonly dynamicParams;
23
22
  readonly nestedGraph?: GraphData | DataSource;
24
23
  readonly retryLimit: number;
25
24
  retryCount: number;
@@ -33,9 +32,12 @@ export declare class ComputedNode extends Node {
33
32
  readonly anyInput: boolean;
34
33
  dataSources: DataSource[];
35
34
  private inputs?;
35
+ private output?;
36
36
  pendings: Set<string>;
37
37
  private ifSource?;
38
38
  private unlessSource?;
39
+ private defaultValue?;
40
+ private isSkip;
39
41
  readonly isStaticNode = false;
40
42
  readonly isComputedNode = true;
41
43
  constructor(graphId: string, nodeId: string, data: ComputedNodeData, graph: GraphAI);
@@ -51,9 +53,9 @@ export declare class ComputedNode extends Node {
51
53
  private shouldApplyAgentFilter;
52
54
  private agentFilterHandler;
53
55
  execute(): Promise<void>;
56
+ private afterExecute;
54
57
  private prepareExecute;
55
58
  private errorProcess;
56
- private getParams;
57
59
  private getContext;
58
60
  private getResult;
59
61
  private getDebugInfo;
package/lib/node.js CHANGED
@@ -6,6 +6,7 @@ const nodeUtils_1 = require("./utils/nodeUtils");
6
6
  const type_1 = require("./type");
7
7
  const utils_2 = require("./utils/utils");
8
8
  const transaction_log_1 = require("./transaction_log");
9
+ const result_1 = require("./utils/result");
9
10
  class Node {
10
11
  constructor(nodeId, graph) {
11
12
  this.waitlist = new Set(); // List of nodes which need data from this node.
@@ -31,7 +32,7 @@ class Node {
31
32
  });
32
33
  }
33
34
  afterConsoleLog(result) {
34
- if (this.console.after === true) {
35
+ if (this.console === true || this.console.after === true) {
35
36
  console.log(typeof result === "string" ? result : JSON.stringify(result, null, 2));
36
37
  }
37
38
  else if (this.console.after) {
@@ -45,6 +46,7 @@ class ComputedNode extends Node {
45
46
  super(nodeId, graph);
46
47
  this.retryCount = 0;
47
48
  this.dataSources = []; // no longer needed. This is for transaction log.
49
+ this.isSkip = false;
48
50
  this.isStaticNode = false;
49
51
  this.isComputedNode = true;
50
52
  this.graphId = graphId;
@@ -58,7 +60,8 @@ class ComputedNode extends Node {
58
60
  this.priority = data.priority ?? 0;
59
61
  this.anyInput = data.anyInput ?? false;
60
62
  this.inputs = data.inputs;
61
- this.dataSources = data.inputs ? (0, nodeUtils_1.inputs2dataSources)(data.inputs).flat(10) : [];
63
+ this.output = data.output;
64
+ this.dataSources = [...(data.inputs ? (0, nodeUtils_1.inputs2dataSources)(data.inputs).flat(10) : []), ...(data.params ? (0, nodeUtils_1.inputs2dataSources)(data.params).flat(10) : [])];
62
65
  if (data.inputs && Array.isArray(data.inputs)) {
63
66
  throw new Error(`array inputs have been deprecated. nodeId: ${nodeId}: see https://github.com/receptron/graphai/blob/main/docs/NamedInputs.md`);
64
67
  }
@@ -83,15 +86,10 @@ class ComputedNode extends Node {
83
86
  if (data.unless) {
84
87
  this.unlessSource = this.addPendingNode(data.unless);
85
88
  }
86
- this.dynamicParams = Object.keys(this.params).reduce((tmp, key) => {
87
- const dataSource = (0, utils_2.parseNodeName)(this.params[key]);
88
- if (dataSource.nodeId) {
89
- (0, utils_2.assert)(!this.anyInput, "Dynamic params are not supported with anyInput");
90
- tmp[key] = dataSource;
91
- this.pendings.add(dataSource.nodeId);
92
- }
93
- return tmp;
94
- }, {});
89
+ if (data.defaultValue) {
90
+ this.defaultValue = data.defaultValue;
91
+ }
92
+ this.isSkip = false;
95
93
  this.log.initForComputedNode(this, graph);
96
94
  }
97
95
  getAgentId() {
@@ -107,8 +105,9 @@ class ComputedNode extends Node {
107
105
  if (this.state !== type_1.NodeState.Waiting || this.pendings.size !== 0) {
108
106
  return false;
109
107
  }
110
- if ((this.ifSource && !(0, utils_2.isLogicallyTrue)(this.graph.resultOf(this.ifSource))) ||
111
- (this.unlessSource && (0, utils_2.isLogicallyTrue)(this.graph.resultOf(this.unlessSource)))) {
108
+ this.isSkip = !!((this.ifSource && !(0, utils_2.isLogicallyTrue)(this.graph.resultOf(this.ifSource))) ||
109
+ (this.unlessSource && (0, utils_2.isLogicallyTrue)(this.graph.resultOf(this.unlessSource))));
110
+ if (this.isSkip && this.defaultValue === undefined) {
112
111
  this.state = type_1.NodeState.Skipped;
113
112
  this.log.onSkipped(this, this.graph);
114
113
  return false;
@@ -202,6 +201,10 @@ class ComputedNode extends Node {
202
201
  // then it removes itself from the "running node" list of the graph.
203
202
  // Notice that setting the result of this node may make other nodes ready to run.
204
203
  async execute() {
204
+ if (this.isSkip) {
205
+ this.afterExecute(this.defaultValue, []);
206
+ return;
207
+ }
205
208
  const previousResults = this.graph.resultsOf(this.inputs, this.anyInput);
206
209
  const transactionId = Date.now();
207
210
  this.prepareExecute(transactionId, Object.values(previousResults));
@@ -218,16 +221,7 @@ class ComputedNode extends Node {
218
221
  // if this is a nested agent or not.
219
222
  if (this.nestedGraph) {
220
223
  this.graph.taskManager.prepareForNesting();
221
- // context.taskManager = this.graph.taskManager;
222
224
  context.onLogCallback = this.graph.onLogCallback;
223
- /*
224
- if ("nodes" in this.nestedGraph) {
225
- context.graphData = this.nestedGraph;
226
- } else {
227
- context.graphData = this.graph.resultOf(this.nestedGraph) as GraphData; // HACK: compiler work-around
228
- }
229
- */
230
- // context.agents = this.graph.agentFunctionInfoDictionary;
231
225
  context.forNestedGraph = {
232
226
  graphData: "nodes" in this.nestedGraph ? this.nestedGraph : this.graph.resultOf(this.nestedGraph), // HACK: compiler work-around
233
227
  agents: this.graph.agentFunctionInfoDictionary,
@@ -253,16 +247,23 @@ class ComputedNode extends Node {
253
247
  console.log(`-- transactionId mismatch with ${this.nodeId} (probably timeout)`);
254
248
  return;
255
249
  }
256
- this.state = type_1.NodeState.Completed;
257
- this.result = this.getResult(result);
258
- this.log.onComplete(this, this.graph, localLog);
259
- this.onSetResult();
260
- this.graph.onExecutionComplete(this);
250
+ // after process
251
+ this.afterExecute(result, localLog);
261
252
  }
262
253
  catch (error) {
263
254
  this.errorProcess(error, transactionId, previousResults);
264
255
  }
265
256
  }
257
+ afterExecute(result, localLog) {
258
+ this.state = type_1.NodeState.Completed;
259
+ this.result = this.getResult(result);
260
+ if (this.output) {
261
+ this.result = (0, result_1.resultsOf)(this.output, { self: this }, this.graph.propFunctions, true);
262
+ }
263
+ this.log.onComplete(this, this.graph, localLog);
264
+ this.onSetResult();
265
+ this.graph.onExecutionComplete(this);
266
+ }
266
267
  // This private method (called only by execute()) prepares the ComputedNode object
267
268
  // for execution, and create a new transaction to record it.
268
269
  prepareExecute(transactionId, inputs) {
@@ -292,24 +293,9 @@ class ComputedNode extends Node {
292
293
  this.retry(type_1.NodeState.Failed, Error("Unknown"));
293
294
  }
294
295
  }
295
- getParams() {
296
- return Object.keys(this.dynamicParams).reduce((tmp, key) => {
297
- const result = this.graph.resultOf(this.dynamicParams[key]);
298
- tmp[key] = result;
299
- return tmp;
300
- }, { ...this.params });
301
- }
302
- /*
303
- private getInputs(previousResults: Record<string, ResultData | undefined>) {
304
- if (Array.isArray(this.inputs)) {
305
- return (this.inputs ?? []).map((key) => previousResults[String(key)]).filter((a) => !this.anyInput || a);
306
- }
307
- return [];
308
- }
309
- */
310
296
  getContext(previousResults, localLog) {
311
297
  const context = {
312
- params: this.getParams(),
298
+ params: this.graph.resultsOf(this.params),
313
299
  namedInputs: previousResults,
314
300
  inputSchema: this.agentFunction ? undefined : this.graph.getAgentFunctionInfo(this.agentId)?.inputs,
315
301
  debugInfo: this.getDebugInfo(),
@@ -343,7 +329,7 @@ class ComputedNode extends Node {
343
329
  };
344
330
  }
345
331
  beforeConsoleLog(context) {
346
- if (this.console.before === true) {
332
+ if (this.console === true || this.console.before === true) {
347
333
  console.log(JSON.stringify(context.namedInputs, null, 2));
348
334
  }
349
335
  else if (this.console.before) {
package/lib/type.d.ts CHANGED
@@ -23,11 +23,16 @@ export type DataSource = {
23
23
  value?: any;
24
24
  propIds?: string[];
25
25
  };
26
+ type ConsoleAttribute = true | string | Record<string, any>;
27
+ export type ConsoleElement = true | {
28
+ before?: ConsoleAttribute;
29
+ after?: ConsoleAttribute;
30
+ };
26
31
  export type StaticNodeData = {
27
- value: ResultData;
32
+ value?: ResultData;
28
33
  update?: string;
29
34
  isResult?: boolean;
30
- console?: Record<string, string | boolean>;
35
+ console?: ConsoleElement;
31
36
  };
32
37
  export type AgentAnonymousFunction = (...params: any[]) => unknown;
33
38
  export type AgentFilterParams = Record<string, any>;
@@ -37,7 +42,8 @@ export type GraphDataLoaderOption = {
37
42
  };
38
43
  export type ComputedNodeData = {
39
44
  agent: string | AgentAnonymousFunction;
40
- inputs?: Array<any> | Record<string, any>;
45
+ inputs?: Record<string, any>;
46
+ output?: Record<string, any>;
41
47
  anyInput?: boolean;
42
48
  params?: NodeDataParams;
43
49
  filterParams?: AgentFilterParams;
@@ -45,12 +51,13 @@ export type ComputedNodeData = {
45
51
  timeout?: number;
46
52
  if?: string;
47
53
  unless?: string;
54
+ defaultValue?: ResultData;
48
55
  graph?: GraphData | string;
49
56
  graphLoader?: GraphDataLoaderOption;
50
57
  isResult?: boolean;
51
58
  priority?: number;
52
59
  passThrough?: PassThrough;
53
- console?: Record<string, string | boolean>;
60
+ console?: ConsoleElement;
54
61
  };
55
62
  export type NodeData = StaticNodeData | ComputedNodeData;
56
63
  export type LoopData = {
@@ -137,3 +144,4 @@ export type AgentFunctionInfo = {
137
144
  };
138
145
  export type AgentFunctionInfoDictionary = Record<string, AgentFunctionInfo>;
139
146
  export type PropFunction = (result: ResultData, propId: string) => ResultData;
147
+ export {};
@@ -18,7 +18,7 @@ const propArrayFunction = (result, propId) => {
18
18
  return result.length === 0;
19
19
  }
20
20
  // array join
21
- const matchJoin = propId.match(/^join\(([,-]?)\)$/);
21
+ const matchJoin = propId.match(/^join\(([,-\s]?)\)$/);
22
22
  if (matchJoin && Array.isArray(matchJoin)) {
23
23
  return result.join(matchJoin[1] ?? "");
24
24
  }
@@ -1,6 +1,6 @@
1
1
  import { DataSource, ResultData, PropFunction } from "../type";
2
2
  import { GraphNodes } from "../node";
3
- export declare const resultsOf: (inputs: Record<string, any> | Array<string>, nodes: GraphNodes, propFunctions: PropFunction[]) => Record<string, ResultData>;
3
+ export declare const resultsOf: (inputs: Record<string, any>, nodes: GraphNodes, propFunctions: PropFunction[], isSelfNode?: boolean) => Record<string, ResultData>;
4
4
  export declare const resultOf: (source: DataSource, nodes: GraphNodes, propFunctions: PropFunction[]) => ResultData;
5
5
  export declare const cleanResultInner: (results: ResultData) => ResultData | null;
6
6
  export declare const cleanResult: (results: Record<string, ResultData | undefined>) => Record<string, ResultData>;
@@ -3,35 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.cleanResult = exports.cleanResultInner = exports.resultOf = exports.resultsOf = void 0;
4
4
  const utils_1 = require("../utils/utils");
5
5
  const data_source_1 = require("../utils/data_source");
6
- const resultsOfInner = (input, nodes, propFunctions) => {
6
+ const resultsOfInner = (input, nodes, propFunctions, isSelfNode = false) => {
7
7
  if (Array.isArray(input)) {
8
- return input.map((inp) => resultsOfInner(inp, nodes, propFunctions));
8
+ return input.map((inp) => resultsOfInner(inp, nodes, propFunctions, isSelfNode));
9
9
  }
10
10
  if ((0, utils_1.isNamedInputs)(input)) {
11
- return (0, exports.resultsOf)(input, nodes, propFunctions);
11
+ return (0, exports.resultsOf)(input, nodes, propFunctions, isSelfNode);
12
12
  }
13
13
  if (typeof input === "string") {
14
14
  const templateMatch = [...input.matchAll(/\${(:[^}]+)}/g)].map((m) => m[1]);
15
15
  if (templateMatch.length > 0) {
16
- const results = resultsOfInner(templateMatch, nodes, propFunctions);
16
+ const results = resultsOfInner(templateMatch, nodes, propFunctions, isSelfNode);
17
17
  return Array.from(templateMatch.keys()).reduce((tmp, key) => {
18
18
  return tmp.replaceAll("${" + templateMatch[key] + "}", results[key]);
19
19
  }, input);
20
20
  }
21
21
  }
22
- return (0, exports.resultOf)((0, utils_1.parseNodeName)(input), nodes, propFunctions);
22
+ return (0, exports.resultOf)((0, utils_1.parseNodeName)(input, isSelfNode), nodes, propFunctions);
23
23
  };
24
- const resultsOf = (inputs, nodes, propFunctions) => {
25
- // for inputs. TODO remove if array input is not supported
26
- if (Array.isArray(inputs)) {
27
- return inputs.reduce((tmp, key) => {
28
- tmp[key] = resultsOfInner(key, nodes, propFunctions);
29
- return tmp;
30
- }, {});
31
- }
24
+ const resultsOf = (inputs, nodes, propFunctions, isSelfNode = false) => {
32
25
  return Object.keys(inputs).reduce((tmp, key) => {
33
26
  const input = inputs[key];
34
- tmp[key] = (0, utils_1.isNamedInputs)(input) ? (0, exports.resultsOf)(input, nodes, propFunctions) : resultsOfInner(input, nodes, propFunctions);
27
+ tmp[key] = (0, utils_1.isNamedInputs)(input) ? (0, exports.resultsOf)(input, nodes, propFunctions, isSelfNode) : resultsOfInner(input, nodes, propFunctions, isSelfNode);
35
28
  return tmp;
36
29
  }, {});
37
30
  };
@@ -1,6 +1,6 @@
1
1
  import { DataSource, AgentFunction, DefaultInputData } from "../type";
2
2
  export declare const sleep: (milliseconds: number) => Promise<unknown>;
3
- export declare const parseNodeName: (inputNodeId: any) => DataSource;
3
+ export declare const parseNodeName: (inputNodeId: any, isSelfNode?: boolean) => DataSource;
4
4
  export declare function assert(condition: boolean, message: string, isWarn?: boolean): asserts condition;
5
5
  export declare const isObject: (x: unknown) => x is object;
6
6
  export declare const isNull: (data: unknown) => data is null | undefined;
@@ -6,7 +6,14 @@ const sleep = async (milliseconds) => {
6
6
  return await new Promise((resolve) => setTimeout(resolve, milliseconds));
7
7
  };
8
8
  exports.sleep = sleep;
9
- const parseNodeName = (inputNodeId) => {
9
+ const parseNodeName = (inputNodeId, isSelfNode = false) => {
10
+ if (isSelfNode) {
11
+ if (typeof inputNodeId === "string" && inputNodeId[0] === ".") {
12
+ const parts = inputNodeId.split(".");
13
+ return { nodeId: "self", propIds: parts.slice(1) };
14
+ }
15
+ return { value: inputNodeId };
16
+ }
10
17
  if (typeof inputNodeId === "string") {
11
18
  const regex = /^:(.*)$/;
12
19
  const match = inputNodeId.match(regex);
package/lib/validator.js CHANGED
@@ -15,7 +15,7 @@ const validateGraphData = (data, agentIds) => {
15
15
  const graphAgentIds = new Set();
16
16
  Object.keys(data.nodes).forEach((nodeId) => {
17
17
  const node = data.nodes[nodeId];
18
- const isStaticNode = "value" in node;
18
+ const isStaticNode = !("agent" in node);
19
19
  (0, nodeValidator_1.nodeValidator)(node);
20
20
  const agentId = isStaticNode ? "" : node.agent;
21
21
  isStaticNode && (0, static_node_validator_1.staticNodeValidator)(node) && staticNodeIds.push(nodeId);
@@ -4,6 +4,7 @@ exports.ValidationError = exports.staticNodeAttributeKeys = exports.computedNode
4
4
  exports.graphDataAttributeKeys = ["nodes", "concurrency", "agentId", "loop", "verbose", "version"];
5
5
  exports.computedNodeAttributeKeys = [
6
6
  "inputs",
7
+ "output",
7
8
  "anyInput",
8
9
  "params",
9
10
  "retry",
@@ -15,6 +16,7 @@ exports.computedNodeAttributeKeys = [
15
16
  "priority",
16
17
  "if",
17
18
  "unless",
19
+ "defaultValue",
18
20
  "filterParams",
19
21
  "console",
20
22
  "passThrough",
@@ -6,9 +6,9 @@ const nodeValidator = (nodeData) => {
6
6
  if (nodeData.agent && nodeData.value) {
7
7
  throw new common_1.ValidationError("Cannot set both agent and value");
8
8
  }
9
- if (!("agent" in nodeData) && !("value" in nodeData)) {
10
- throw new common_1.ValidationError("Either agent or value is required");
11
- }
9
+ // if (!("agent" in nodeData) && !("value" in nodeData)) {
10
+ // throw new ValidationError("Either agent or value is required");
11
+ // }
12
12
  return true;
13
13
  };
14
14
  exports.nodeValidator = nodeValidator;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphai",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "Asynchronous data flow execution engine for agentic AI apps.",
5
5
  "main": "lib/bundle.cjs.js",
6
6
  "module": "lib/bundle.esm.js",