graphai 0.0.7 → 0.0.8
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 +5 -8
- package/lib/graphai.d.ts +7 -9
- package/lib/graphai.js +22 -27
- package/package.json +1 -1
- package/samples/agents/arxiv_agent.ts +1 -2
- package/samples/agents/parroting_agent.ts +5 -0
- package/samples/agents/slashgpt_agent.ts +34 -6
- package/samples/graphs/arxiv.yml +3 -7
- package/samples/graphs/slash_gpt.yml +9 -2
- package/samples/sample_co2.ts +103 -0
- package/samples/sample_gpt.ts +2 -2
- package/samples/sample_paper_ai.ts +1 -5
- package/src/graphai.ts +28 -34
- package/tests/agents/agents.ts +4 -5
- package/tests/graphai/test_dispatch.ts +6 -7
- package/tests/graphai/test_http_client.ts +3 -3
- package/tests/graphai/test_multiple_functions.ts +2 -2
- package/tests/graphs/test_dispatch.yml +6 -1
- package/tests/graphs/test_multiple_functions_1.yml +3 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
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.
|
|
6
6
|
|
|
7
|
-
You just need to describe dependencies among those API calls in a single data flow graph
|
|
7
|
+
You just need to describe dependencies among those API calls in a single data flow graph, create a GraphAI object with that graph, and run it.
|
|
8
8
|
|
|
9
9
|
Here is an example:
|
|
10
10
|
|
|
@@ -27,7 +27,7 @@ const sampleAgentFunction = async (context: AgentFunctionContext) => {
|
|
|
27
27
|
const {
|
|
28
28
|
nodeId, // taskA, taskB or taskC
|
|
29
29
|
params, // agent-specific parameters specified in the graph definition file
|
|
30
|
-
|
|
30
|
+
inputs // inputs from previous nodes
|
|
31
31
|
} = context;
|
|
32
32
|
// Agent-specific code (such as calling OpenAI's chat.completions API)
|
|
33
33
|
...
|
|
@@ -46,7 +46,7 @@ const sampleAgentFunction = async (context: AgentFunctionContext) => {
|
|
|
46
46
|
|
|
47
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
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,
|
|
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, outputs, retry, timeout, source, agentId) that dictate the node's behavior and its relationship with other nodes.
|
|
50
50
|
|
|
51
51
|
### DFG Structure
|
|
52
52
|
|
|
@@ -61,19 +61,16 @@ An agent is an abstract object, which takes some inputs and generates an output
|
|
|
61
61
|
|
|
62
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
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
|
|
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>>```).
|
|
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.
|
|
67
65
|
|
|
68
66
|
## Node Structure
|
|
69
67
|
|
|
70
68
|
- '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
69
|
- '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
|
-
- '
|
|
70
|
+
- 'outputs': An optinal property, which specifies the mapping from outputId to nodeId. If this property is set, the node become a special node called "dispatcher". A dispatcher node injects result(s) into specified nodes, enabling the dynamic flow of data.
|
|
73
71
|
- '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
72
|
- '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
73
|
- '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
74
|
|
|
78
75
|
## GraphAI class
|
|
79
76
|
|
package/lib/graphai.d.ts
CHANGED
|
@@ -13,12 +13,11 @@ export type NodeDataParams<ParamsType = Record<string, any>> = ParamsType;
|
|
|
13
13
|
type NodeData = {
|
|
14
14
|
inputs?: Array<string>;
|
|
15
15
|
params: NodeDataParams;
|
|
16
|
-
payloadMapping?: Record<string, string>;
|
|
17
16
|
retry?: number;
|
|
18
17
|
timeout?: number;
|
|
19
18
|
agentId?: string;
|
|
20
19
|
source?: boolean;
|
|
21
|
-
|
|
20
|
+
outputs?: Record<string, string>;
|
|
22
21
|
};
|
|
23
22
|
export type GraphData = {
|
|
24
23
|
nodes: Record<string, NodeData>;
|
|
@@ -32,7 +31,7 @@ export type TransactionLog = {
|
|
|
32
31
|
retryCount: number;
|
|
33
32
|
agentId?: string;
|
|
34
33
|
params?: NodeDataParams;
|
|
35
|
-
|
|
34
|
+
inputs?: Array<ResultData>;
|
|
36
35
|
errorMessage?: string;
|
|
37
36
|
result?: ResultData;
|
|
38
37
|
};
|
|
@@ -40,7 +39,7 @@ export type AgentFunctionContext<ParamsType, ResultType, PreviousResultType> = {
|
|
|
40
39
|
nodeId: string;
|
|
41
40
|
retry: number;
|
|
42
41
|
params: NodeDataParams<ParamsType>;
|
|
43
|
-
|
|
42
|
+
inputs: Array<PreviousResultType>;
|
|
44
43
|
};
|
|
45
44
|
export type AgentFunction<ParamsType = Record<string, any>, ResultType = Record<string, any>, PreviousResultType = Record<string, any>> = (context: AgentFunctionContext<ParamsType, ResultType, PreviousResultType>) => Promise<ResultData<ResultType>>;
|
|
46
45
|
export type AgentFunctionDictonary = Record<string, AgentFunction<any, any, any>>;
|
|
@@ -48,11 +47,10 @@ declare class Node {
|
|
|
48
47
|
nodeId: string;
|
|
49
48
|
params: NodeDataParams;
|
|
50
49
|
inputs: Array<string>;
|
|
51
|
-
payloadMapping: Record<string, string>;
|
|
52
50
|
pendings: Set<string>;
|
|
53
51
|
waitlist: Set<string>;
|
|
54
52
|
state: NodeState;
|
|
55
|
-
agentId
|
|
53
|
+
agentId?: string;
|
|
56
54
|
result: ResultData;
|
|
57
55
|
retryLimit: number;
|
|
58
56
|
retryCount: number;
|
|
@@ -60,13 +58,12 @@ declare class Node {
|
|
|
60
58
|
timeout?: number;
|
|
61
59
|
error?: Error;
|
|
62
60
|
source: boolean;
|
|
63
|
-
|
|
61
|
+
outputs?: Record<string, string>;
|
|
64
62
|
private graph;
|
|
65
63
|
constructor(nodeId: string, data: NodeData, graph: GraphAI);
|
|
66
64
|
asString(): string;
|
|
67
65
|
private retry;
|
|
68
66
|
removePending(nodeId: string): void;
|
|
69
|
-
payload(): ResultDataDictonary<Record<string, any>>;
|
|
70
67
|
pushQueueIfReady(): void;
|
|
71
68
|
injectResult(result: ResultData): void;
|
|
72
69
|
private setResult;
|
|
@@ -83,7 +80,7 @@ export declare class GraphAI {
|
|
|
83
80
|
private concurrency;
|
|
84
81
|
private logs;
|
|
85
82
|
constructor(data: GraphData, callbackDictonary: AgentFunctionDictonary | AgentFunction<any, any, any>);
|
|
86
|
-
getCallback(
|
|
83
|
+
getCallback(_agentId?: string): AgentFunction<any, any, any>;
|
|
87
84
|
asString(): string;
|
|
88
85
|
results(): ResultDataDictonary<Record<string, any>>;
|
|
89
86
|
errors(): Record<string, Error>;
|
|
@@ -94,5 +91,6 @@ export declare class GraphAI {
|
|
|
94
91
|
appendLog(log: TransactionLog): void;
|
|
95
92
|
transactionLogs(): TransactionLog[];
|
|
96
93
|
injectResult(nodeId: string, result: ResultData): void;
|
|
94
|
+
resultsOf(nodeIds: Array<string>): ResultData<Record<string, any>>[];
|
|
97
95
|
}
|
|
98
96
|
export {};
|
package/lib/graphai.js
CHANGED
|
@@ -19,14 +19,13 @@ class Node {
|
|
|
19
19
|
this.retryCount = 0;
|
|
20
20
|
this.nodeId = nodeId;
|
|
21
21
|
this.inputs = data.inputs ?? [];
|
|
22
|
-
this.payloadMapping = data.payloadMapping ?? {};
|
|
23
22
|
this.pendings = new Set(this.inputs);
|
|
24
23
|
this.params = data.params;
|
|
25
|
-
this.agentId = data.agentId
|
|
24
|
+
this.agentId = data.agentId;
|
|
26
25
|
this.retryLimit = data.retry ?? 0;
|
|
27
26
|
this.timeout = data.timeout;
|
|
28
27
|
this.source = data.source === true;
|
|
29
|
-
this.
|
|
28
|
+
this.outputs = data.outputs;
|
|
30
29
|
this.graph = graph;
|
|
31
30
|
}
|
|
32
31
|
asString() {
|
|
@@ -51,17 +50,6 @@ class Node {
|
|
|
51
50
|
this.pushQueueIfReady();
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
|
-
payload() {
|
|
55
|
-
return this.inputs.reduce((results, nodeId) => {
|
|
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
|
-
}
|
|
62
|
-
return results;
|
|
63
|
-
}, {});
|
|
64
|
-
}
|
|
65
53
|
pushQueueIfReady() {
|
|
66
54
|
if (this.pendings.size === 0 && !this.source) {
|
|
67
55
|
this.graph.pushQueue(this);
|
|
@@ -74,6 +62,7 @@ class Node {
|
|
|
74
62
|
retryCount: this.retryCount,
|
|
75
63
|
state: NodeState.Injected,
|
|
76
64
|
startTime: Date.now(),
|
|
65
|
+
result,
|
|
77
66
|
};
|
|
78
67
|
log.endTime = log.startTime;
|
|
79
68
|
this.graph.appendLog(log);
|
|
@@ -93,7 +82,6 @@ class Node {
|
|
|
93
82
|
});
|
|
94
83
|
}
|
|
95
84
|
async execute() {
|
|
96
|
-
const payload = this.payload();
|
|
97
85
|
const log = {
|
|
98
86
|
nodeId: this.nodeId,
|
|
99
87
|
retryCount: this.retryCount,
|
|
@@ -101,8 +89,11 @@ class Node {
|
|
|
101
89
|
startTime: Date.now(),
|
|
102
90
|
agentId: this.agentId,
|
|
103
91
|
params: this.params,
|
|
104
|
-
payload,
|
|
105
92
|
};
|
|
93
|
+
const results = this.graph.resultsOf(this.inputs);
|
|
94
|
+
if (results.length > 0) {
|
|
95
|
+
log.inputs = results;
|
|
96
|
+
}
|
|
106
97
|
this.graph.appendLog(log);
|
|
107
98
|
this.state = NodeState.Executing;
|
|
108
99
|
const transactionId = log.startTime;
|
|
@@ -124,7 +115,7 @@ class Node {
|
|
|
124
115
|
nodeId: this.nodeId,
|
|
125
116
|
retry: this.retryCount,
|
|
126
117
|
params: this.params,
|
|
127
|
-
|
|
118
|
+
inputs: results,
|
|
128
119
|
});
|
|
129
120
|
if (this.transactionId !== transactionId) {
|
|
130
121
|
console.log(`-- ${this.nodeId}: transactionId mismatch`);
|
|
@@ -132,10 +123,10 @@ class Node {
|
|
|
132
123
|
}
|
|
133
124
|
log.endTime = Date.now();
|
|
134
125
|
log.result = result;
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
126
|
+
const outputs = this.outputs;
|
|
127
|
+
if (outputs !== undefined) {
|
|
137
128
|
Object.keys(result).forEach((outputId) => {
|
|
138
|
-
const nodeId =
|
|
129
|
+
const nodeId = outputs[outputId];
|
|
139
130
|
this.graph.injectResult(nodeId, result[outputId]);
|
|
140
131
|
});
|
|
141
132
|
log.state = NodeState.Dispatched;
|
|
@@ -173,10 +164,7 @@ class GraphAI {
|
|
|
173
164
|
this.runningNodes = new Set();
|
|
174
165
|
this.nodeQueue = [];
|
|
175
166
|
this.logs = [];
|
|
176
|
-
this.callbackDictonary = typeof callbackDictonary === "function" ? {
|
|
177
|
-
if (this.callbackDictonary["default"] === undefined) {
|
|
178
|
-
throw new Error("No default function");
|
|
179
|
-
}
|
|
167
|
+
this.callbackDictonary = typeof callbackDictonary === "function" ? { _default: callbackDictonary } : callbackDictonary;
|
|
180
168
|
this.concurrency = data.concurrency ?? defaultConcurrency;
|
|
181
169
|
this.onComplete = () => {
|
|
182
170
|
console.error("-- SOMETHING IS WRONG: onComplete is called without run()");
|
|
@@ -194,11 +182,13 @@ class GraphAI {
|
|
|
194
182
|
});
|
|
195
183
|
});
|
|
196
184
|
}
|
|
197
|
-
getCallback(
|
|
198
|
-
|
|
185
|
+
getCallback(_agentId) {
|
|
186
|
+
const agentId = _agentId ?? "_default";
|
|
187
|
+
console.log(agentId);
|
|
188
|
+
if (this.callbackDictonary[agentId]) {
|
|
199
189
|
return this.callbackDictonary[agentId];
|
|
200
190
|
}
|
|
201
|
-
|
|
191
|
+
throw new Error("No agent: " + agentId);
|
|
202
192
|
}
|
|
203
193
|
asString() {
|
|
204
194
|
return Object.keys(this.nodes)
|
|
@@ -288,5 +278,10 @@ class GraphAI {
|
|
|
288
278
|
console.error("-- Invalid nodeId", nodeId);
|
|
289
279
|
}
|
|
290
280
|
}
|
|
281
|
+
resultsOf(nodeIds) {
|
|
282
|
+
return nodeIds.map((nodeId) => {
|
|
283
|
+
return this.nodes[nodeId].result;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
291
286
|
}
|
|
292
287
|
exports.GraphAI = GraphAI;
|
package/package.json
CHANGED
|
@@ -35,12 +35,11 @@ export const arxivAgent: AgentFunction<{ keywords: string[]; limit: number }, ar
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
export const arxiv2TextAgent: AgentFunction<{}, string, string[]> = async (context) => {
|
|
38
|
-
const result = (context
|
|
38
|
+
const result = (context.inputs[0] || [])
|
|
39
39
|
.map((r: any) => {
|
|
40
40
|
const { id, title, summary } = r;
|
|
41
41
|
return ["id:", id, "title:", title, "summary:", summary].join("\n");
|
|
42
42
|
})
|
|
43
43
|
.join("\n\n\n");
|
|
44
|
-
|
|
45
44
|
return result;
|
|
46
45
|
};
|
|
@@ -4,18 +4,46 @@ import { ChatSession, ChatConfig, ManifestData } from "slashgpt";
|
|
|
4
4
|
|
|
5
5
|
const config = new ChatConfig(path.resolve(__dirname));
|
|
6
6
|
|
|
7
|
-
export const
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
export const slashGPTFuncitons2TextAgent: AgentFunction<
|
|
8
|
+
{ function_data_key: string; result_key: number },
|
|
9
|
+
string,
|
|
10
|
+
{ function_data: { [key: string]: string[] } }
|
|
11
|
+
> = async (context) => {
|
|
12
|
+
const { params } = context;
|
|
13
|
+
const result = (context?.inputs[0].function_data[params.function_data_key] || []).map((r: any) => {
|
|
14
|
+
const { title, description } = r;
|
|
15
|
+
return ["title:", title, "description:", description].join("\n");
|
|
16
|
+
});
|
|
17
|
+
return result[params.result_key];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const slashGPTAgent: AgentFunction<
|
|
21
|
+
{ manifest: ManifestData; prompt: string; function_result?: boolean; debug?: boolean },
|
|
22
|
+
{ answer: string },
|
|
23
|
+
string
|
|
24
|
+
> = async (context) => {
|
|
25
|
+
const { params } = context;
|
|
26
|
+
if (params.debug) {
|
|
27
|
+
console.log("executing", context.nodeId, context);
|
|
28
|
+
}
|
|
29
|
+
const session = new ChatSession(config, params?.manifest ?? {});
|
|
10
30
|
|
|
11
|
-
const prompt =
|
|
31
|
+
const prompt = params?.prompt ?? [context.inputs].filter((a) => a !== undefined).join("\n\n");
|
|
32
|
+
// console.log(prompt);
|
|
12
33
|
session.append_user_question(prompt);
|
|
13
34
|
|
|
14
35
|
await session.call_loop(() => {});
|
|
15
|
-
|
|
36
|
+
|
|
37
|
+
// console.log(session.history)
|
|
38
|
+
const message = (() => {
|
|
39
|
+
if (params.function_result) {
|
|
40
|
+
return session.history.messages().find((m) => m.role === "function_result");
|
|
41
|
+
}
|
|
42
|
+
return session.history.last_message();
|
|
43
|
+
})();
|
|
16
44
|
if (message === undefined) {
|
|
17
45
|
throw new Error("No message in the history");
|
|
18
46
|
}
|
|
19
|
-
const result = { answer: message.content };
|
|
47
|
+
const result = { answer: message.content, function_data: message.function_data };
|
|
20
48
|
return result;
|
|
21
49
|
};
|
package/samples/graphs/arxiv.yml
CHANGED
|
@@ -5,17 +5,13 @@ nodes:
|
|
|
5
5
|
- llm
|
|
6
6
|
- gpt
|
|
7
7
|
limit: 10
|
|
8
|
-
|
|
8
|
+
agentId: arxivAgent
|
|
9
9
|
arxiv2TextAgent:
|
|
10
10
|
inputs: [searchArxiv]
|
|
11
|
-
|
|
12
|
-
payloadMapping:
|
|
13
|
-
searchArxiv: inputData
|
|
11
|
+
agentId: arxiv2TextAgent
|
|
14
12
|
slashGPTAgent:
|
|
15
13
|
inputs: [arxiv2TextAgent]
|
|
16
|
-
|
|
17
|
-
arxiv2TextAgent: inputData
|
|
18
|
-
functionName: slashGPTAgent
|
|
14
|
+
agentId: slashGPTAgent
|
|
19
15
|
params:
|
|
20
16
|
prompt: |
|
|
21
17
|
与えられたそれぞれの論文の要点をまとめ、以下の項目で日本語で出力せよ。それぞれの項目は最大でも180文字以内に要約せよ。
|
|
@@ -5,8 +5,15 @@ nodes:
|
|
|
5
5
|
node2:
|
|
6
6
|
inputs: [node1]
|
|
7
7
|
params:
|
|
8
|
-
prompt:
|
|
8
|
+
prompt: |
|
|
9
|
+
Please evaluate following business ideas.
|
|
10
|
+
${0}
|
|
9
11
|
node3:
|
|
10
12
|
inputs: [node1, node2]
|
|
11
13
|
params:
|
|
12
|
-
prompt:
|
|
14
|
+
prompt: |
|
|
15
|
+
Please pick the winner of this business idea contest.
|
|
16
|
+
[ideas]
|
|
17
|
+
${0}
|
|
18
|
+
[evalutations]
|
|
19
|
+
${1}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { GraphAI, AgentFunction } from "@/graphai";
|
|
2
|
+
import { readGraphaiData } from "~/utils/file_utils";
|
|
3
|
+
|
|
4
|
+
import { slashGPTAgent, slashGPTFuncitons2TextAgent } from "./agents/slashgpt_agent";
|
|
5
|
+
|
|
6
|
+
const graph_data = {
|
|
7
|
+
nodes: {
|
|
8
|
+
slashGPTAgent: {
|
|
9
|
+
agentId: "slashGPTAgent",
|
|
10
|
+
params: {
|
|
11
|
+
function_result: true,
|
|
12
|
+
prompt: "世界で協力してco2を減らす方法を教えて下さい",
|
|
13
|
+
manifest: {
|
|
14
|
+
prompt: "あなたは世界経済の専門家です。ユーザの問い合わせについて考え、10この結果をfunctionsの結果に返してください。",
|
|
15
|
+
skip_function_result: true,
|
|
16
|
+
actions: {
|
|
17
|
+
your_ideas: {
|
|
18
|
+
type: "message_template",
|
|
19
|
+
message: "dummy",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
functions: [
|
|
23
|
+
{
|
|
24
|
+
name: "your_ideas",
|
|
25
|
+
description: "Your answer to a user's inquiry",
|
|
26
|
+
parameters: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
methods: {
|
|
30
|
+
type: "array",
|
|
31
|
+
items: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
title: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "title",
|
|
37
|
+
},
|
|
38
|
+
description: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "description",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: ["title", "description"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
function2prompt0: {
|
|
54
|
+
params: {
|
|
55
|
+
function_data_key: "methods",
|
|
56
|
+
result_key: 0,
|
|
57
|
+
},
|
|
58
|
+
inputs: ["slashGPTAgent"],
|
|
59
|
+
agentId: "slashGPTFuncitons2TextAgent",
|
|
60
|
+
},
|
|
61
|
+
slashGPTAgent0: {
|
|
62
|
+
agentId: "slashGPTAgent",
|
|
63
|
+
params: {
|
|
64
|
+
debug: true,
|
|
65
|
+
manifest: {
|
|
66
|
+
prompt: "ユーザの問い合わせにある文章の専門家です。専門家として、ユーザのアイデアに対して実現可能なシナリオを800文字で書いてください。",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
inputs: ["function2prompt0"],
|
|
70
|
+
},
|
|
71
|
+
// 1
|
|
72
|
+
function2prompt1: {
|
|
73
|
+
params: {
|
|
74
|
+
debug: true,
|
|
75
|
+
function_data_key: "methods",
|
|
76
|
+
result_key: 1,
|
|
77
|
+
},
|
|
78
|
+
inputs: ["slashGPTAgent"],
|
|
79
|
+
agentId: "slashGPTFuncitons2TextAgent",
|
|
80
|
+
},
|
|
81
|
+
slashGPTAgent1: {
|
|
82
|
+
agentId: "slashGPTAgent",
|
|
83
|
+
params: {
|
|
84
|
+
debug: true,
|
|
85
|
+
manifest: {
|
|
86
|
+
prompt: "ユーザの問い合わせにある文章の専門家です。専門家として、ユーザのアイデアに対して実現可能なシナリオを800文字で書いてください。",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
inputs: ["function2prompt1"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
const runAgent = async () => {
|
|
94
|
+
const graph = new GraphAI(graph_data, { slashGPTAgent, slashGPTFuncitons2TextAgent });
|
|
95
|
+
const result = await graph.run();
|
|
96
|
+
console.log(result);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const main = async () => {
|
|
100
|
+
await runAgent();
|
|
101
|
+
console.log("COMPLETE 1");
|
|
102
|
+
};
|
|
103
|
+
main();
|
package/samples/sample_gpt.ts
CHANGED
|
@@ -8,8 +8,8 @@ const config = new ChatConfig(path.resolve(__dirname));
|
|
|
8
8
|
const slashGPTAgent: AgentFunction<{ manifest: ManifestData; prompt: string }, { answer: string }> = async (context) => {
|
|
9
9
|
console.log("executing", context.nodeId, context.params);
|
|
10
10
|
const session = new ChatSession(config, context.params.manifest ?? {});
|
|
11
|
-
const prompt =
|
|
12
|
-
return prompt.replace("${" +
|
|
11
|
+
const prompt = context.inputs.reduce((prompt, input, index) => {
|
|
12
|
+
return prompt.replace("${" + index + "}", input["answer"]);
|
|
13
13
|
}, context.params.prompt);
|
|
14
14
|
session.append_user_question(prompt);
|
|
15
15
|
|
|
@@ -7,14 +7,10 @@ import { readGraphaiData } from "~/utils/file_utils";
|
|
|
7
7
|
import { slashGPTAgent } from "./agents/slashgpt_agent";
|
|
8
8
|
import { arxivAgent, arxiv2TextAgent } from "./agents/arxiv_agent";
|
|
9
9
|
|
|
10
|
-
export const parrotingAgent: AgentFunction = async (context) => {
|
|
11
|
-
return {};
|
|
12
|
-
};
|
|
13
|
-
|
|
14
10
|
const runAgent = async (file: string) => {
|
|
15
11
|
const file_path = path.resolve(__dirname) + file;
|
|
16
12
|
const graph_data = readGraphaiData(file_path);
|
|
17
|
-
const graph = new GraphAI(graph_data, {
|
|
13
|
+
const graph = new GraphAI(graph_data, { arxivAgent: arxivAgent, arxiv2TextAgent, slashGPTAgent });
|
|
18
14
|
const result = await graph.run();
|
|
19
15
|
console.log(result);
|
|
20
16
|
};
|
package/src/graphai.ts
CHANGED
|
@@ -15,12 +15,11 @@ export type NodeDataParams<ParamsType = Record<string, any>> = ParamsType; // Ag
|
|
|
15
15
|
type NodeData = {
|
|
16
16
|
inputs?: Array<string>;
|
|
17
17
|
params: NodeDataParams;
|
|
18
|
-
payloadMapping?: Record<string, string>;
|
|
19
18
|
retry?: number;
|
|
20
19
|
timeout?: number; // msec
|
|
21
20
|
agentId?: string;
|
|
22
21
|
source?: boolean;
|
|
23
|
-
|
|
22
|
+
outputs?: Record<string, string>; // mapping from routeId to nodeId
|
|
24
23
|
};
|
|
25
24
|
|
|
26
25
|
export type GraphData = {
|
|
@@ -36,7 +35,7 @@ export type TransactionLog = {
|
|
|
36
35
|
retryCount: number;
|
|
37
36
|
agentId?: string;
|
|
38
37
|
params?: NodeDataParams;
|
|
39
|
-
|
|
38
|
+
inputs?: Array<ResultData>;
|
|
40
39
|
errorMessage?: string;
|
|
41
40
|
result?: ResultData;
|
|
42
41
|
};
|
|
@@ -45,7 +44,7 @@ export type AgentFunctionContext<ParamsType, ResultType, PreviousResultType> = {
|
|
|
45
44
|
nodeId: string;
|
|
46
45
|
retry: number;
|
|
47
46
|
params: NodeDataParams<ParamsType>;
|
|
48
|
-
|
|
47
|
+
inputs: Array<PreviousResultType>;
|
|
49
48
|
};
|
|
50
49
|
|
|
51
50
|
export type AgentFunction<ParamsType = Record<string, any>, ResultType = Record<string, any>, PreviousResultType = Record<string, any>> = (
|
|
@@ -58,11 +57,10 @@ class Node {
|
|
|
58
57
|
public nodeId: string;
|
|
59
58
|
public params: NodeDataParams; // Agent-specific parameters
|
|
60
59
|
public inputs: Array<string>; // List of nodes this node needs data from.
|
|
61
|
-
public payloadMapping: Record<string, string>;
|
|
62
60
|
public pendings: Set<string>; // List of nodes this node is waiting data from.
|
|
63
61
|
public waitlist = new Set<string>(); // List of nodes which need data from this node.
|
|
64
62
|
public state = NodeState.Waiting;
|
|
65
|
-
public agentId
|
|
63
|
+
public agentId?: string;
|
|
66
64
|
public result: ResultData = undefined;
|
|
67
65
|
public retryLimit: number;
|
|
68
66
|
public retryCount: number = 0;
|
|
@@ -70,21 +68,20 @@ class Node {
|
|
|
70
68
|
public timeout?: number; // msec
|
|
71
69
|
public error?: Error;
|
|
72
70
|
public source: boolean;
|
|
73
|
-
public
|
|
71
|
+
public outputs?: Record<string, string>; // Mapping from routeId to nodeId
|
|
74
72
|
|
|
75
73
|
private graph: GraphAI;
|
|
76
74
|
|
|
77
75
|
constructor(nodeId: string, data: NodeData, graph: GraphAI) {
|
|
78
76
|
this.nodeId = nodeId;
|
|
79
77
|
this.inputs = data.inputs ?? [];
|
|
80
|
-
this.payloadMapping = data.payloadMapping ?? {};
|
|
81
78
|
this.pendings = new Set(this.inputs);
|
|
82
79
|
this.params = data.params;
|
|
83
|
-
this.agentId = data.agentId
|
|
80
|
+
this.agentId = data.agentId;
|
|
84
81
|
this.retryLimit = data.retry ?? 0;
|
|
85
82
|
this.timeout = data.timeout;
|
|
86
83
|
this.source = data.source === true;
|
|
87
|
-
this.
|
|
84
|
+
this.outputs = data.outputs;
|
|
88
85
|
this.graph = graph;
|
|
89
86
|
}
|
|
90
87
|
|
|
@@ -112,17 +109,6 @@ class Node {
|
|
|
112
109
|
}
|
|
113
110
|
}
|
|
114
111
|
|
|
115
|
-
public payload() {
|
|
116
|
-
return this.inputs.reduce((results: ResultDataDictonary, nodeId) => {
|
|
117
|
-
if (this.payloadMapping && this.payloadMapping[nodeId]) {
|
|
118
|
-
results[this.payloadMapping[nodeId]] = this.graph.nodes[nodeId].result;
|
|
119
|
-
} else {
|
|
120
|
-
results[nodeId] = this.graph.nodes[nodeId].result;
|
|
121
|
-
}
|
|
122
|
-
return results;
|
|
123
|
-
}, {});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
112
|
public pushQueueIfReady() {
|
|
127
113
|
if (this.pendings.size === 0 && !this.source) {
|
|
128
114
|
this.graph.pushQueue(this);
|
|
@@ -136,6 +122,7 @@ class Node {
|
|
|
136
122
|
retryCount: this.retryCount,
|
|
137
123
|
state: NodeState.Injected,
|
|
138
124
|
startTime: Date.now(),
|
|
125
|
+
result,
|
|
139
126
|
};
|
|
140
127
|
log.endTime = log.startTime;
|
|
141
128
|
this.graph.appendLog(log);
|
|
@@ -156,7 +143,6 @@ class Node {
|
|
|
156
143
|
}
|
|
157
144
|
|
|
158
145
|
public async execute() {
|
|
159
|
-
const payload = this.payload();
|
|
160
146
|
const log: TransactionLog = {
|
|
161
147
|
nodeId: this.nodeId,
|
|
162
148
|
retryCount: this.retryCount,
|
|
@@ -164,8 +150,11 @@ class Node {
|
|
|
164
150
|
startTime: Date.now(),
|
|
165
151
|
agentId: this.agentId,
|
|
166
152
|
params: this.params,
|
|
167
|
-
payload,
|
|
168
153
|
};
|
|
154
|
+
const results = this.graph.resultsOf(this.inputs);
|
|
155
|
+
if (results.length > 0) {
|
|
156
|
+
log.inputs = results;
|
|
157
|
+
}
|
|
169
158
|
this.graph.appendLog(log);
|
|
170
159
|
this.state = NodeState.Executing;
|
|
171
160
|
const transactionId = log.startTime;
|
|
@@ -189,7 +178,7 @@ class Node {
|
|
|
189
178
|
nodeId: this.nodeId,
|
|
190
179
|
retry: this.retryCount,
|
|
191
180
|
params: this.params,
|
|
192
|
-
|
|
181
|
+
inputs: results,
|
|
193
182
|
});
|
|
194
183
|
if (this.transactionId !== transactionId) {
|
|
195
184
|
console.log(`-- ${this.nodeId}: transactionId mismatch`);
|
|
@@ -199,10 +188,10 @@ class Node {
|
|
|
199
188
|
log.endTime = Date.now();
|
|
200
189
|
log.result = result;
|
|
201
190
|
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
191
|
+
const outputs = this.outputs;
|
|
192
|
+
if (outputs !== undefined) {
|
|
204
193
|
Object.keys(result).forEach((outputId) => {
|
|
205
|
-
const nodeId =
|
|
194
|
+
const nodeId = outputs[outputId];
|
|
206
195
|
this.graph.injectResult(nodeId, result[outputId]);
|
|
207
196
|
});
|
|
208
197
|
log.state = NodeState.Dispatched;
|
|
@@ -247,10 +236,7 @@ export class GraphAI {
|
|
|
247
236
|
private logs: Array<TransactionLog> = [];
|
|
248
237
|
|
|
249
238
|
constructor(data: GraphData, callbackDictonary: AgentFunctionDictonary | AgentFunction<any, any, any>) {
|
|
250
|
-
this.callbackDictonary = typeof callbackDictonary === "function" ? {
|
|
251
|
-
if (this.callbackDictonary["default"] === undefined) {
|
|
252
|
-
throw new Error("No default function");
|
|
253
|
-
}
|
|
239
|
+
this.callbackDictonary = typeof callbackDictonary === "function" ? { _default: callbackDictonary } : callbackDictonary;
|
|
254
240
|
this.concurrency = data.concurrency ?? defaultConcurrency;
|
|
255
241
|
this.onComplete = () => {
|
|
256
242
|
console.error("-- SOMETHING IS WRONG: onComplete is called without run()");
|
|
@@ -270,11 +256,13 @@ export class GraphAI {
|
|
|
270
256
|
});
|
|
271
257
|
}
|
|
272
258
|
|
|
273
|
-
public getCallback(
|
|
274
|
-
|
|
259
|
+
public getCallback(_agentId?: string) {
|
|
260
|
+
const agentId = _agentId ?? "_default";
|
|
261
|
+
console.log(agentId);
|
|
262
|
+
if (this.callbackDictonary[agentId]) {
|
|
275
263
|
return this.callbackDictonary[agentId];
|
|
276
264
|
}
|
|
277
|
-
|
|
265
|
+
throw new Error("No agent: " + agentId);
|
|
278
266
|
}
|
|
279
267
|
|
|
280
268
|
public asString() {
|
|
@@ -372,4 +360,10 @@ export class GraphAI {
|
|
|
372
360
|
console.error("-- Invalid nodeId", nodeId);
|
|
373
361
|
}
|
|
374
362
|
}
|
|
363
|
+
|
|
364
|
+
public resultsOf(nodeIds: Array<string>) {
|
|
365
|
+
return nodeIds.map((nodeId) => {
|
|
366
|
+
return this.nodes[nodeId].result;
|
|
367
|
+
});
|
|
368
|
+
}
|
|
375
369
|
}
|
package/tests/agents/agents.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { AgentFunction } from "@/graphai";
|
|
|
2
2
|
import { sleep } from "~/utils/utils";
|
|
3
3
|
|
|
4
4
|
export const testAgent: AgentFunction<{ delay: number; fail: boolean }> = async (context) => {
|
|
5
|
-
const { nodeId, retry, params,
|
|
5
|
+
const { nodeId, retry, params, inputs } = context;
|
|
6
6
|
console.log("executing", nodeId);
|
|
7
7
|
await sleep(params.delay / (retry + 1));
|
|
8
8
|
|
|
@@ -11,10 +11,9 @@ export const testAgent: AgentFunction<{ delay: number; fail: boolean }> = async
|
|
|
11
11
|
console.log("failed (intentional)", nodeId, retry);
|
|
12
12
|
throw new Error("Intentional Failure");
|
|
13
13
|
} else {
|
|
14
|
-
const result =
|
|
15
|
-
(result,
|
|
16
|
-
|
|
17
|
-
return result;
|
|
14
|
+
const result = inputs.reduce(
|
|
15
|
+
(result: Record<string, any>, input: Record<string, any>) => {
|
|
16
|
+
return { ...result, ...input };
|
|
18
17
|
},
|
|
19
18
|
{ [nodeId]: "output" },
|
|
20
19
|
);
|
|
@@ -8,8 +8,8 @@ import { testAgent } from "~/agents/agents";
|
|
|
8
8
|
import test from "node:test";
|
|
9
9
|
import assert from "node:assert";
|
|
10
10
|
|
|
11
|
-
const dispatchAgent: AgentFunction<{ delay: number; fail: boolean }
|
|
12
|
-
const { nodeId, retry, params,
|
|
11
|
+
const dispatchAgent: AgentFunction<{ delay: number; fail: boolean }, Record<string, any>, Record<string, any>> = async (context) => {
|
|
12
|
+
const { nodeId, retry, params, inputs } = context;
|
|
13
13
|
console.log("executing", nodeId);
|
|
14
14
|
await sleep(params.delay / (retry + 1));
|
|
15
15
|
|
|
@@ -18,10 +18,9 @@ const dispatchAgent: AgentFunction<{ delay: number; fail: boolean }> = async (co
|
|
|
18
18
|
console.log("failed (intentional)", nodeId, retry);
|
|
19
19
|
throw new Error("Intentional Failure");
|
|
20
20
|
} else {
|
|
21
|
-
const result =
|
|
22
|
-
(result,
|
|
23
|
-
|
|
24
|
-
return result;
|
|
21
|
+
const result = inputs.reduce(
|
|
22
|
+
(result: Record<string, any>, input: Record<string, any>) => {
|
|
23
|
+
return { ...result, ...input };
|
|
25
24
|
},
|
|
26
25
|
{ [nodeId]: "dispatch" },
|
|
27
26
|
);
|
|
@@ -31,7 +30,7 @@ const dispatchAgent: AgentFunction<{ delay: number; fail: boolean }> = async (co
|
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
test("test dispatch", async () => {
|
|
34
|
-
const result = await fileTestRunner("/graphs/test_dispatch.yml", {
|
|
33
|
+
const result = await fileTestRunner("/graphs/test_dispatch.yml", { test: testAgent, alt: dispatchAgent });
|
|
35
34
|
assert.deepStrictEqual(result, {
|
|
36
35
|
node1: { node1: "output" },
|
|
37
36
|
node20: { node2: "dispatch" },
|
|
@@ -5,8 +5,8 @@ import test from "node:test";
|
|
|
5
5
|
import assert from "node:assert";
|
|
6
6
|
|
|
7
7
|
const httpClientAgent: AgentFunction<Record<string, string>> = async (context) => {
|
|
8
|
-
const { nodeId, retry, params,
|
|
9
|
-
console.log("executing", nodeId, params,
|
|
8
|
+
const { nodeId, retry, params, inputs } = context;
|
|
9
|
+
console.log("executing", nodeId, params, inputs);
|
|
10
10
|
|
|
11
11
|
const response = await fetch(params.url);
|
|
12
12
|
const result = await response.json();
|
|
@@ -31,7 +31,7 @@ const graph_data = {
|
|
|
31
31
|
},
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
test("test
|
|
34
|
+
test("test http client", async () => {
|
|
35
35
|
const result = await graphDataTestRunner("http.log", graph_data, httpClientAgent);
|
|
36
36
|
assert.deepStrictEqual(result, {
|
|
37
37
|
node1: { result: true, messages: ["hello"] },
|
|
@@ -33,8 +33,8 @@ const numberTestAgent: AgentFunction<{ number: number }, { [key: string]: number
|
|
|
33
33
|
return result;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
test("test
|
|
37
|
-
const result = await fileTestRunner("/graphs/test_multiple_functions_1.yml", {
|
|
36
|
+
test("test multiple function", async () => {
|
|
37
|
+
const result = await fileTestRunner("/graphs/test_multiple_functions_1.yml", { test1: testAgent1, test2: testAgent2, numberTestAgent });
|
|
38
38
|
assert.deepStrictEqual(result, {
|
|
39
39
|
node1: { node1: "output 1" },
|
|
40
40
|
node2: { node2: "output 1" },
|
|
@@ -2,23 +2,28 @@ nodes:
|
|
|
2
2
|
node1:
|
|
3
3
|
params:
|
|
4
4
|
delay: 500
|
|
5
|
+
agentId: test
|
|
5
6
|
node2:
|
|
6
7
|
params:
|
|
7
8
|
delay: 100
|
|
8
9
|
agentId: alt
|
|
9
|
-
|
|
10
|
+
outputs:
|
|
10
11
|
output1: node20
|
|
11
12
|
node20:
|
|
12
13
|
source: true
|
|
14
|
+
agentId: test
|
|
13
15
|
node3:
|
|
14
16
|
params:
|
|
15
17
|
delay: 500
|
|
16
18
|
inputs: [node1, node20]
|
|
19
|
+
agentId: test
|
|
17
20
|
node4:
|
|
18
21
|
params:
|
|
19
22
|
delay: 100
|
|
20
23
|
inputs: [node3]
|
|
24
|
+
agentId: test
|
|
21
25
|
node5:
|
|
22
26
|
params:
|
|
23
27
|
delay: 500
|
|
24
28
|
inputs: [node20, node4]
|
|
29
|
+
agentId: test
|
|
@@ -2,9 +2,11 @@ nodes:
|
|
|
2
2
|
node1:
|
|
3
3
|
params:
|
|
4
4
|
delay: 500
|
|
5
|
+
agentId: test1
|
|
5
6
|
node2:
|
|
6
7
|
params:
|
|
7
8
|
delay: 100
|
|
9
|
+
agentId: test1
|
|
8
10
|
node3:
|
|
9
11
|
params:
|
|
10
12
|
delay: 500
|
|
@@ -14,6 +16,7 @@ nodes:
|
|
|
14
16
|
params:
|
|
15
17
|
delay: 100
|
|
16
18
|
inputs: [node3]
|
|
19
|
+
agentId: test1
|
|
17
20
|
node5:
|
|
18
21
|
params:
|
|
19
22
|
delay: 500
|