graphai 0.2.8 → 0.3.1

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,30 +22,30 @@ 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: [:source.name]
26
26
  chunks: // Break the text from Wikipedia into chunks (2048 character each with 512 overlap)
27
27
  agent: stringSplitterAgent
28
- inputs: [wikipedia]
28
+ inputs: [:wikipedia]
29
29
  chunkEmbeddings: // Get embedding vector of each chunk
30
30
  agent: stringEmbeddingsAgent
31
- inputs: [chunks]
31
+ inputs: [:chunks]
32
32
  topicEmbedding: // Get embedding vector of the question
33
33
  agent: stringEmbeddingsAgent
34
- inputs: [source.query]
34
+ inputs: [:source.query]
35
35
  similarities: // Calculate the cosine similarity of each chunk
36
36
  agent: dotProductAgent
37
- inputs: [chunkEmbeddings, topicEmbedding]
37
+ inputs: [:chunkEmbeddings, :topicEmbedding]
38
38
  sortedChunks: // Sort chunks based on their similarities
39
39
  agent: sortByValuesAgent
40
- inputs: [chunks, similarities]
40
+ inputs: [:chunks, :similarities]
41
41
  referenceText: // Concatenate chunks up to the token limit (5000)
42
42
  agent: tokenBoundStringsAgent
43
- inputs: [sortedChunks]
43
+ inputs: [:sortedChunks]
44
44
  params:
45
45
  limit: 5000
46
46
  prompt: // Generate a prompt with that reference text
47
47
  agent: stringTemplateAgent
48
- inputs: [source, referenceText]
48
+ inputs: [:source, :referenceText]
49
49
  params:
50
50
  template: |-
51
51
  Using the following document, ${0}
@@ -56,7 +56,7 @@ nodes:
56
56
  manifest:
57
57
  model: gpt-3.5-turbo
58
58
  isResult: true // indicating this is the final result
59
- inputs: [prompt]
59
+ inputs: [:prompt]
60
60
  ```
61
61
 
62
62
  ```mermaid
@@ -96,10 +96,11 @@ A DFG consists of a collection of [nodes](#node), which contains a series of nes
96
96
 
97
97
  ### Data Source
98
98
 
99
- Connections between nodes will be established by references from one node to another, using either its "inputs" or "update" property. The values of those properties are *data sources*. A *data souce* is specified by either the nodeId (e.g., "node1"), or nodeId + propertyId (e.g., "node1.item"), index (e.g., "node1.$0", "node2.$last") or combinations (e.g., "node1.messages.$0.content").
99
+ Connections between nodes will be established by references from one node to another, using either its "inputs", "update", "if" or "while" property. The values of those properties are *data sources*. A *data souce* is specified by either the ":" + nodeId (e.g., ":node1"), or ":" + nodeId + propertyId (e.g., ":node1.item"), index (e.g., ":node1.$0", ":node2.$last") or combinations (e.g., ":node1.messages.$0.content").
100
100
 
101
101
  ### DFG Structure
102
102
 
103
+ - *version*: GraphAI version, *required*. The latest version is 0.3.
103
104
  - *nodes*: A list of node. Required.
104
105
  - *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.
105
106
  - *loop*: An optional property, which specifies if the graph needs to be executed multiple times (iterations). See the [Loop section below](#loop) for details.
@@ -134,7 +135,7 @@ Here is an examnple (from [weather chat](https://github.com/receptron/graphai/bl
134
135
  messagesWithUserInput: {
135
136
  // Appends the user's input to the messages.
136
137
  agent: (messages: Array<any>, content: string) => [...messages, { role: "user", content }],
137
- inputs: ["messages", "userInput"],
138
+ inputs: [":messages", ":userInput"],
138
139
  if: "checkInput",
139
140
  },
140
141
  ```
@@ -183,23 +184,23 @@ nodes:
183
184
  value: "Find out which materials we need to purchase this week for Joe Smith's residential house project."
184
185
  projectId: // identifies the projectId from the question
185
186
  agent: "identifierAgent"
186
- inputs: ["source"] // == "sourceNode.query"
187
+ inputs: [":source"] // == "sourceNode.query"
187
188
  database:
188
189
  agent: "nestedAgent"
189
- inputs: ["question", "projectId"]
190
+ inputs: [":question", ":projectId"]
190
191
  graph:
191
192
  nodes:
192
193
  schema: // retrieves the database schema for the apecified projectId
193
194
  agent: "schemaAgent"
194
- inputs: ["$1"]
195
+ inputs: [":$1"]
195
196
  ... // issue query to the database and build an appropriate prompt with it.
196
197
  query: // send the generated prompt to the LLM
197
198
  agent: "llama3Agent"
198
- inputs: ["prompt"]
199
+ inputs: [":prompt"]
199
200
  isResult: true
200
201
  response: // Deliver the answer
201
202
  agent: "deliveryAgent"
202
- inputs: [database.query.$last.content]
203
+ inputs: [:database.query.$last.content]
203
204
  ```
204
205
 
205
206
  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.
@@ -239,14 +240,14 @@ The "update" property of two static nodes ("people" and "result"), updates those
239
240
 
240
241
  ```
241
242
  loop:
242
- while: people
243
+ while: :people
243
244
  nodes:
244
245
  people:
245
246
  value: [Steve Jobs, Elon Musk, Nikola Tesla]
246
- update: retriever.array
247
+ update: :retriever.array
247
248
  result:
248
249
  value: []
249
- update: reducer
250
+ update: :reducer
250
251
  isResult: true
251
252
  retriever:
252
253
  agent: shift
@@ -256,10 +257,10 @@ nodes:
256
257
  params:
257
258
  manifest:
258
259
  prompt: Describe about the person in less than 100 words
259
- inputs: [retriever.item]
260
+ inputs: [:retriever.item]
260
261
  reducer:
261
262
  agent: push
262
- inputs: [result, query.content]
263
+ inputs: [:result, :query.content]
263
264
  ```
264
265
 
265
266
  ```mermaid
@@ -292,7 +293,7 @@ nodes:
292
293
  value: [Steve Jobs, Elon Musk, Nikola Tesla]
293
294
  retriever:
294
295
  agent: "mapAgent"
295
- inputs: ["people"]
296
+ inputs: [":people"]
296
297
  graph:
297
298
  nodes:
298
299
  query:
@@ -300,7 +301,7 @@ nodes:
300
301
  params:
301
302
  manifest:
302
303
  prompt: Describe about the person in less than 100 words
303
- inputs: ["$0"]
304
+ inputs: [":$0"]
304
305
  ```
305
306
 
306
307
  Here is the conceptual representation of this operation.
@@ -324,8 +325,8 @@ A sample code, [weather chat](https://github.com/receptron/graphai/blob/main/sam
324
325
  tool_calls: {
325
326
  // This node is activated if the LLM requests a tool call.
326
327
  agent: "nestedAgent",
327
- inputs: ["groq.choices.$0.message.tool_calls", "messagesWithFirstRes"],
328
- if: "groq.choices.$0.message.tool_calls",
328
+ inputs: [":groq.choices.$0.message.tool_calls", ":messagesWithFirstRes"],
329
+ if: ":groq.choices.$0.message.tool_calls",
329
330
  graph: {
330
331
  // This graph is nested only for the readability.
331
332
  ```
@@ -341,7 +342,7 @@ The [weather chat](https://github.com/receptron/graphai/blob/main/samples/sample
341
342
  // Receives messages from either case.
342
343
  agent: "copyAgent",
343
344
  anyInput: true,
344
- inputs: ["no_tool_calls", "tool_calls.messagesWithSecondRes"],
345
+ inputs: [":no_tool_calls", ":tool_calls.messagesWithSecondRes"],
345
346
  },
346
347
  ```
347
348
 
@@ -0,0 +1,2 @@
1
+ import { AgentFilterFunction, AgentFunctionContext } from "../type";
2
+ export declare const streamAgentFilterGenerator: <T>(callback: (context: AgentFunctionContext, data: T) => void) => AgentFilterFunction;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.streamAgentFilterGenerator = void 0;
4
+ const streamAgentFilterGenerator = (callback) => {
5
+ const streamAgentFilter = async (context, next) => {
6
+ context.filterParams.streamTokenCallback = (data) => {
7
+ callback(context, data);
8
+ };
9
+ return next(context);
10
+ };
11
+ return streamAgentFilter;
12
+ };
13
+ exports.streamAgentFilterGenerator = streamAgentFilterGenerator;
@@ -1,14 +1,15 @@
1
1
  import { AgentFunction } from "../../graphai";
2
+ import { Groq } from "groq-sdk";
2
3
  export declare const groqAgent: AgentFunction<{
3
4
  model: string;
4
5
  query?: string;
5
6
  system?: string;
6
7
  verbose?: boolean;
7
- tools?: Record<string, any>;
8
+ tools?: Record<string, Groq.Chat.CompletionCreateParams.Tool>;
8
9
  temperature?: number;
9
10
  max_tokens?: number;
10
- tool_choice?: string | Record<string, any>;
11
- }, Record<string, any> | string, string | Array<Record<string, any>>>;
11
+ tool_choice?: string | Record<string, Groq.Chat.CompletionCreateParams.ToolChoice>;
12
+ }, Groq.Chat.ChatCompletion, string | Array<Groq.Chat.CompletionCreateParams.Message>>;
12
13
  declare const groqAgentInfo: {
13
14
  name: string;
14
15
  agent: AgentFunction<{
@@ -16,21 +17,21 @@ declare const groqAgentInfo: {
16
17
  query?: string | undefined;
17
18
  system?: string | undefined;
18
19
  verbose?: boolean | undefined;
19
- tools?: Record<string, any> | undefined;
20
+ tools?: Record<string, Groq.Chat.Completions.CompletionCreateParams.Tool> | undefined;
20
21
  temperature?: number | undefined;
21
22
  max_tokens?: number | undefined;
22
- tool_choice?: string | Record<string, any> | undefined;
23
- }, string | Record<string, any>, string | Record<string, any>[]>;
23
+ tool_choice?: string | Record<string, Groq.Chat.Completions.CompletionCreateParams.ToolChoice> | undefined;
24
+ }, Groq.Chat.Completions.ChatCompletion, string | Groq.Chat.Completions.CompletionCreateParams.Message[]>;
24
25
  mock: AgentFunction<{
25
26
  model: string;
26
27
  query?: string | undefined;
27
28
  system?: string | undefined;
28
29
  verbose?: boolean | undefined;
29
- tools?: Record<string, any> | undefined;
30
+ tools?: Record<string, Groq.Chat.Completions.CompletionCreateParams.Tool> | undefined;
30
31
  temperature?: number | undefined;
31
32
  max_tokens?: number | undefined;
32
- tool_choice?: string | Record<string, any> | undefined;
33
- }, string | Record<string, any>, string | Record<string, any>[]>;
33
+ tool_choice?: string | Record<string, Groq.Chat.Completions.CompletionCreateParams.ToolChoice> | undefined;
34
+ }, Groq.Chat.Completions.ChatCompletion, string | Groq.Chat.Completions.CompletionCreateParams.Message[]>;
34
35
  samples: never[];
35
36
  description: string;
36
37
  category: string[];
@@ -28,7 +28,7 @@ const groqAgent = async ({ params, inputs }) => {
28
28
  const [input_query, previous_messages] = inputs;
29
29
  // Notice that we ignore params.system if previous_message exists.
30
30
  const messages = previous_messages && Array.isArray(previous_messages) ? previous_messages : system ? [{ role: "system", content: system }] : [];
31
- const content = (query ? [query] : []).concat(input_query ? [input_query] : []).join("\n");
31
+ const content = (query ? [query] : []).concat(input_query && typeof input_query === "string" ? [input_query] : []).join("\n");
32
32
  if (content) {
33
33
  messages.push({
34
34
  role: "user",
@@ -0,0 +1,57 @@
1
+ import { AgentFunction } from "../../graphai";
2
+ export declare const openAIAgent: AgentFunction<{
3
+ model?: string;
4
+ query?: string;
5
+ system?: string;
6
+ verbose?: boolean;
7
+ temperature?: number;
8
+ }, Record<string, any> | string, string | Array<any>>;
9
+ export declare const openAIMockAgent: AgentFunction<{
10
+ model?: string;
11
+ query?: string;
12
+ system?: string;
13
+ verbose?: boolean;
14
+ temperature?: number;
15
+ }, Record<string, any> | string, string | Array<any>>;
16
+ declare const openaiAgentInfo: {
17
+ name: string;
18
+ agent: AgentFunction<{
19
+ model?: string | undefined;
20
+ query?: string | undefined;
21
+ system?: string | undefined;
22
+ verbose?: boolean | undefined;
23
+ temperature?: number | undefined;
24
+ }, string | Record<string, any>, string | any[]>;
25
+ mock: AgentFunction<{
26
+ model?: string | undefined;
27
+ query?: string | undefined;
28
+ system?: string | undefined;
29
+ verbose?: boolean | undefined;
30
+ temperature?: number | undefined;
31
+ }, string | Record<string, any>, string | any[]>;
32
+ samples: {
33
+ inputs: string[];
34
+ params: {};
35
+ result: {
36
+ object: string;
37
+ id: string;
38
+ choices: {
39
+ message: {
40
+ role: string;
41
+ content: string;
42
+ };
43
+ finish_reason: string;
44
+ index: number;
45
+ logprobs: null;
46
+ }[];
47
+ created: number;
48
+ model: string;
49
+ };
50
+ }[];
51
+ description: string;
52
+ category: string[];
53
+ author: string;
54
+ repository: string;
55
+ license: string;
56
+ };
57
+ export default openaiAgentInfo;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.openAIMockAgent = exports.openAIAgent = void 0;
7
+ const openai_1 = __importDefault(require("openai"));
8
+ const utils_1 = require("../../utils/utils");
9
+ const openAIAgent = async ({ filterParams, params, inputs }) => {
10
+ const { verbose, query, system, temperature } = params;
11
+ const [input_query, previous_messages] = inputs;
12
+ // Notice that we ignore params.system if previous_message exists.
13
+ const messages = previous_messages && Array.isArray(previous_messages) ? previous_messages : system ? [{ role: "system", content: system }] : [];
14
+ const content = (query ? [query] : []).concat(input_query ? [input_query] : []).join("\n");
15
+ if (content) {
16
+ messages.push({
17
+ role: "user",
18
+ content,
19
+ });
20
+ }
21
+ if (verbose) {
22
+ console.log(messages);
23
+ }
24
+ const openai = new openai_1.default();
25
+ const chatStream = await openai.beta.chat.completions.stream({
26
+ model: params.model || "gpt-3.5-turbo",
27
+ messages,
28
+ temperature: temperature ?? 0.7,
29
+ stream: true,
30
+ });
31
+ for await (const message of chatStream) {
32
+ const token = message.choices[0].delta.content;
33
+ if (filterParams && filterParams.streamTokenCallback && token) {
34
+ filterParams.streamTokenCallback(token);
35
+ }
36
+ }
37
+ const chatCompletion = await chatStream.finalChatCompletion();
38
+ return chatCompletion;
39
+ };
40
+ exports.openAIAgent = openAIAgent;
41
+ const input_sample = "this is response result";
42
+ const result_sample = {
43
+ object: "chat.completion",
44
+ id: "chatcmpl-9N7HxXYbwjmdbdiQE94MHoVluQhyt",
45
+ choices: [
46
+ {
47
+ message: {
48
+ role: "assistant",
49
+ content: input_sample,
50
+ },
51
+ finish_reason: "stop",
52
+ index: 0,
53
+ logprobs: null,
54
+ },
55
+ ],
56
+ created: 1715296589,
57
+ model: "gpt-3.5-turbo-0125",
58
+ };
59
+ const openAIMockAgent = async ({ filterParams }) => {
60
+ for await (const token of input_sample.split("")) {
61
+ if (filterParams && filterParams.streamTokenCallback && token) {
62
+ await (0, utils_1.sleep)(100);
63
+ filterParams.streamTokenCallback(token);
64
+ }
65
+ }
66
+ return result_sample;
67
+ };
68
+ exports.openAIMockAgent = openAIMockAgent;
69
+ const openaiAgentInfo = {
70
+ name: "openAIAgent",
71
+ agent: exports.openAIAgent,
72
+ mock: exports.openAIMockAgent,
73
+ samples: [
74
+ {
75
+ inputs: [input_sample],
76
+ params: {},
77
+ result: result_sample,
78
+ },
79
+ ],
80
+ description: "Openai Agent",
81
+ category: ["llm"],
82
+ author: "Receptron team",
83
+ repository: "https://github.com/receptron/graphai",
84
+ license: "MIT",
85
+ };
86
+ exports.default = openaiAgentInfo;
@@ -7,7 +7,7 @@ exports.slashGPTAgent = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const slashgpt_1 = require("slashgpt");
9
9
  const config = new slashgpt_1.ChatConfig(path_1.default.resolve(__dirname));
10
- const slashGPTAgent = async ({ params, inputs, debugInfo: { verbose, nodeId } }) => {
10
+ const slashGPTAgent = async ({ params, inputs, debugInfo: { verbose, nodeId }, filterParams }) => {
11
11
  if (verbose) {
12
12
  console.log("executing", nodeId, params);
13
13
  }
@@ -15,7 +15,11 @@ const slashGPTAgent = async ({ params, inputs, debugInfo: { verbose, nodeId } })
15
15
  const query = params?.query ? [params.query] : [];
16
16
  const contents = query.concat(inputs);
17
17
  session.append_user_question(contents.join("\n"));
18
- await session.call_loop(() => { });
18
+ await session.call_loop(() => { }, (token) => {
19
+ if (filterParams && filterParams.streamTokenCallback && token) {
20
+ filterParams.streamTokenCallback(token);
21
+ }
22
+ });
19
23
  return session.history.messages();
20
24
  };
21
25
  exports.slashGPTAgent = slashGPTAgent;
@@ -1,11 +1,11 @@
1
1
  import { AgentFunction } from "../../graphai";
2
- export declare const dotProductAgent: AgentFunction<Record<never, never>, Array<number>, Array<Array<number>>>;
2
+ export declare const dotProductAgent: AgentFunction<Record<never, never>, Array<number>, Array<Array<number>> | Array<number>>;
3
3
  declare const dotProductAgentInfo: {
4
4
  name: string;
5
- agent: AgentFunction<Record<never, never>, number[], number[][]>;
6
- mock: AgentFunction<Record<never, never>, number[], number[][]>;
5
+ agent: AgentFunction<Record<never, never>, number[], number[] | number[][]>;
6
+ mock: AgentFunction<Record<never, never>, number[], number[] | number[][]>;
7
7
  samples: {
8
- inputs: number[][][];
8
+ inputs: (number[] | number[][])[];
9
9
  params: {};
10
10
  result: number[];
11
11
  }[];
@@ -5,14 +5,14 @@ exports.dotProductAgent = void 0;
5
5
  // typically used to calculate cosine similarity of embedding vectors.
6
6
  // Inputs:
7
7
  // inputs[0]: Two dimentional array of numbers.
8
- // inputs[1]: Two dimentional array of numbers (but the array size is 1 for the first dimention)
8
+ // inputs[1]: One dimentional array of numbers.
9
9
  // Outputs:
10
10
  // { contents: Array<number> } // array of docProduct of each vector (A[]) and vector B
11
11
  const dotProductAgent = async ({ inputs }) => {
12
12
  const embeddings = inputs[0];
13
- const reference = inputs[1][0];
13
+ const reference = inputs[1];
14
14
  if (embeddings[0].length != reference.length) {
15
- throw new Error("dotProduct: Length of vectors do not match.");
15
+ throw new Error(`dotProduct: Length of vectors do not match. ${embeddings[0].length}, ${reference.length}`);
16
16
  }
17
17
  const contents = embeddings.map((embedding) => {
18
18
  return embedding.reduce((dotProduct, value, index) => {
@@ -34,7 +34,7 @@ const dotProductAgentInfo = {
34
34
  [3, 4],
35
35
  [5, 6],
36
36
  ],
37
- [[3, 2]],
37
+ [3, 2],
38
38
  ],
39
39
  params: {},
40
40
  result: [7, 17, 27],
@@ -2,11 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.streamMockAgent = void 0;
4
4
  const utils_1 = require("../../utils/utils");
5
- const streamMockAgent = async ({ params }) => {
6
- const message = params.message;
5
+ const streamMockAgent = async ({ params, filterParams }) => {
6
+ const message = params.message || "";
7
7
  for await (const token of message.split("")) {
8
- if (params.streamCallback) {
9
- params.streamCallback(token);
8
+ if (filterParams.streamTokenCallback) {
9
+ filterParams.streamTokenCallback(token);
10
10
  }
11
11
  await (0, utils_1.sleep)(params.sleep || 100);
12
12
  }
@@ -19,9 +19,9 @@ const streamMockAgentInfo = {
19
19
  agent: exports.streamMockAgent,
20
20
  mock: exports.streamMockAgent,
21
21
  samples: [],
22
- description: "Sstream mock agent",
22
+ description: "Stream mock agent",
23
23
  category: [],
24
- author: "xSatoshi Nakajima",
24
+ author: "Isamu Arimoto",
25
25
  repository: "https://github.com/receptron/graphai",
26
26
  license: "MIT",
27
27
  };
package/lib/graphai.d.ts CHANGED
@@ -5,7 +5,7 @@ import { ComputedNode, StaticNode } from "./node";
5
5
  import { TaskManager } from "./task_manager";
6
6
  type GraphNodes = Record<string, ComputedNode | StaticNode>;
7
7
  export declare class GraphAI {
8
- private readonly version;
8
+ readonly version: number;
9
9
  private readonly graphId;
10
10
  private readonly data;
11
11
  private readonly loop?;
package/lib/graphai.js CHANGED
@@ -6,6 +6,7 @@ const utils_1 = require("./utils/utils");
6
6
  const validator_1 = require("./validator");
7
7
  const task_manager_1 = require("./task_manager");
8
8
  const defaultConcurrency = 8;
9
+ const latestVersion = 0.3;
9
10
  class GraphAI {
10
11
  // This method is called when either the GraphAI obect was created,
11
12
  // or we are about to start n-th iteration (n>2).
@@ -39,8 +40,7 @@ class GraphAI {
39
40
  });
40
41
  return nodes;
41
42
  }
42
- getValueFromResults(key, results) {
43
- const source = (0, utils_1.parseNodeName)(key);
43
+ getValueFromResults(source, results) {
44
44
  return (0, utils_1.getDataFromSource)(source.nodeId ? results[source.nodeId] : undefined, source);
45
45
  }
46
46
  // for static
@@ -58,7 +58,7 @@ class GraphAI {
58
58
  const update = node?.update;
59
59
  if (update && previousResults) {
60
60
  const result = this.getValueFromResults(update, previousResults);
61
- this.injectValue(nodeId, result, update);
61
+ this.injectValue(nodeId, result, update.nodeId);
62
62
  }
63
63
  }
64
64
  });
@@ -67,10 +67,13 @@ class GraphAI {
67
67
  this.logs = [];
68
68
  this.onLogCallback = (__log, __isUpdate) => { };
69
69
  this.repeatCount = 0;
70
- if (!data.version) {
71
- console.log("------------ no version");
70
+ if (!data.version && !options.taskManager) {
71
+ console.log("------------ missing version number");
72
+ }
73
+ this.version = data.version ?? latestVersion;
74
+ if (this.version < latestVersion) {
75
+ console.log(`------------ upgrade to ${latestVersion}!`);
72
76
  }
73
- this.version = data.version ?? 0.2;
74
77
  this.retryLimit = data.retry; // optional
75
78
  this.graphId = URL.createObjectURL(new Blob()).slice(-36);
76
79
  this.data = data;
@@ -195,7 +198,8 @@ class GraphAI {
195
198
  this.initializeNodes(results);
196
199
  // Notice that we need to check the while condition *after* calling initializeNodes.
197
200
  if (loop.while) {
198
- const value = this.getValueFromResults(loop.while, this.results(true));
201
+ const source = (0, utils_1.parseNodeName)(loop.while, this.version);
202
+ const value = this.getValueFromResults(source, this.results(true));
199
203
  // NOTE: We treat an empty array as false.
200
204
  if (Array.isArray(value) ? value.length === 0 : !value) {
201
205
  return false; // while condition is not met
package/lib/node.d.ts CHANGED
@@ -16,6 +16,7 @@ export declare class ComputedNode extends Node {
16
16
  readonly graphId: string;
17
17
  readonly isResult: boolean;
18
18
  readonly params: NodeDataParams;
19
+ private readonly filterParams;
19
20
  private readonly dynamicParams;
20
21
  readonly nestedGraph?: GraphData;
21
22
  readonly retryLimit: number;
@@ -49,7 +50,7 @@ export declare class ComputedNode extends Node {
49
50
  }
50
51
  export declare class StaticNode extends Node {
51
52
  value?: ResultData;
52
- readonly update?: string;
53
+ readonly update?: DataSource;
53
54
  readonly isResult: boolean;
54
55
  readonly isStaticNode = true;
55
56
  readonly isComputedNode = false;
package/lib/node.js CHANGED
@@ -38,6 +38,7 @@ class ComputedNode extends Node {
38
38
  this.isComputedNode = true;
39
39
  this.graphId = graphId;
40
40
  this.params = data.params ?? {};
41
+ this.filterParams = data.filterParams ?? {};
41
42
  this.nestedGraph = data.graph;
42
43
  if (typeof data.agent === "string") {
43
44
  this.agentId = data.agent;
@@ -54,21 +55,18 @@ class ComputedNode extends Node {
54
55
  this.isResult = data.isResult ?? false;
55
56
  this.priority = data.priority ?? 0;
56
57
  this.anyInput = data.anyInput ?? false;
57
- this.dataSources = (data.inputs ?? []).map((input) => (0, utils_2.parseNodeName)(input));
58
+ this.dataSources = (data.inputs ?? []).map((input) => (0, utils_2.parseNodeName)(input, graph.version));
58
59
  this.pendings = new Set(this.dataSources.filter((source) => source.nodeId).map((source) => source.nodeId));
59
60
  if (data.if) {
60
- this.ifSource = (0, utils_2.parseNodeName)(data.if);
61
+ this.ifSource = (0, utils_2.parseNodeName)(data.if, graph.version);
61
62
  (0, utils_2.assert)(!!this.ifSource.nodeId, `Invalid data source ${data.if}`);
62
63
  this.pendings.add(this.ifSource.nodeId);
63
64
  }
64
- const regex = /^\$\{([^{}]+)\}$/;
65
65
  this.dynamicParams = Object.keys(this.params).reduce((tmp, key) => {
66
- const value = this.params[key];
67
- const match = typeof value === "string" ? value.match(regex) : null;
68
- if (match) {
69
- const dataSource = (0, utils_2.parseNodeName)(match[1]);
66
+ const dataSource = (0, utils_2.parseNodeName)(this.params[key], graph.version < 0.3 ? 0.3 : graph.version);
67
+ if (dataSource.nodeId) {
68
+ (0, utils_2.assert)(!this.anyInput, "Dynamic params are not supported with anyInput");
70
69
  tmp[key] = dataSource;
71
- (0, utils_2.assert)(!!dataSource.nodeId, `Invalid data source ${key}:${value}`);
72
70
  this.pendings.add(dataSource.nodeId);
73
71
  }
74
72
  return tmp;
@@ -175,6 +173,9 @@ class ComputedNode extends Node {
175
173
  const agentFilter = this.graph.agentFilters[index++];
176
174
  if (agentFilter) {
177
175
  if (this.shouldApplyAgentFilter(agentFilter)) {
176
+ if (agentFilter.filterParams) {
177
+ context.filterParams = { ...agentFilter.filterParams, ...context.filterParams };
178
+ }
178
179
  return agentFilter.agent(context, next);
179
180
  }
180
181
  return next(context);
@@ -212,10 +213,11 @@ class ComputedNode extends Node {
212
213
  inputs: previousResults,
213
214
  debugInfo: {
214
215
  nodeId: this.nodeId,
216
+ agentId: this.agentId,
215
217
  retry: this.retryCount,
216
218
  verbose: this.graph.verbose,
217
219
  },
218
- filterParams: {},
220
+ filterParams: this.filterParams,
219
221
  log: localLog,
220
222
  };
221
223
  // NOTE: We use the existence of graph object in the agent-specific params to determine
@@ -282,7 +284,7 @@ class StaticNode extends Node {
282
284
  this.isStaticNode = true;
283
285
  this.isComputedNode = false;
284
286
  this.value = data.value;
285
- this.update = data.update;
287
+ this.update = data.update ? (0, utils_2.parseNodeName)(data.update, graph.version) : undefined;
286
288
  this.isResult = data.isResult ?? false;
287
289
  }
288
290
  injectValue(value, injectFrom) {
package/lib/type.d.ts CHANGED
@@ -27,11 +27,13 @@ export type StaticNodeData = {
27
27
  isResult?: boolean;
28
28
  };
29
29
  export type AgentNamelessFunction = (...param: any[]) => unknown;
30
+ export type AgentFilterParams = Record<string, any>;
30
31
  export type ComputedNodeData = {
31
32
  agent: string | AgentNamelessFunction;
32
33
  inputs?: Array<any>;
33
34
  anyInput?: boolean;
34
35
  params?: NodeDataParams;
36
+ filterParams?: AgentFilterParams;
35
37
  retry?: number;
36
38
  timeout?: number;
37
39
  if?: string;
@@ -59,11 +61,12 @@ export type AgentFunctionContext<ParamsType = DefaultParamsType, InputDataType =
59
61
  verbose: boolean;
60
62
  nodeId: string;
61
63
  retry: number;
64
+ agentId?: string;
62
65
  };
63
66
  graphData?: GraphData | string;
64
67
  agents?: AgentFunctionDictonary;
65
68
  taskManager?: TaskManager;
66
- filterParams: Record<string, any>;
69
+ filterParams: AgentFilterParams;
67
70
  log?: TransactionLog[];
68
71
  };
69
72
  export type AgentFunction<ParamsType = DefaultParamsType, ResultType = DefaultResultData, InputDataType = DefaultInputData> = (context: AgentFunctionContext<ParamsType, InputDataType>) => Promise<ResultData<ResultType>>;
@@ -73,6 +76,7 @@ export type AgentFilterInfo = {
73
76
  agent: AgentFilterFunction;
74
77
  agentIds?: string[];
75
78
  nodeIds?: string[];
79
+ filterParams?: AgentFilterParams;
76
80
  };
77
81
  export type AgentFunctionDictonary = Record<string, AgentFunction<any, any, any>>;
78
82
  export type AgentFunctionInfo = {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defaultTestAgents = void 0;
4
4
  const experimental_agents_1 = require("../experimental_agents");
5
+ const openai_agent_1 = require("../experimental_agents/llm_agents/openai_agent");
5
6
  exports.defaultTestAgents = {
6
7
  bypassAgent: experimental_agents_1.bypassAgent,
7
8
  echoAgent: experimental_agents_1.echoAgent,
@@ -19,4 +20,5 @@ exports.defaultTestAgents = {
19
20
  popAgent: experimental_agents_1.popAgent,
20
21
  shiftAgent: experimental_agents_1.shiftAgent,
21
22
  streamMockAgent: experimental_agents_1.streamMockAgent,
23
+ openAIMockAgent: openai_agent_1.openAIMockAgent,
22
24
  };
@@ -1,6 +1,6 @@
1
1
  import { DataSource, ResultData } 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, 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) => boolean;
6
6
  export declare const getDataFromSource: (result: ResultData | undefined, source: DataSource) => ResultData | undefined;
@@ -5,7 +5,7 @@ const sleep = async (milliseconds) => {
5
5
  return await new Promise((resolve) => setTimeout(resolve, milliseconds));
6
6
  };
7
7
  exports.sleep = sleep;
8
- const parseNodeName = (inputNodeId) => {
8
+ const parseNodeName_02 = (inputNodeId) => {
9
9
  if (typeof inputNodeId === "string") {
10
10
  const regex = /^"(.*)"$/;
11
11
  const match = inputNodeId.match(regex);
@@ -20,6 +20,24 @@ const parseNodeName = (inputNodeId) => {
20
20
  }
21
21
  return { value: inputNodeId }; // non-string literal
22
22
  };
23
+ const parseNodeName = (inputNodeId, version) => {
24
+ if (version === 0.2) {
25
+ return parseNodeName_02(inputNodeId);
26
+ }
27
+ if (typeof inputNodeId === "string") {
28
+ const regex = /^:(.*)$/;
29
+ const match = inputNodeId.match(regex);
30
+ if (!match) {
31
+ return { value: inputNodeId }; // string literal
32
+ }
33
+ const parts = match[1].split(".");
34
+ if (parts.length == 1) {
35
+ return { nodeId: parts[0] };
36
+ }
37
+ return { nodeId: parts[0], propIds: parts.slice(1) };
38
+ }
39
+ return { value: inputNodeId }; // non-string literal
40
+ };
23
41
  exports.parseNodeName = parseNodeName;
24
42
  function assert(condition, message, isWarn = false) {
25
43
  if (!condition) {
@@ -4,7 +4,7 @@ exports.agentValidator = void 0;
4
4
  const agentValidator = (graphAgentIds, agentIds) => {
5
5
  graphAgentIds.forEach((agentId) => {
6
6
  if (!agentIds.has(agentId)) {
7
- throw new Error("Invalid AgentId : " + agentId + " is not in callbackDictonary.");
7
+ throw new Error("Invalid Agent : " + agentId + " is not in callbackDictonary.");
8
8
  }
9
9
  });
10
10
  return true;
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.staticNodeAttributeKeys = exports.computedNodeAttributeKeys = exports.graphDataAttributeKeys = void 0;
4
4
  exports.graphDataAttributeKeys = ["nodes", "concurrency", "agentId", "loop", "verbose", "version"];
5
- exports.computedNodeAttributeKeys = ["inputs", "anyInput", "params", "retry", "timeout", "agent", "graph", "isResult", "priority", "if"];
5
+ exports.computedNodeAttributeKeys = ["inputs", "anyInput", "params", "retry", "timeout", "agent", "graph", "isResult", "priority", "if", "filterParams"];
6
6
  exports.staticNodeAttributeKeys = ["value", "update", "isResult"];
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.nodeValidator = void 0;
4
4
  const nodeValidator = (nodeData) => {
5
5
  if (nodeData.agent && nodeData.value) {
6
- throw new Error("Cannot set both agentId and value");
6
+ throw new Error("Cannot set both agent and value");
7
7
  }
8
8
  if (!("agent" in nodeData) && !("value" in nodeData)) {
9
- throw new Error("Either agentId or value is required");
9
+ throw new Error("Either agent or value is required");
10
10
  }
11
11
  return true;
12
12
  };
@@ -12,7 +12,7 @@ const relationValidator = (data, staticNodeIds, computedNodeIds) => {
12
12
  pendings[computedNodeId] = new Set();
13
13
  if ("inputs" in nodeData && nodeData && nodeData.inputs) {
14
14
  nodeData.inputs.forEach((inputNodeId) => {
15
- const sourceNodeId = (0, utils_1.parseNodeName)(inputNodeId).nodeId;
15
+ const sourceNodeId = (0, utils_1.parseNodeName)(inputNodeId, data.version ?? 0.02).nodeId;
16
16
  if (sourceNodeId) {
17
17
  if (!nodeIds.has(sourceNodeId)) {
18
18
  throw new Error(`Inputs not match: NodeId ${computedNodeId}, Inputs: ${sourceNodeId}`);
@@ -29,7 +29,7 @@ const relationValidator = (data, staticNodeIds, computedNodeIds) => {
29
29
  const nodeData = data.nodes[staticNodeId];
30
30
  if ("value" in nodeData && nodeData.update) {
31
31
  const update = nodeData.update;
32
- const updateNodeId = (0, utils_1.parseNodeName)(update).nodeId;
32
+ const updateNodeId = (0, utils_1.parseNodeName)(update, data.version ?? 0.02).nodeId;
33
33
  if (!updateNodeId) {
34
34
  throw new Error("Update it a literal");
35
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphai",
3
- "version": "0.2.8",
3
+ "version": "0.3.1",
4
4
  "description": "Asynchronous data flow execution engine for agentic AI apps.",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -28,12 +28,14 @@
28
28
  "homepage": "https://github.com/receptron/graphai#readme",
29
29
  "devDependencies": {
30
30
  "@inquirer/prompts": "^5.0.0",
31
+ "@types/cors": "^2.8.17",
31
32
  "@types/express": "^4.17.21",
32
33
  "@types/node": "^20.8.7",
33
34
  "@types/xml2js": "^0.4.14",
34
35
  "@typescript-eslint/eslint-plugin": "^6.8.0",
35
36
  "@typescript-eslint/parser": "^6.8.0",
36
37
  "arxiv-api-ts": "^1.0.3",
38
+ "cors": "^2.8.5",
37
39
  "deepmerge": "^4.3.1",
38
40
  "dotenv": "^16.4.5",
39
41
  "eslint": "^7.32.0 || ^8.2.0",
@@ -42,7 +44,7 @@
42
44
  "groq-sdk": "^0.3.3",
43
45
  "openai": "^4.12.4",
44
46
  "prettier": "^3.0.3",
45
- "slashgpt": "^0.0.8",
47
+ "slashgpt": "^0.0.9",
46
48
  "tiktoken": "^1.0.14",
47
49
  "ts-node": "^10.9.1",
48
50
  "tsc-alias": "^1.8.8",