graphai 0.2.8 → 0.3.0

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
 
@@ -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",
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
@@ -40,7 +40,7 @@ class GraphAI {
40
40
  return nodes;
41
41
  }
42
42
  getValueFromResults(key, results) {
43
- const source = (0, utils_1.parseNodeName)(key);
43
+ const source = (0, utils_1.parseNodeName)(key, this.version);
44
44
  return (0, utils_1.getDataFromSource)(source.nodeId ? results[source.nodeId] : undefined, source);
45
45
  }
46
46
  // for static
@@ -71,6 +71,9 @@ class GraphAI {
71
71
  console.log("------------ no version");
72
72
  }
73
73
  this.version = data.version ?? 0.2;
74
+ if (this.version < 0.3) {
75
+ console.log("------------ upgrade to 0.3!");
76
+ }
74
77
  this.retryLimit = data.retry; // optional
75
78
  this.graphId = URL.createObjectURL(new Blob()).slice(-36);
76
79
  this.data = data;
package/lib/node.js CHANGED
@@ -54,21 +54,17 @@ class ComputedNode extends Node {
54
54
  this.isResult = data.isResult ?? false;
55
55
  this.priority = data.priority ?? 0;
56
56
  this.anyInput = data.anyInput ?? false;
57
- this.dataSources = (data.inputs ?? []).map((input) => (0, utils_2.parseNodeName)(input));
57
+ this.dataSources = (data.inputs ?? []).map((input) => (0, utils_2.parseNodeName)(input, graph.version));
58
58
  this.pendings = new Set(this.dataSources.filter((source) => source.nodeId).map((source) => source.nodeId));
59
59
  if (data.if) {
60
- this.ifSource = (0, utils_2.parseNodeName)(data.if);
60
+ this.ifSource = (0, utils_2.parseNodeName)(data.if, graph.version);
61
61
  (0, utils_2.assert)(!!this.ifSource.nodeId, `Invalid data source ${data.if}`);
62
62
  this.pendings.add(this.ifSource.nodeId);
63
63
  }
64
- const regex = /^\$\{([^{}]+)\}$/;
65
64
  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]);
65
+ const dataSource = (0, utils_2.parseNodeName)(this.params[key], graph.version < 0.3 ? 0.3 : graph.version);
66
+ if (dataSource.nodeId) {
70
67
  tmp[key] = dataSource;
71
- (0, utils_2.assert)(!!dataSource.nodeId, `Invalid data source ${key}:${value}`);
72
68
  this.pendings.add(dataSource.nodeId);
73
69
  }
74
70
  return tmp;
@@ -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) {
@@ -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.0",
4
4
  "description": "Asynchronous data flow execution engine for agentic AI apps.",
5
5
  "main": "lib/index.js",
6
6
  "files": [