graphai 0.0.5 → 0.0.7

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/.eslintrc.js CHANGED
@@ -1,49 +1,30 @@
1
1
  module.exports = {
2
- "env": {
3
- "es2021": true,
4
- "node": true
2
+ env: {
3
+ es2021: true,
4
+ node: true,
5
+ },
6
+ extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
7
+ overrides: [
8
+ {
9
+ env: {
10
+ node: true,
11
+ },
12
+ files: [".eslintrc.{js,cjs}"],
13
+ parserOptions: {
14
+ sourceType: "script",
15
+ },
5
16
  },
6
- "extends": [
7
- "eslint:recommended",
8
- "plugin:@typescript-eslint/recommended"
9
- ],
10
- "overrides": [
11
- {
12
- "env": {
13
- "node": true
14
- },
15
- "files": [
16
- ".eslintrc.{js,cjs}"
17
- ],
18
- "parserOptions": {
19
- "sourceType": "script"
20
- }
21
- }
22
- ],
23
- "parser": "@typescript-eslint/parser",
24
- "parserOptions": {
25
- "ecmaVersion": "latest",
26
- "sourceType": "module"
27
- },
28
- "plugins": [
29
- "@typescript-eslint"
30
- ],
31
- "rules": {
32
- "indent": [
33
- "error",
34
- 2
35
- ],
36
- "linebreak-style": [
37
- "error",
38
- "unix"
39
- ],
40
- "quotes": [
41
- "error",
42
- "double"
43
- ],
44
- "semi": [
45
- "error",
46
- "always"
47
- ]
48
- }
17
+ ],
18
+ parser: "@typescript-eslint/parser",
19
+ parserOptions: {
20
+ ecmaVersion: "latest",
21
+ sourceType: "module",
22
+ },
23
+ plugins: ["@typescript-eslint"],
24
+ rules: {
25
+ indent: ["error", 2],
26
+ "linebreak-style": ["error", "unix"],
27
+ quotes: ["error", "double"],
28
+ semi: ["error", "always"],
29
+ },
49
30
  };
@@ -24,4 +24,5 @@ jobs:
24
24
  node-version: ${{ matrix.node-version }}
25
25
  cache: 'npm'
26
26
  - run: yarn install
27
+ - run: cd tests/http-server/docs/ && npx http-server &
27
28
  - run: yarn test
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # GraphAI
2
2
 
3
+ ## Overview
4
+
3
5
  GraphAI is an asynchronous data flow execution engine, which makes it easy to create AI applications that need to make asynchronous AI API calls multiple times with some dependencies among them, such as giving the answer from one LLM call to another LLM call as a prompt.
4
6
 
5
- You just need to describe dependencies among those API calls in a single graph definition file (in JSON or YAML), create a GraphAI object with it, and run it.
7
+ You just need to describe dependencies among those API calls in a single data flow graph (typically in YAML), create a GraphAI object with it, and run it.
6
8
 
7
9
  Here is an example:
8
10
 
@@ -10,34 +12,99 @@ Here is an example:
10
12
  nodes:
11
13
  taskA:
12
14
  params:
13
- // app-specific parameters for taskA
15
+ // agent-specific parameters for taskA
14
16
  taskB:
15
17
  params:
16
- // app-specific parameters for taskB
18
+ // agent-specific parameters for taskB
17
19
  taskC:
18
20
  params:
19
- // app-specific parameters for taskC
21
+ // agent-specific parameters for taskC
20
22
  inputs: [taskA, taskB]
21
23
  ```
22
24
 
23
25
  ``` TypeScript
24
- const nodeExecute = async (context: NodeExecuteContext) => {
26
+ const sampleAgentFunction = async (context: AgentFunctionContext) => {
25
27
  const {
26
28
  nodeId, // taskA, taskB or taskC
27
- params, // app-specific/task-specific parameters specified in the graph definition file
29
+ params, // agent-specific parameters specified in the graph definition file
28
30
  payload // for taskC, { taskA: resultA, taskB: resultB }
29
31
  } = context;
30
- // App-specific code (such as calling OpenAI's chat.completions API)
32
+ // Agent-specific code (such as calling OpenAI's chat.completions API)
31
33
  ...
32
34
  return result;
33
35
  }
34
36
 
35
37
  ...
36
38
  const file = fs.readFileSync(pathToYamlFile, "utf8");
37
- const graphdata = YAML.parse(file);
38
- const graph = new GraphAI(graph_data, nodeExecute);
39
+ const graphData = YAML.parse(file);
40
+ const graph = new GraphAI(graphData, sampleAgentFunction);
39
41
  const results = await graph.run();
40
42
  return results["taskC"];
41
43
  ```
42
44
 
45
+ ## Data Flow Graph
46
+
47
+ A Data Flow Graph (DFG) is a JavaScript object, which defines the flow of data. It is typically described in YAML file and loaded at runtime.
48
+
49
+ A DFG consists of a collection of 'nodes', which contains a series of nested keys representing individual nodes in the data flow. Each node is identified by a unique key (e.g., node1, node2) and can contain several predefined keys (params, inputs, retry, timeout, source, dispatch, agentId) that dictate the node's behavior and its relationship with other nodes.
50
+
51
+ ### DFG Structure
52
+
53
+ - 'nodes': A list of node. Required.
54
+ - 'concurrency': An optional property, which specifies the maximum number of concurrent operations (agent functions to be executed at the same time). The default is 8.
55
+
56
+ ## Agent
57
+
58
+ An agent is an abstract object, which takes some inputs and generates an output asynchronously. It could be an LLM (such as GPT-4), an image/video/music generator, a database, or a REST API over HTTP. Each node (except 'source node') is associated with an agent function, which takes data flow into the node as inputs, and generates an output.
59
+
60
+ ## Agent function
61
+
62
+ An agent function is a TypeScript function, which implements an agent. A DFG is associated one or more agent functions. If the DFG is associated with multiple agent functions, each node needs to be associated only one of them (either explicitly with 'agentId' or implicitly to the default Agent function).
63
+
64
+ An agent function receives two set of parameters via AgentFunctionContext, agent specific parameters specified in the DFG and input data came from other nodes (payload).
65
+
66
+ A regular agent function returns the data (type: ```Record<string, any>```), but a dispatcher agent function returns the date with outputID(s) (type: ```Record<string: Record<string, any>>```).
67
+
68
+ ## Node Structure
69
+
70
+ - 'inputs': An optional list of node identifiers that the current node depends on. This establishes a flow where the current node can only be executed after the completion of the nodes listed under 'inputs'. If this list is empty and the 'source' property is not set, the associated Agent Function will be immediatley executed.
71
+ - 'source': An optional flag, which specifies if the node is a 'source node' or not. A souce node is a special node, which receives data from either an external code (via GraphAI's injectResult method) or from a 'dispatcher node'.
72
+ - 'dispatch': An optinal property (type: ```Record<string, string>```) which indicates that the current node is a 'dispatcher node'. A dispatcher node is associated is a dispaher-style agent function, which has multiple possible 'outputs'.
73
+ - 'retry': An optional number, which specifies the maximum number of retries to be made. If the last attempt fails, that return value will be recorded.
74
+ - 'timeout': An optional number, which specifies the maximum waittime in msec. If the associated agent function does not return the value in time, the "Timeout" error will be recorded and the returned value will be discarded.
75
+ - 'params': An optional parameters to the associated agent function, which are agent specific.
76
+ - 'payloadMapping': An optional property (type: ```Record<string, string>```), which maps input nodeIds to agent specific inputIDs.
77
+
78
+ ## GraphAI class
79
+
80
+ ### ```constructor(data: GraphData, callbackDictonary: AgentFunctionDictonary | AgentFunction<any, any, any>)```
81
+ Initializes a new instance of the GraphAI class with the specified graph data and a dictionary of callback functions.
82
+
83
+ - ```data: GraphData```: The graph data including nodes and optional concurrency limit.
84
+ - ```callbackDictonary: AgentFunctionDictonary | AgentFunction<any, any, any>```: A dictionary mapping agent IDs to their respective callback functions, or a single default callback function to be used for all nodes.
85
+
86
+ ### ```async run(): Promise<ResultDataDictonary<ResultData>>```
87
+ Executes the graph asynchronously, starting with nodes that have no dependencies or whose dependencies have been met. The method continues to execute nodes as their dependencies are satisfied until all nodes have been executed or an error occurs.
88
+
89
+ Returns: A promise that resolves with the results of all executed nodes or rejects with the first encountered error.
90
+
91
+ ### ```results(): ResultDataDictonary<ResultData>```
92
+ Compiles and returns the results of all executed nodes in the graph.
93
+
94
+ Returns: A dictionary mapping node IDs to their results. Only includes nodes that have completed execution and produced a result.
95
+
96
+ ### ```errors(): Record<string, Error>```
97
+ Compiles and returns the errors from all nodes that encountered an error during execution.
98
+
99
+ Returns: A dictionary mapping node IDs to the errors they encountered. Only includes nodes that have executed and encountered an error. It does not include any errors which have been retried.
100
+
101
+ ### ```transactionLogs(): Array<TransactionLog>```
102
+ Retrieves all transaction logs recorded during the execution of the graph.
103
+
104
+ Returns: An array of transaction logs detailing the execution states and outcomes of the nodes within the graph.
105
+
106
+ ### ```injectResult(nodeId: string, result: ResultData): void```
107
+ Injects a result into a specified node. This is used to manually set the result of a source node, allowing dependent nodes to proceed with execution.
43
108
 
109
+ - ```nodeId: string```: The ID of the source node into which the result is to be injected.
110
+ - ```result: ResultData```: The result to be injected into the specified node.
package/lib/graphai.d.ts CHANGED
@@ -1,54 +1,66 @@
1
1
  export declare enum NodeState {
2
- Waiting = 0,
3
- Executing = 1,
4
- Failed = 2,
5
- TimedOut = 3,
6
- Completed = 4
2
+ Waiting = "waiting",
3
+ Executing = "executing",
4
+ Failed = "failed",
5
+ TimedOut = "timed-out",
6
+ Completed = "completed",
7
+ Injected = "injected",
8
+ Dispatched = "dispatched"
7
9
  }
8
10
  type ResultData<ResultType = Record<string, any>> = ResultType | undefined;
9
11
  type ResultDataDictonary<ResultType = Record<string, any>> = Record<string, ResultData<ResultType>>;
10
12
  export type NodeDataParams<ParamsType = Record<string, any>> = ParamsType;
11
13
  type NodeData = {
12
- inputs: undefined | Array<string>;
14
+ inputs?: Array<string>;
13
15
  params: NodeDataParams;
14
- retry: undefined | number;
15
- timeout: undefined | number;
16
- functionName: undefined | string;
16
+ payloadMapping?: Record<string, string>;
17
+ retry?: number;
18
+ timeout?: number;
19
+ agentId?: string;
20
+ source?: boolean;
21
+ dispatch?: Record<string, string>;
17
22
  };
18
- type GraphData = {
23
+ export type GraphData = {
19
24
  nodes: Record<string, NodeData>;
20
- concurrency: number;
21
- };
22
- type NodeExecuteContext<ResultType, ParamsType> = {
23
- nodeId: string;
24
- retry: number;
25
- params: NodeDataParams<ParamsType>;
26
- payload: ResultDataDictonary<ResultType>;
25
+ concurrency?: number;
27
26
  };
28
27
  export type TransactionLog = {
29
28
  nodeId: string;
30
29
  state: NodeState;
31
- startTime: undefined | number;
32
- endTime: undefined | number;
30
+ startTime: number;
31
+ endTime?: number;
33
32
  retryCount: number;
34
- error: undefined | Error;
33
+ agentId?: string;
34
+ params?: NodeDataParams;
35
+ payload?: ResultDataDictonary<ResultData>;
36
+ errorMessage?: string;
35
37
  result?: ResultData;
36
38
  };
37
- export type NodeExecute<ResultType = Record<string, any>, ParamsType = Record<string, any>> = (context: NodeExecuteContext<ResultType, ParamsType>) => Promise<ResultData<ResultType>>;
39
+ export type AgentFunctionContext<ParamsType, ResultType, PreviousResultType> = {
40
+ nodeId: string;
41
+ retry: number;
42
+ params: NodeDataParams<ParamsType>;
43
+ payload: ResultDataDictonary<PreviousResultType>;
44
+ };
45
+ export type AgentFunction<ParamsType = Record<string, any>, ResultType = Record<string, any>, PreviousResultType = Record<string, any>> = (context: AgentFunctionContext<ParamsType, ResultType, PreviousResultType>) => Promise<ResultData<ResultType>>;
46
+ export type AgentFunctionDictonary = Record<string, AgentFunction<any, any, any>>;
38
47
  declare class Node {
39
48
  nodeId: string;
40
49
  params: NodeDataParams;
41
50
  inputs: Array<string>;
51
+ payloadMapping: Record<string, string>;
42
52
  pendings: Set<string>;
43
53
  waitlist: Set<string>;
44
54
  state: NodeState;
45
- functionName: string;
55
+ agentId: string;
46
56
  result: ResultData;
47
57
  retryLimit: number;
48
58
  retryCount: number;
49
59
  transactionId: undefined | number;
50
- timeout: number;
51
- error: undefined | Error;
60
+ timeout?: number;
61
+ error?: Error;
62
+ source: boolean;
63
+ dispatch?: Record<string, string>;
52
64
  private graph;
53
65
  constructor(nodeId: string, data: NodeData, graph: GraphAI);
54
66
  asString(): string;
@@ -56,20 +68,22 @@ declare class Node {
56
68
  removePending(nodeId: string): void;
57
69
  payload(): ResultDataDictonary<Record<string, any>>;
58
70
  pushQueueIfReady(): void;
71
+ injectResult(result: ResultData): void;
72
+ private setResult;
59
73
  execute(): Promise<void>;
60
74
  }
61
75
  type GraphNodes = Record<string, Node>;
62
- type NodeExecuteDictonary = Record<string, NodeExecute>;
63
76
  export declare class GraphAI {
64
77
  nodes: GraphNodes;
65
- callbackDictonary: NodeExecuteDictonary;
78
+ callbackDictonary: AgentFunctionDictonary;
79
+ isRunning: boolean;
66
80
  private runningNodes;
67
81
  private nodeQueue;
68
82
  private onComplete;
69
83
  private concurrency;
70
84
  private logs;
71
- constructor(data: GraphData, callbackDictonary: NodeExecuteDictonary | NodeExecute);
72
- getCallback(functionName: string): NodeExecute<Record<string, any>, Record<string, any>>;
85
+ constructor(data: GraphData, callbackDictonary: AgentFunctionDictonary | AgentFunction<any, any, any>);
86
+ getCallback(agentId: string): AgentFunction<any, any, any>;
73
87
  asString(): string;
74
88
  results(): ResultDataDictonary<Record<string, any>>;
75
89
  errors(): Record<string, Error>;
@@ -79,5 +93,6 @@ export declare class GraphAI {
79
93
  removeRunning(node: Node): void;
80
94
  appendLog(log: TransactionLog): void;
81
95
  transactionLogs(): TransactionLog[];
96
+ injectResult(nodeId: string, result: ResultData): void;
82
97
  }
83
98
  export {};
package/lib/graphai.js CHANGED
@@ -3,25 +3,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GraphAI = exports.NodeState = void 0;
4
4
  var NodeState;
5
5
  (function (NodeState) {
6
- NodeState[NodeState["Waiting"] = 0] = "Waiting";
7
- NodeState[NodeState["Executing"] = 1] = "Executing";
8
- NodeState[NodeState["Failed"] = 2] = "Failed";
9
- NodeState[NodeState["TimedOut"] = 3] = "TimedOut";
10
- NodeState[NodeState["Completed"] = 4] = "Completed";
6
+ NodeState["Waiting"] = "waiting";
7
+ NodeState["Executing"] = "executing";
8
+ NodeState["Failed"] = "failed";
9
+ NodeState["TimedOut"] = "timed-out";
10
+ NodeState["Completed"] = "completed";
11
+ NodeState["Injected"] = "injected";
12
+ NodeState["Dispatched"] = "dispatched";
11
13
  })(NodeState || (exports.NodeState = NodeState = {}));
12
14
  class Node {
13
15
  constructor(nodeId, data, graph) {
16
+ this.waitlist = new Set(); // List of nodes which need data from this node.
17
+ this.state = NodeState.Waiting;
18
+ this.result = undefined;
19
+ this.retryCount = 0;
14
20
  this.nodeId = nodeId;
15
21
  this.inputs = data.inputs ?? [];
22
+ this.payloadMapping = data.payloadMapping ?? {};
16
23
  this.pendings = new Set(this.inputs);
17
24
  this.params = data.params;
18
- this.waitlist = new Set();
19
- this.state = NodeState.Waiting;
20
- this.functionName = data.functionName ?? "default";
21
- this.result = undefined;
25
+ this.agentId = data.agentId ?? "default";
22
26
  this.retryLimit = data.retry ?? 0;
23
- this.retryCount = 0;
24
- this.timeout = data.timeout ?? 0;
27
+ this.timeout = data.timeout;
28
+ this.source = data.source === true;
29
+ this.dispatch = data.dispatch;
25
30
  this.graph = graph;
26
31
  }
27
32
  asString() {
@@ -36,43 +41,77 @@ class Node {
36
41
  this.state = state;
37
42
  this.result = undefined;
38
43
  this.error = error;
39
- this.transactionId = 0; // This is necessary for timeout case
44
+ this.transactionId = undefined; // This is necessary for timeout case
40
45
  this.graph.removeRunning(this);
41
46
  }
42
47
  }
43
48
  removePending(nodeId) {
44
49
  this.pendings.delete(nodeId);
45
- this.pushQueueIfReady();
50
+ if (this.graph.isRunning) {
51
+ this.pushQueueIfReady();
52
+ }
46
53
  }
47
54
  payload() {
48
55
  return this.inputs.reduce((results, nodeId) => {
49
- results[nodeId] = this.graph.nodes[nodeId].result;
56
+ if (this.payloadMapping && this.payloadMapping[nodeId]) {
57
+ results[this.payloadMapping[nodeId]] = this.graph.nodes[nodeId].result;
58
+ }
59
+ else {
60
+ results[nodeId] = this.graph.nodes[nodeId].result;
61
+ }
50
62
  return results;
51
63
  }, {});
52
64
  }
53
65
  pushQueueIfReady() {
54
- if (this.pendings.size === 0) {
66
+ if (this.pendings.size === 0 && !this.source) {
55
67
  this.graph.pushQueue(this);
56
68
  }
57
69
  }
70
+ injectResult(result) {
71
+ if (this.source) {
72
+ const log = {
73
+ nodeId: this.nodeId,
74
+ retryCount: this.retryCount,
75
+ state: NodeState.Injected,
76
+ startTime: Date.now(),
77
+ };
78
+ log.endTime = log.startTime;
79
+ this.graph.appendLog(log);
80
+ this.setResult(result, NodeState.Injected);
81
+ }
82
+ else {
83
+ console.error("- injectResult called on non-source node.", this.nodeId);
84
+ }
85
+ }
86
+ setResult(result, state) {
87
+ this.state = state;
88
+ this.result = result;
89
+ this.waitlist.forEach((nodeId) => {
90
+ const node = this.graph.nodes[nodeId];
91
+ // Todo: Avoid running before Run()
92
+ node.removePending(this.nodeId);
93
+ });
94
+ }
58
95
  async execute() {
96
+ const payload = this.payload();
59
97
  const log = {
60
98
  nodeId: this.nodeId,
61
99
  retryCount: this.retryCount,
62
100
  state: NodeState.Executing,
63
101
  startTime: Date.now(),
64
- endTime: undefined,
65
- error: undefined,
102
+ agentId: this.agentId,
103
+ params: this.params,
104
+ payload,
66
105
  };
67
106
  this.graph.appendLog(log);
68
107
  this.state = NodeState.Executing;
69
108
  const transactionId = log.startTime;
70
109
  this.transactionId = transactionId;
71
- if (this.timeout > 0) {
110
+ if (this.timeout && this.timeout > 0) {
72
111
  setTimeout(() => {
73
112
  if (this.state === NodeState.Executing && this.transactionId === transactionId) {
74
113
  console.log(`-- ${this.nodeId}: timeout ${this.timeout}`);
75
- log.error = Error("Timeout");
114
+ log.errorMessage = "Timeout";
76
115
  log.state = NodeState.TimedOut;
77
116
  log.endTime = Date.now();
78
117
  this.retry(NodeState.TimedOut, Error("Timeout"));
@@ -80,26 +119,32 @@ class Node {
80
119
  }, this.timeout);
81
120
  }
82
121
  try {
83
- const callback = this.graph.getCallback(this.functionName);
122
+ const callback = this.graph.getCallback(this.agentId);
84
123
  const result = await callback({
85
124
  nodeId: this.nodeId,
86
125
  retry: this.retryCount,
87
126
  params: this.params,
88
- payload: this.payload(),
127
+ payload,
89
128
  });
90
129
  if (this.transactionId !== transactionId) {
91
130
  console.log(`-- ${this.nodeId}: transactionId mismatch`);
92
131
  return;
93
132
  }
94
- log.state = NodeState.Completed;
95
133
  log.endTime = Date.now();
96
134
  log.result = result;
97
- this.state = NodeState.Completed;
98
- this.result = result;
99
- this.waitlist.forEach((nodeId) => {
100
- const node = this.graph.nodes[nodeId];
101
- node.removePending(this.nodeId);
102
- });
135
+ const dispatch = this.dispatch;
136
+ if (dispatch !== undefined) {
137
+ Object.keys(result).forEach((outputId) => {
138
+ const nodeId = dispatch[outputId];
139
+ this.graph.injectResult(nodeId, result[outputId]);
140
+ });
141
+ log.state = NodeState.Dispatched;
142
+ this.state = NodeState.Dispatched;
143
+ this.graph.removeRunning(this);
144
+ return;
145
+ }
146
+ log.state = NodeState.Completed;
147
+ this.setResult(result, NodeState.Completed);
103
148
  this.graph.removeRunning(this);
104
149
  }
105
150
  catch (error) {
@@ -110,12 +155,12 @@ class Node {
110
155
  log.state = NodeState.Failed;
111
156
  log.endTime = Date.now();
112
157
  if (error instanceof Error) {
113
- log.error = error;
158
+ log.errorMessage = error.message;
114
159
  this.retry(NodeState.Failed, error);
115
160
  }
116
161
  else {
117
162
  console.error(`-- ${this.nodeId}: Unexpecrted error was caught`);
118
- log.error = Error("Unknown");
163
+ log.errorMessage = "Unknown";
119
164
  this.retry(NodeState.Failed, Error("Unknown"));
120
165
  }
121
166
  }
@@ -124,15 +169,18 @@ class Node {
124
169
  const defaultConcurrency = 8;
125
170
  class GraphAI {
126
171
  constructor(data, callbackDictonary) {
172
+ this.isRunning = false;
173
+ this.runningNodes = new Set();
174
+ this.nodeQueue = [];
127
175
  this.logs = [];
128
176
  this.callbackDictonary = typeof callbackDictonary === "function" ? { default: callbackDictonary } : callbackDictonary;
129
177
  if (this.callbackDictonary["default"] === undefined) {
130
178
  throw new Error("No default function");
131
179
  }
132
180
  this.concurrency = data.concurrency ?? defaultConcurrency;
133
- this.runningNodes = new Set();
134
- this.nodeQueue = [];
135
- this.onComplete = () => { };
181
+ this.onComplete = () => {
182
+ console.error("-- SOMETHING IS WRONG: onComplete is called without run()");
183
+ };
136
184
  this.nodes = Object.keys(data.nodes).reduce((nodes, nodeId) => {
137
185
  nodes[nodeId] = new Node(nodeId, data.nodes[nodeId], this);
138
186
  return nodes;
@@ -146,9 +194,9 @@ class GraphAI {
146
194
  });
147
195
  });
148
196
  }
149
- getCallback(functionName) {
150
- if (functionName && this.callbackDictonary[functionName]) {
151
- return this.callbackDictonary[functionName];
197
+ getCallback(agentId) {
198
+ if (agentId && this.callbackDictonary[agentId]) {
199
+ return this.callbackDictonary[agentId];
152
200
  }
153
201
  return this.callbackDictonary["default"];
154
202
  }
@@ -178,6 +226,10 @@ class GraphAI {
178
226
  }, {});
179
227
  }
180
228
  async run() {
229
+ if (this.isRunning) {
230
+ console.error("-- Already Running");
231
+ }
232
+ this.isRunning = true;
181
233
  // Nodes without pending data should run immediately.
182
234
  Object.keys(this.nodes).forEach((nodeId) => {
183
235
  const node = this.nodes[nodeId];
@@ -185,6 +237,7 @@ class GraphAI {
185
237
  });
186
238
  return new Promise((resolve, reject) => {
187
239
  this.onComplete = () => {
240
+ this.isRunning = false;
188
241
  const errors = this.errors();
189
242
  const nodeIds = Object.keys(errors);
190
243
  if (nodeIds.length > 0) {
@@ -226,5 +279,14 @@ class GraphAI {
226
279
  transactionLogs() {
227
280
  return this.logs;
228
281
  }
282
+ injectResult(nodeId, result) {
283
+ const node = this.nodes[nodeId];
284
+ if (node) {
285
+ node.injectResult(result);
286
+ }
287
+ else {
288
+ console.error("-- Invalid nodeId", nodeId);
289
+ }
290
+ }
229
291
  }
230
292
  exports.GraphAI = GraphAI;
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "graphai",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Asynchronous data flow execution engine to make it simple to build LLM apps.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
7
- "build": "tsc",
7
+ "build": "tsc && tsc-alias",
8
8
  "eslint": "eslint --fix --ext src/**/*.{ts} tests/**/*.ts",
9
- "format": "prettier --write '{src,tests}/**/*.ts'",
10
- "test": "node --test --require ts-node/register ./tests/test_*.ts",
11
- "gpt": "npx ts-node ./samples/sample_gpt.ts"
9
+ "format": "prettier --write '{src,tests,samples}/**/*.ts' .eslintrc.js",
10
+ "test": "node --test -r tsconfig-paths/register --require ts-node/register ./tests/**/test_*.ts",
11
+ "gpt": "npx ts-node -r tsconfig-paths/register ./samples/sample_gpt.ts"
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
@@ -21,15 +21,20 @@
21
21
  },
22
22
  "homepage": "https://github.com/snakajima/graphai#readme",
23
23
  "devDependencies": {
24
+ "@types/express": "^4.17.21",
24
25
  "@types/node": "^20.8.7",
25
26
  "@typescript-eslint/eslint-plugin": "^6.8.0",
26
27
  "@typescript-eslint/parser": "^6.8.0",
28
+ "arxiv-api-ts": "^1.0.3",
27
29
  "eslint": "^7.32.0 || ^8.2.0",
28
30
  "eslint-plugin-import": "^2.25.2",
31
+ "express": "^4.19.2",
29
32
  "openai": "^4.12.4",
30
33
  "prettier": "^3.0.3",
31
34
  "slashgpt": "^0.0.8",
32
35
  "ts-node": "^10.9.1",
36
+ "tsc-alias": "^1.8.8",
37
+ "tsconfig-paths": "^4.2.0",
33
38
  "typescript": "^5.2.2"
34
39
  },
35
40
  "dependencies": {
@@ -0,0 +1,46 @@
1
+ import search from "arXiv-api-ts";
2
+
3
+ import { AgentFunction } from "@/graphai";
4
+
5
+ type arxivData = { id: string; title: string; summary: string };
6
+
7
+ const search_arxiv_papers = async (keywords: string[], limit = 10) => {
8
+ const includes = keywords.map((k) => {
9
+ return { name: k };
10
+ });
11
+ const papers = await search({
12
+ searchQueryParams: [
13
+ {
14
+ include: includes,
15
+ },
16
+ ],
17
+ sortBy: "lastUpdatedDate",
18
+ sortOrder: "descending",
19
+ start: 0,
20
+ maxResults: limit,
21
+ });
22
+ return papers.entries || [];
23
+ };
24
+
25
+ export const arxivAgent: AgentFunction<{ keywords: string[]; limit: number }, arxivData[]> = async (context) => {
26
+ const { keywords, limit } = context.params;
27
+ const arxivResult = await search_arxiv_papers(keywords, limit);
28
+ // console.log("executing", arxivResult, context.params.keywords);
29
+
30
+ const result = arxivResult.map((r: any) => {
31
+ const { id, title, summary } = r;
32
+ return { id, title, summary };
33
+ });
34
+ return result;
35
+ };
36
+
37
+ export const arxiv2TextAgent: AgentFunction<{}, string, string[]> = async (context) => {
38
+ const result = (context?.payload?.inputData || [])
39
+ .map((r: any) => {
40
+ const { id, title, summary } = r;
41
+ return ["id:", id, "title:", title, "summary:", summary].join("\n");
42
+ })
43
+ .join("\n\n\n");
44
+
45
+ return result;
46
+ };