graphai 0.5.7 → 0.5.9

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/README.md CHANGED
@@ -22,41 +22,53 @@ nodes:
22
22
  query: describe the final sentence by the court for Sam Bank-Fried
23
23
  wikipedia: // Retrieve data from Wikipedia
24
24
  agent: wikipediaAgent
25
- inputs: [:source.name]
25
+ inputs:
26
+ query: :source.name
26
27
  chunks: // Break the text from Wikipedia into chunks (2048 character each with 512 overlap)
27
28
  agent: stringSplitterAgent
28
- inputs: [:wikipedia]
29
+ inputs:
30
+ text: :wikipedia
29
31
  chunkEmbeddings: // Get embedding vector of each chunk
30
32
  agent: stringEmbeddingsAgent
31
- inputs: [:chunks]
33
+ inputs:
34
+ array: :chunks
32
35
  topicEmbedding: // Get embedding vector of the question
33
36
  agent: stringEmbeddingsAgent
34
- inputs: [:source.query]
37
+ inputs:
38
+ item: :source.query
35
39
  similarities: // Calculate the cosine similarity of each chunk
36
40
  agent: dotProductAgent
37
- inputs: [:chunkEmbeddings, :topicEmbedding]
41
+ inputs:
42
+ matrix: :chunkEmbeddings
43
+ vector: :topicEmbedding
38
44
  sortedChunks: // Sort chunks based on their similarities
39
45
  agent: sortByValuesAgent
40
- inputs: [:chunks, :similarities]
46
+ inputs:
47
+ array: :chunks
48
+ values: :similarities
41
49
  referenceText: // Concatenate chunks up to the token limit (5000)
42
50
  agent: tokenBoundStringsAgent
43
- inputs: [:sortedChunks]
51
+ inputs:
52
+ array: :sortedChunks
44
53
  params:
45
54
  limit: 5000
46
55
  prompt: // Generate a prompt with that reference text
47
56
  agent: stringTemplateAgent
48
- inputs: [:source, :referenceText]
57
+ inputs:
58
+ prompt: :source
59
+ text: :referenceText
49
60
  params:
50
61
  template: |-
51
- Using the following document, ${0}
52
- ${1}
62
+ Using the following document, ${prompt}
63
+ ${text}
53
64
  query: // retrieves the answer from GPT3.5
54
65
  agent: slashGPTAgent
55
66
  params:
56
67
  manifest:
57
68
  model: gpt-3.5-turbo
58
69
  isResult: true // indicating this is the final result
59
- inputs: [:prompt]
70
+ inputs:
71
+ prompt: :prompt
60
72
  ```
61
73
 
62
74
  ```mermaid
@@ -134,8 +146,10 @@ Here is an examnple (from [weather chat](https://github.com/receptron/graphai/bl
134
146
  ```typescript
135
147
  messagesWithUserInput: {
136
148
  // Appends the user's input to the messages.
137
- agent: (messages: Array<any>, content: string) => [...messages, { role: "user", content }],
138
- inputs: [":messages", ":userInput"],
149
+ agent: ({ messages: Array<any>, content: string }) => [...messages, { role: "user", content }],
150
+ inputs:
151
+ messages: ":messages"
152
+ content: ":userInput"
139
153
  if: "checkInput",
140
154
  },
141
155
  ```
@@ -186,8 +200,9 @@ nodes:
186
200
  question:
187
201
  value: "Find out which materials we need to purchase this week for Joe Smith's residential house project."
188
202
  projectId: // identifies the projectId from the question
189
- agent: "identifierAgent"
190
- inputs: [":source"] // == "sourceNode.query"
203
+ agent: identifierAgent
204
+ inputs:
205
+ id: :source
191
206
  database:
192
207
  agent: "nestedAgent"
193
208
  inputs:
@@ -201,11 +216,13 @@ nodes:
201
216
  ... // issue query to the database and build an appropriate prompt with it.
202
217
  query: // send the generated prompt to the LLM
203
218
  agent: "llama3Agent"
204
- inputs: [":prompt"]
219
+ inputs:
220
+ promot: ":prompt"
205
221
  isResult: true
206
222
  response: // Deliver the answer
207
223
  agent: "deliveryAgent"
208
- inputs: [:database.query.$last.content]
224
+ inputs:
225
+ text: :database.query.$last.content
209
226
  ```
210
227
 
211
228
  The databaseQuery node (which is associated "nestedAgent") takes the data from "question" node abd "projectId" node, and make them available to inner nodes (nodes of the child graph) via phantom node, "$0" and "$1". After the completion of the child graph, the data from "query" node (which has "isResult" property) becomes available as a property of the output of "database" node.
@@ -256,7 +273,8 @@ nodes:
256
273
  isResult: true
257
274
  retriever:
258
275
  agent: shift
259
- inputs: [people]
276
+ inputs:
277
+ array: :people
260
278
  query:
261
279
  agent: slashgpt
262
280
  params:
@@ -265,7 +283,9 @@ nodes:
265
283
  inputs: [:retriever.item]
266
284
  reducer:
267
285
  agent: push
268
- inputs: [:result, :query.content]
286
+ inputs:
287
+ array: :result
288
+ item: :query.content
269
289
  ```
270
290
 
271
291
  ```mermaid
@@ -353,7 +373,8 @@ This property is particularly useful when you want to continue the flow regardle
353
373
  // Receives messages from either case.
354
374
  agent: "copyAgent",
355
375
  anyInput: true,
356
- inputs: [":no_tool_calls", ":tool_calls.messagesWithSecondRes"],
376
+ inputs:
377
+ array: [":no_tool_calls", ":tool_calls.messagesWithSecondRes"],
357
378
  },
358
379
  ```
359
380
 
package/lib/graphai.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- import { AgentFunctionInfoDictionary, AgentFilterInfo, GraphData, DataSource, ResultDataDictionary, ResultData, DefaultResultData, GraphOptions, NestedDataSource } from "./type";
1
+ import { AgentFunctionInfoDictionary, AgentFilterInfo, GraphData, DataSource, ResultDataDictionary, ResultData, DefaultResultData, GraphOptions } from "./type";
2
2
  import { TransactionLog } from "./transaction_log";
3
- import { ComputedNode, StaticNode } from "./node";
3
+ import { ComputedNode, GraphNodes } from "./node";
4
4
  import { TaskManager } from "./task_manager";
5
- type GraphNodes = Record<string, ComputedNode | StaticNode>;
6
5
  export declare const defaultConcurrency = 8;
7
6
  export declare const graphDataLatestVersion = 0.5;
8
7
  export declare class GraphAI {
@@ -24,7 +23,8 @@ export declare class GraphAI {
24
23
  private repeatCount;
25
24
  private createNodes;
26
25
  private getValueFromResults;
27
- private initializeNodes;
26
+ private initializeStaticNodes;
27
+ private updateStaticNodes;
28
28
  constructor(data: GraphData, agentFunctionInfoDictionary: AgentFunctionInfoDictionary, options?: GraphOptions);
29
29
  getAgentFunctionInfo(agentId?: string): import("./type").AgentFunctionInfo | {
30
30
  agent: () => Promise<null>;
@@ -46,8 +46,6 @@ export declare class GraphAI {
46
46
  updateLog(log: TransactionLog): void;
47
47
  transactionLogs(): TransactionLog[];
48
48
  injectValue(nodeId: string, value: ResultData, injectFrom?: string): void;
49
- private nestedResultOf;
50
- resultsOf(sources: NestedDataSource): Record<string, ResultData>;
49
+ resultsOf(inputs?: Array<any> | Record<string, any>, anyInput?: boolean): Record<string, ResultData>;
51
50
  resultOf(source: DataSource): ResultData;
52
51
  }
53
- export {};
package/lib/graphai.js CHANGED
@@ -5,6 +5,7 @@ const node_1 = require("./node");
5
5
  const utils_1 = require("./utils/utils");
6
6
  const validator_1 = require("./validator");
7
7
  const task_manager_1 = require("./task_manager");
8
+ const result_1 = require("./result");
8
9
  exports.defaultConcurrency = 8;
9
10
  exports.graphDataLatestVersion = 0.5;
10
11
  class GraphAI {
@@ -44,7 +45,7 @@ class GraphAI {
44
45
  return (0, utils_1.getDataFromSource)(source.nodeId ? results[source.nodeId] : undefined, source);
45
46
  }
46
47
  // for static
47
- initializeNodes(previousResults) {
48
+ initializeStaticNodes() {
48
49
  // If the result property is specified, inject it.
49
50
  // If the previousResults exists (indicating we are in a loop),
50
51
  // process the update property (nodeId or nodeId.propId).
@@ -52,9 +53,19 @@ class GraphAI {
52
53
  const node = this.nodes[nodeId];
53
54
  if (node?.isStaticNode) {
54
55
  const value = node?.value;
55
- if (value) {
56
+ if (value !== undefined) {
56
57
  this.injectValue(nodeId, value, nodeId);
57
58
  }
59
+ }
60
+ });
61
+ }
62
+ updateStaticNodes(previousResults) {
63
+ // If the result property is specified, inject it.
64
+ // If the previousResults exists (indicating we are in a loop),
65
+ // process the update property (nodeId or nodeId.propId).
66
+ Object.keys(this.data.nodes).forEach((nodeId) => {
67
+ const node = this.nodes[nodeId];
68
+ if (node?.isStaticNode) {
58
69
  const update = node?.update;
59
70
  if (update && previousResults) {
60
71
  const result = this.getValueFromResults(update, previousResults);
@@ -95,7 +106,7 @@ class GraphAI {
95
106
  };
96
107
  (0, validator_1.validateGraphData)(data, [...Object.keys(agentFunctionInfoDictionary), ...this.bypassAgentIds]);
97
108
  this.nodes = this.createNodes(data);
98
- this.initializeNodes();
109
+ this.initializeStaticNodes();
99
110
  }
100
111
  getAgentFunctionInfo(agentId) {
101
112
  if (agentId && this.agentFunctionInfoDictionary[agentId]) {
@@ -209,11 +220,13 @@ class GraphAI {
209
220
  processLoopIfNecessary() {
210
221
  this.repeatCount++;
211
222
  const loop = this.loop;
212
- if (loop && (loop.count === undefined || this.repeatCount < loop.count)) {
213
- const results = this.results(true); // results from previous loop
214
- this.nodes = this.createNodes(this.data);
215
- this.initializeNodes(results);
216
- // Notice that we need to check the while condition *after* calling initializeNodes.
223
+ if (!loop) {
224
+ return false;
225
+ }
226
+ // We need to update static nodes, before checking the condition
227
+ const previousResults = this.results(true); // results from previous loop
228
+ this.updateStaticNodes(previousResults);
229
+ if (loop.count === undefined || this.repeatCount < loop.count) {
217
230
  if (loop.while) {
218
231
  const source = (0, utils_1.parseNodeName)(loop.while, this.version);
219
232
  const value = this.getValueFromResults(source, this.results(true));
@@ -222,6 +235,9 @@ class GraphAI {
222
235
  return false; // while condition is not met
223
236
  }
224
237
  }
238
+ this.nodes = this.createNodes(this.data);
239
+ this.initializeStaticNodes();
240
+ this.updateStaticNodes(previousResults);
225
241
  this.pushReadyNodesIntoQueue();
226
242
  return true; // Indicating that we are going to continue.
227
243
  }
@@ -252,24 +268,15 @@ class GraphAI {
252
268
  throw new Error(`injectValue with Invalid nodeId, ${nodeId}`);
253
269
  }
254
270
  }
255
- nestedResultOf(source) {
256
- if (Array.isArray(source)) {
257
- return source.map((a) => {
258
- return this.nestedResultOf(a);
259
- });
271
+ resultsOf(inputs, anyInput = false) {
272
+ const results = (0, result_1.resultsOf)(inputs ?? [], this.nodes, this.version);
273
+ if (anyInput) {
274
+ return (0, result_1.cleanResult)(results);
260
275
  }
261
- return this.resultOf(source);
262
- }
263
- resultsOf(sources) {
264
- const ret = {};
265
- Object.keys(sources).forEach((key) => {
266
- ret[key] = this.nestedResultOf(sources[key]);
267
- });
268
- return ret;
276
+ return results;
269
277
  }
270
278
  resultOf(source) {
271
- const { result } = source.nodeId ? this.nodes[source.nodeId] : { result: undefined };
272
- return (0, utils_1.getDataFromSource)(result, source);
279
+ return (0, result_1.resultOf)(source, this.nodes);
273
280
  }
274
281
  }
275
282
  exports.GraphAI = GraphAI;
package/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { GraphAI, defaultConcurrency, graphDataLatestVersion } from "./graphai";
2
- export { AgentFunction, AgentFunctionInfo, AgentFunctionInfoDictionary, AgentFunctionInfoSample, AgentFunctionContext, GraphData, ResultDataDictionary, ResultData, NodeState, AgentFilterFunction, AgentFilterInfo, NodeData, StaticNodeData, ComputedNodeData, DefaultResultData, DefaultInputData, } from "./type";
2
+ export { AgentFunction, AgentFunctionInfo, AgentFunctionInfoDictionary, AgentFunctionInfoSample, AgentFunctionContext, GraphData, ResultDataDictionary, ResultData, NodeState, AgentFilterFunction, AgentFilterInfo, NodeData, StaticNodeData, ComputedNodeData, DefaultResultData, DefaultInputData, DefaultParamsType, } from "./type";
3
3
  export type { TransactionLog } from "./transaction_log";
4
- export { defaultAgentInfo, agentInfoWrapper, defaultTestContext, strIntentionalError, assert, sleep } from "./utils/utils";
4
+ export { defaultAgentInfo, agentInfoWrapper, defaultTestContext, strIntentionalError, assert, sleep, isObject } from "./utils/utils";
5
5
  export { ValidationError } from "./validators/common";
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ValidationError = exports.sleep = exports.assert = exports.strIntentionalError = exports.defaultTestContext = exports.agentInfoWrapper = exports.defaultAgentInfo = exports.NodeState = exports.graphDataLatestVersion = exports.defaultConcurrency = exports.GraphAI = void 0;
3
+ exports.ValidationError = exports.isObject = exports.sleep = exports.assert = exports.strIntentionalError = exports.defaultTestContext = exports.agentInfoWrapper = exports.defaultAgentInfo = exports.NodeState = exports.graphDataLatestVersion = exports.defaultConcurrency = exports.GraphAI = void 0;
4
4
  var graphai_1 = require("./graphai");
5
5
  Object.defineProperty(exports, "GraphAI", { enumerable: true, get: function () { return graphai_1.GraphAI; } });
6
6
  Object.defineProperty(exports, "defaultConcurrency", { enumerable: true, get: function () { return graphai_1.defaultConcurrency; } });
@@ -14,5 +14,6 @@ Object.defineProperty(exports, "defaultTestContext", { enumerable: true, get: fu
14
14
  Object.defineProperty(exports, "strIntentionalError", { enumerable: true, get: function () { return utils_1.strIntentionalError; } });
15
15
  Object.defineProperty(exports, "assert", { enumerable: true, get: function () { return utils_1.assert; } });
16
16
  Object.defineProperty(exports, "sleep", { enumerable: true, get: function () { return utils_1.sleep; } });
17
+ Object.defineProperty(exports, "isObject", { enumerable: true, get: function () { return utils_1.isObject; } });
17
18
  var common_1 = require("./validators/common");
18
19
  Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return common_1.ValidationError; } });
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, NestedDataSource } from "./type";
2
+ import { NodeDataParams, ResultData, DataSource, ComputedNodeData, StaticNodeData, NodeState } from "./type";
3
3
  import { TransactionLog } from "./transaction_log";
4
4
  export declare class Node {
5
5
  readonly nodeId: string;
@@ -29,9 +29,9 @@ export declare class ComputedNode extends Node {
29
29
  transactionId: undefined | number;
30
30
  private readonly passThrough?;
31
31
  readonly anyInput: boolean;
32
- dataSources: NestedDataSource;
32
+ dataSources: DataSource[];
33
33
  private inputs?;
34
- inputNames?: Array<string>;
34
+ isNamedInputs: boolean;
35
35
  pendings: Set<string>;
36
36
  private ifSource?;
37
37
  private unlessSource?;
@@ -40,7 +40,7 @@ export declare class ComputedNode extends Node {
40
40
  readonly isComputedNode = true;
41
41
  constructor(graphId: string, nodeId: string, data: ComputedNodeData, graph: GraphAI);
42
42
  getAgentId(): string;
43
- private addPengindNode;
43
+ private addPendingNode;
44
44
  isReadyNode(): boolean;
45
45
  private retry;
46
46
  private checkDataAvailability;
@@ -53,8 +53,10 @@ export declare class ComputedNode extends Node {
53
53
  execute(): Promise<void>;
54
54
  private prepareExecute;
55
55
  private errorProcess;
56
- private getNamedInput;
56
+ private getParams;
57
57
  private getInputs;
58
+ private getContext;
59
+ private getResult;
58
60
  private getDebugInfo;
59
61
  private beforeConsoleLog;
60
62
  private afterConsoleLog;
@@ -68,3 +70,4 @@ export declare class StaticNode extends Node {
68
70
  constructor(nodeId: string, data: StaticNodeData, graph: GraphAI);
69
71
  injectValue(value: ResultData, injectFrom?: string): void;
70
72
  }
73
+ export type GraphNodes = Record<string, ComputedNode | StaticNode>;
package/lib/node.js CHANGED
@@ -35,7 +35,8 @@ class ComputedNode extends Node {
35
35
  constructor(graphId, nodeId, data, graph) {
36
36
  super(nodeId, graph);
37
37
  this.retryCount = 0;
38
- this.dataSources = {}; // data sources.
38
+ this.dataSources = []; // no longer needed. This is for transaction log.
39
+ this.isNamedInputs = false;
39
40
  this.isStaticNode = false;
40
41
  this.isComputedNode = true;
41
42
  this.graphId = graphId;
@@ -48,33 +49,29 @@ class ComputedNode extends Node {
48
49
  this.isResult = data.isResult ?? false;
49
50
  this.priority = data.priority ?? 0;
50
51
  this.anyInput = data.anyInput ?? false;
51
- if (data.inputs) {
52
- if (Array.isArray(data.inputs)) {
53
- this.inputs = data.inputs;
54
- this.dataSources = (0, nodeUtils_1.inputs2dataSources)(data.inputs, graph.version);
55
- }
56
- else {
57
- this.inputNames = Object.keys(data.inputs);
58
- this.dataSources = (0, nodeUtils_1.namedInputs2dataSources)(data.inputs, graph.version);
59
- }
52
+ this.inputs = data.inputs;
53
+ this.isNamedInputs = (0, utils_2.isObject)(data.inputs) && !Array.isArray(data.inputs);
54
+ this.dataSources = data.inputs ? (0, nodeUtils_1.inputs2dataSources)(data.inputs, graph.version).flat(10) : [];
55
+ if (data.inputs && !this.isNamedInputs) {
56
+ console.warn(`array inputs have been deprecated. nodeId: ${nodeId}: see https://github.com/receptron/graphai/blob/main/docs/NamedInputs.md`);
60
57
  }
61
- this.pendings = new Set((0, nodeUtils_1.flatDataSourceNodeIds)(Object.values(this.dataSources)));
58
+ this.pendings = new Set((0, nodeUtils_1.dataSourceNodeIds)(this.dataSources));
62
59
  (0, utils_2.assert)(["function", "string"].includes(typeof data.agent), "agent must be either string or function");
63
60
  if (typeof data.agent === "string") {
64
61
  this.agentId = data.agent;
65
62
  }
66
63
  else {
67
64
  const agent = data.agent;
68
- this.agentFunction = this.inputNames ? async ({ namedInputs }) => agent(namedInputs) : async ({ inputs }) => agent(...inputs);
65
+ this.agentFunction = this.isNamedInputs ? async ({ namedInputs }) => agent(namedInputs) : async ({ inputs }) => agent(...inputs);
69
66
  }
70
67
  if (data.graph) {
71
- this.nestedGraph = typeof data.graph === "string" ? this.addPengindNode(data.graph) : data.graph;
68
+ this.nestedGraph = typeof data.graph === "string" ? this.addPendingNode(data.graph) : data.graph;
72
69
  }
73
70
  if (data.if) {
74
- this.ifSource = this.addPengindNode(data.if);
71
+ this.ifSource = this.addPendingNode(data.if);
75
72
  }
76
73
  if (data.unless) {
77
- this.unlessSource = this.addPengindNode(data.unless);
74
+ this.unlessSource = this.addPendingNode(data.unless);
78
75
  }
79
76
  this.dynamicParams = Object.keys(this.params).reduce((tmp, key) => {
80
77
  const dataSource = (0, utils_2.parseNodeName)(this.params[key], graph.version < 0.3 ? 0.3 : graph.version);
@@ -90,7 +87,7 @@ class ComputedNode extends Node {
90
87
  getAgentId() {
91
88
  return this.agentId ?? "__custom__function"; // only for display purpose in the log.
92
89
  }
93
- addPengindNode(nodeId) {
90
+ addPendingNode(nodeId) {
94
91
  const source = (0, utils_2.parseNodeName)(nodeId, this.graph.version);
95
92
  (0, utils_2.assert)(!!source.nodeId, `Invalid data source ${nodeId}`);
96
93
  this.pendings.add(source.nodeId);
@@ -126,7 +123,7 @@ class ComputedNode extends Node {
126
123
  }
127
124
  }
128
125
  checkDataAvailability() {
129
- return Object.values(this.graph.resultsOf(this.dataSources))
126
+ return Object.values(this.graph.resultsOf(this.inputs))
130
127
  .flat()
131
128
  .some((result) => result !== undefined);
132
129
  }
@@ -195,7 +192,7 @@ class ComputedNode extends Node {
195
192
  // then it removes itself from the "running node" list of the graph.
196
193
  // Notice that setting the result of this node may make other nodes ready to run.
197
194
  async execute() {
198
- const previousResults = this.graph.resultsOf(this.dataSources);
195
+ const previousResults = this.graph.resultsOf(this.inputs, this.anyInput);
199
196
  const transactionId = Date.now();
200
197
  this.prepareExecute(transactionId, Object.values(previousResults));
201
198
  if (this.timeout && this.timeout > 0) {
@@ -206,22 +203,7 @@ class ComputedNode extends Node {
206
203
  try {
207
204
  const agentFunction = this.agentFunction ?? this.graph.getAgentFunctionInfo(this.agentId).agent;
208
205
  const localLog = [];
209
- const params = Object.keys(this.dynamicParams).reduce((tmp, key) => {
210
- const result = this.graph.resultOf(this.dynamicParams[key]);
211
- tmp[key] = result;
212
- return tmp;
213
- }, { ...this.params });
214
- const context = {
215
- params: params,
216
- inputs: this.getInputs(previousResults),
217
- namedInputs: this.getNamedInput(previousResults),
218
- inputSchema: this.agentFunction ? undefined : this.graph.getAgentFunctionInfo(this.agentId)?.inputs,
219
- debugInfo: this.getDebugInfo(),
220
- filterParams: this.filterParams,
221
- agentFilters: this.graph.agentFilters,
222
- config: this.graph.config,
223
- log: localLog,
224
- };
206
+ const context = this.getContext(previousResults, localLog);
225
207
  // NOTE: We use the existence of graph object in the agent-specific params to determine
226
208
  // if this is a nested agent or not.
227
209
  if (this.nestedGraph) {
@@ -248,23 +230,13 @@ class ComputedNode extends Node {
248
230
  return;
249
231
  }
250
232
  this.state = type_1.NodeState.Completed;
251
- this.result = (() => {
252
- if (result && this.passThrough) {
253
- if ((0, utils_2.isObject)(result) && !Array.isArray(result)) {
254
- return { ...result, ...this.passThrough };
255
- }
256
- else if (Array.isArray(result)) {
257
- return result.map((r) => ((0, utils_2.isObject)(r) && !Array.isArray(r) ? { ...r, ...this.passThrough } : r));
258
- }
259
- }
260
- return result;
261
- })();
233
+ this.result = this.getResult(result);
262
234
  this.log.onComplete(this, this.graph, localLog);
263
235
  this.onSetResult();
264
236
  this.graph.onExecutionComplete(this);
265
237
  }
266
238
  catch (error) {
267
- this.errorProcess(error, transactionId);
239
+ this.errorProcess(error, transactionId, previousResults);
268
240
  }
269
241
  }
270
242
  // This private method (called only by execute()) prepares the ComputedNode object
@@ -277,9 +249,10 @@ class ComputedNode extends Node {
277
249
  // This private method (called only by execute) processes an error received from
278
250
  // the agent function. It records the error in the transaction log and handles
279
251
  // the retry if specified.
280
- errorProcess(error, transactionId) {
252
+ errorProcess(error, transactionId, namedInputs) {
281
253
  if (error instanceof Error && error.message !== utils_1.strIntentionalError) {
282
254
  console.error(`<-- NodeId: ${this.nodeId}, Agent: ${this.agentId}`);
255
+ console.error({ namedInputs });
283
256
  console.error(error);
284
257
  console.error("-->");
285
258
  }
@@ -295,22 +268,43 @@ class ComputedNode extends Node {
295
268
  this.retry(type_1.NodeState.Failed, Error("Unknown"));
296
269
  }
297
270
  }
298
- getNamedInput(previousResults) {
299
- if (this.inputNames) {
300
- return this.inputNames.reduce((tmp, name) => {
301
- if (!this.anyInput || previousResults[name]) {
302
- tmp[name] = previousResults[name];
303
- }
304
- return tmp;
305
- }, {});
306
- }
307
- return {};
271
+ getParams() {
272
+ return Object.keys(this.dynamicParams).reduce((tmp, key) => {
273
+ const result = this.graph.resultOf(this.dynamicParams[key]);
274
+ tmp[key] = result;
275
+ return tmp;
276
+ }, { ...this.params });
308
277
  }
309
278
  getInputs(previousResults) {
310
- if (this.inputNames) {
311
- return [];
279
+ if (Array.isArray(this.inputs)) {
280
+ return (this.inputs ?? []).map((key) => previousResults[String(key)]).filter((a) => !this.anyInput || a);
281
+ }
282
+ return [];
283
+ }
284
+ getContext(previousResults, localLog) {
285
+ const context = {
286
+ params: this.getParams(),
287
+ inputs: this.getInputs(previousResults),
288
+ namedInputs: this.isNamedInputs ? previousResults : {},
289
+ inputSchema: this.agentFunction ? undefined : this.graph.getAgentFunctionInfo(this.agentId)?.inputs,
290
+ debugInfo: this.getDebugInfo(),
291
+ filterParams: this.filterParams,
292
+ agentFilters: this.graph.agentFilters,
293
+ config: this.graph.config,
294
+ log: localLog,
295
+ };
296
+ return context;
297
+ }
298
+ getResult(result) {
299
+ if (result && this.passThrough) {
300
+ if ((0, utils_2.isObject)(result) && !Array.isArray(result)) {
301
+ return { ...result, ...this.passThrough };
302
+ }
303
+ else if (Array.isArray(result)) {
304
+ return result.map((r) => ((0, utils_2.isObject)(r) && !Array.isArray(r) ? { ...r, ...this.passThrough } : r));
305
+ }
312
306
  }
313
- return (this.inputs ?? []).map((key) => previousResults[String(key)]).filter((a) => !this.anyInput || a);
307
+ return result;
314
308
  }
315
309
  getDebugInfo() {
316
310
  return {
@@ -324,7 +318,7 @@ class ComputedNode extends Node {
324
318
  }
325
319
  beforeConsoleLog(context) {
326
320
  if (this.console.before === true) {
327
- console.log(JSON.stringify(this.inputNames ? context.namedInputs : context.inputs, null, 2));
321
+ console.log(JSON.stringify(this.isNamedInputs ? context.namedInputs : context.inputs, null, 2));
328
322
  }
329
323
  else if (this.console.before) {
330
324
  console.log(this.console.before);
@@ -0,0 +1,6 @@
1
+ import { DataSource, ResultData } from "./type";
2
+ import { GraphNodes } from "./node";
3
+ export declare const resultsOf: (inputs: Record<string, any> | Array<string>, nodes: GraphNodes, graphVersion: number) => Record<string, ResultData>;
4
+ export declare const resultOf: (source: DataSource, nodes: GraphNodes) => ResultData;
5
+ export declare const cleanResultInner: (results: ResultData) => ResultData | null;
6
+ export declare const cleanResult: (results: Record<string, ResultData | undefined>) => Record<string, ResultData>;
package/lib/result.js ADDED
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cleanResult = exports.cleanResultInner = exports.resultOf = exports.resultsOf = void 0;
4
+ const utils_1 = require("./utils/utils");
5
+ const resultsOfInner = (input, nodes, graphVersion) => {
6
+ if (Array.isArray(input)) {
7
+ return input.map((inp) => resultsOfInner(inp, nodes, graphVersion));
8
+ }
9
+ if ((0, utils_1.isNamedInputs)(input)) {
10
+ return (0, exports.resultsOf)(input, nodes, graphVersion);
11
+ }
12
+ if (typeof input === "string") {
13
+ const templateMatch = [...input.matchAll(/\${(:[^}]+)}/g)].map((m) => m[1]);
14
+ if (templateMatch.length > 0) {
15
+ const results = resultsOfInner(templateMatch, nodes, graphVersion);
16
+ return Array.from(templateMatch.keys()).reduce((tmp, key) => {
17
+ return tmp.replaceAll("${" + templateMatch[key] + "}", results[key]);
18
+ }, input);
19
+ }
20
+ }
21
+ return (0, exports.resultOf)((0, utils_1.parseNodeName)(input, graphVersion), nodes);
22
+ };
23
+ const resultsOf = (inputs, nodes, graphVersion) => {
24
+ // for inputs. TODO remove if array input is not supported
25
+ if (Array.isArray(inputs)) {
26
+ return inputs.reduce((tmp, key) => {
27
+ tmp[key] = resultsOfInner(key, nodes, graphVersion);
28
+ return tmp;
29
+ }, {});
30
+ }
31
+ return Object.keys(inputs).reduce((tmp, key) => {
32
+ const input = inputs[key];
33
+ tmp[key] = (0, utils_1.isNamedInputs)(input) ? (0, exports.resultsOf)(input, nodes, graphVersion) : resultsOfInner(input, nodes, graphVersion);
34
+ return tmp;
35
+ }, {});
36
+ };
37
+ exports.resultsOf = resultsOf;
38
+ const resultOf = (source, nodes) => {
39
+ const { result } = source.nodeId ? nodes[source.nodeId] : { result: undefined };
40
+ return (0, utils_1.getDataFromSource)(result, source);
41
+ };
42
+ exports.resultOf = resultOf;
43
+ // clean up object for anyInput
44
+ const cleanResultInner = (results) => {
45
+ if (Array.isArray(results)) {
46
+ return results.map((result) => (0, exports.cleanResultInner)(result)).filter((result) => !(0, utils_1.isNull)(result));
47
+ }
48
+ if ((0, utils_1.isObject)(results)) {
49
+ return Object.keys(results).reduce((tmp, key) => {
50
+ const value = (0, exports.cleanResultInner)(results[key]);
51
+ if (!(0, utils_1.isNull)(value)) {
52
+ tmp[key] = value;
53
+ }
54
+ return tmp;
55
+ }, {});
56
+ }
57
+ return results;
58
+ };
59
+ exports.cleanResultInner = cleanResultInner;
60
+ const cleanResult = (results) => {
61
+ return Object.keys(results).reduce((tmp, key) => {
62
+ const value = (0, exports.cleanResultInner)(results[key]);
63
+ if (!(0, utils_1.isNull)(value)) {
64
+ tmp[key] = value;
65
+ }
66
+ return tmp;
67
+ }, {});
68
+ };
69
+ exports.cleanResult = cleanResult;
@@ -44,7 +44,7 @@ class TransactionLog {
44
44
  this.state = node.state;
45
45
  this.retryCount = node.retryCount > 0 ? node.retryCount : undefined;
46
46
  this.startTime = transactionId;
47
- this.inputs = (0, nodeUtils_1.flatDataSourceNodeIds)(Object.values(node.dataSources));
47
+ this.inputs = (0, nodeUtils_1.dataSourceNodeIds)(node.dataSources);
48
48
  this.inputsData = inputs.length > 0 ? inputs : undefined;
49
49
  graph.setLoopLog(this);
50
50
  graph.appendLog(this);
package/lib/type.d.ts CHANGED
@@ -23,9 +23,6 @@ export type DataSource = {
23
23
  value?: any;
24
24
  propIds?: string[];
25
25
  };
26
- export type DataSources = DataSource | DataSource[] | DataSources[];
27
- export type NestedDataSource = Record<string, DataSources>;
28
- export type ResultDataSet = ResultData | ResultData[] | ResultDataSet[];
29
26
  export type StaticNodeData = {
30
27
  value: ResultData;
31
28
  update?: string;
@@ -61,6 +58,7 @@ export type GraphData = {
61
58
  loop?: LoopData;
62
59
  verbose?: boolean;
63
60
  retry?: number;
61
+ metadata?: any;
64
62
  };
65
63
  export type GraphOptions = {
66
64
  agentFilters?: AgentFilterInfo[] | undefined;
@@ -118,6 +116,7 @@ export type AgentFunctionInfo = {
118
116
  author: string;
119
117
  repository: string;
120
118
  license: string;
119
+ environmentVariables?: string[];
121
120
  stream?: boolean;
122
121
  apiKeys?: string[];
123
122
  npms?: string[];
@@ -1,5 +1,3 @@
1
- import { DataSource, DataSources, NestedDataSource } from "../type";
2
- export declare const inputs2dataSources: (inputs: string[], graphVersion: number) => Record<string, DataSource>;
3
- export declare const namedInputs2dataSources: (inputs: Record<string, any>, graphVersion: number) => NestedDataSource;
4
- export declare const flatDataSourceNodeIds: (sources: DataSource[] | DataSources[]) => string[];
5
- export declare const flatDataSource: (sources: DataSource[] | DataSources[]) => DataSource[];
1
+ import { DataSource } from "../type";
2
+ export declare const inputs2dataSources: (inputs: any, graphVersion: number) => DataSource[];
3
+ export declare const dataSourceNodeIds: (sources: DataSource[]) => string[];
@@ -1,49 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.flatDataSource = exports.flatDataSourceNodeIds = exports.namedInputs2dataSources = exports.inputs2dataSources = void 0;
3
+ exports.dataSourceNodeIds = exports.inputs2dataSources = void 0;
4
4
  const utils_1 = require("./utils");
5
+ // for dataSource
5
6
  const inputs2dataSources = (inputs, graphVersion) => {
6
- return inputs.reduce((tmp, input) => {
7
- tmp[input] = (0, utils_1.parseNodeName)(input, graphVersion);
8
- return tmp;
9
- }, {});
10
- };
11
- exports.inputs2dataSources = inputs2dataSources;
12
- const nestedParseNodeName = (input, graphVersion) => {
13
- if (Array.isArray(input)) {
14
- return input.map((inp) => nestedParseNodeName(inp, graphVersion));
7
+ if (Array.isArray(inputs)) {
8
+ return inputs.map((inp) => (0, exports.inputs2dataSources)(inp, graphVersion)).flat();
15
9
  }
16
- return (0, utils_1.parseNodeName)(input, graphVersion);
17
- };
18
- const namedInputs2dataSources = (inputs, graphVersion) => {
19
- return Object.keys(inputs).reduce((tmp, key) => {
20
- const input = inputs[key];
21
- tmp[key] = nestedParseNodeName(input, graphVersion);
22
- return tmp;
23
- }, {});
24
- };
25
- exports.namedInputs2dataSources = namedInputs2dataSources;
26
- const flatDataSourceNodeIds = (sources) => {
27
- return (0, exports.flatDataSource)(sources)
28
- .filter((source) => source.nodeId)
29
- .map((source) => source.nodeId);
30
- };
31
- exports.flatDataSourceNodeIds = flatDataSourceNodeIds;
32
- const flatDataSource = (sources) => {
33
- return sources
34
- .map((source) => {
35
- if (Array.isArray(source)) {
36
- return source
37
- .map((s) => {
38
- if (Array.isArray(s)) {
39
- return (0, exports.flatDataSource)(s);
40
- }
41
- return s;
42
- })
43
- .flat();
10
+ if ((0, utils_1.isObject)(inputs)) {
11
+ return Object.values(inputs)
12
+ .map((input) => (0, exports.inputs2dataSources)(input, graphVersion))
13
+ .flat();
14
+ }
15
+ if (typeof inputs === "string") {
16
+ const templateMatch = [...inputs.matchAll(/\${(:[^}]+)}/g)].map((m) => m[1]);
17
+ if (templateMatch.length > 0) {
18
+ return (0, exports.inputs2dataSources)(templateMatch, graphVersion);
44
19
  }
45
- return source;
46
- })
47
- .flat();
20
+ }
21
+ return (0, utils_1.parseNodeName)(inputs, graphVersion);
22
+ };
23
+ exports.inputs2dataSources = inputs2dataSources;
24
+ const dataSourceNodeIds = (sources) => {
25
+ return sources.filter((source) => source.nodeId).map((source) => source.nodeId);
48
26
  };
49
- exports.flatDataSource = flatDataSource;
27
+ exports.dataSourceNodeIds = dataSourceNodeIds;
@@ -1,8 +1,9 @@
1
- import { DataSource, ResultData, AgentFunction } from "../type";
1
+ import { DataSource, ResultData, AgentFunction, DefaultInputData } from "../type";
2
2
  export declare const sleep: (milliseconds: number) => Promise<unknown>;
3
3
  export declare const parseNodeName: (inputNodeId: any, version: number) => 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
+ export declare const isNull: (data: unknown) => data is null | undefined;
6
7
  export declare const getDataFromSource: (result: ResultData | undefined, source: DataSource) => ResultData | undefined;
7
8
  export declare const strIntentionalError = "Intentional Error for Debugging";
8
9
  export declare const defaultAgentInfo: {
@@ -46,3 +47,4 @@ export declare const defaultTestContext: {
46
47
  agents: {};
47
48
  log: never[];
48
49
  };
50
+ export declare const isNamedInputs: <NamedInput = DefaultInputData>(namedInputs: NamedInput) => boolean;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.defaultTestContext = exports.isLogicallyTrue = exports.debugResultKey = exports.agentInfoWrapper = exports.defaultAgentInfo = exports.strIntentionalError = exports.getDataFromSource = exports.isObject = exports.parseNodeName = exports.sleep = void 0;
3
+ exports.isNamedInputs = exports.defaultTestContext = exports.isLogicallyTrue = exports.debugResultKey = exports.agentInfoWrapper = exports.defaultAgentInfo = exports.strIntentionalError = exports.getDataFromSource = exports.isNull = exports.isObject = exports.parseNodeName = exports.sleep = void 0;
4
4
  exports.assert = assert;
5
5
  const sleep = async (milliseconds) => {
6
6
  return await new Promise((resolve) => setTimeout(resolve, milliseconds));
@@ -52,6 +52,10 @@ const isObject = (x) => {
52
52
  return x !== null && typeof x === "object";
53
53
  };
54
54
  exports.isObject = isObject;
55
+ const isNull = (data) => {
56
+ return data === null || data === undefined;
57
+ };
58
+ exports.isNull = isNull;
55
59
  const getNestedData = (result, propId) => {
56
60
  if (Array.isArray(result)) {
57
61
  const regex = /^\$(\d+)$/;
@@ -63,10 +67,32 @@ const getNestedData = (result, propId) => {
63
67
  if (propId === "$last") {
64
68
  return result[result.length - 1];
65
69
  }
70
+ if (propId === "length()") {
71
+ return result.length;
72
+ }
73
+ if (propId === "flat()") {
74
+ return result.flat();
75
+ }
76
+ const matchJoin = propId.match(/^join\(([,-])\)$/);
77
+ if (matchJoin && Array.isArray(matchJoin)) {
78
+ return result.join(matchJoin[1]);
79
+ }
80
+ // flat, join
66
81
  }
67
82
  else if ((0, exports.isObject)(result)) {
83
+ if (propId === "keys()") {
84
+ return Object.keys(result);
85
+ }
86
+ if (propId === "values()") {
87
+ return Object.values(result);
88
+ }
68
89
  return result[propId];
69
90
  }
91
+ else if (typeof result === "string") {
92
+ if (propId === "jsonParse()") {
93
+ return JSON.parse(result);
94
+ }
95
+ }
70
96
  return undefined;
71
97
  };
72
98
  const innerGetDataFromSource = (result, propIds) => {
@@ -166,3 +192,7 @@ exports.defaultTestContext = {
166
192
  agents: {},
167
193
  log: [],
168
194
  };
195
+ const isNamedInputs = (namedInputs) => {
196
+ return (0, exports.isObject)(namedInputs) && !Array.isArray(namedInputs) && Object.keys(namedInputs || {}).length > 0;
197
+ };
198
+ exports.isNamedInputs = isNamedInputs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphai",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "Asynchronous data flow execution engine for agentic AI apps.",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "homepage": "https://github.com/receptron/graphai#readme",
28
28
  "devDependencies": {
29
- "typedoc": "^0.26.5"
29
+ "typedoc": "^0.26.7"
30
30
  },
31
31
  "types": "./lib/index.d.ts",
32
32
  "directories": {