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 +27 -26
- package/lib/experimental_agents/llm_agents/groq_agent.d.ts +10 -9
- package/lib/experimental_agents/llm_agents/groq_agent.js +1 -1
- package/lib/graphai.d.ts +1 -1
- package/lib/graphai.js +4 -1
- package/lib/node.js +4 -8
- package/lib/utils/utils.d.ts +1 -1
- package/lib/utils/utils.js +19 -1
- package/lib/validators/relation_validator.js +2 -2
- package/package.json +1 -1
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 "
|
|
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: ["
|
|
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: ["
|
|
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,
|
|
8
|
+
tools?: Record<string, Groq.Chat.CompletionCreateParams.Tool>;
|
|
8
9
|
temperature?: number;
|
|
9
10
|
max_tokens?: number;
|
|
10
|
-
tool_choice?: string | Record<string,
|
|
11
|
-
},
|
|
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,
|
|
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,
|
|
23
|
-
},
|
|
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,
|
|
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,
|
|
33
|
-
},
|
|
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
|
-
|
|
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
|
|
67
|
-
|
|
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;
|
package/lib/utils/utils.d.ts
CHANGED
|
@@ -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;
|
package/lib/utils/utils.js
CHANGED
|
@@ -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
|
|
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
|
}
|