graphai 0.0.9 → 0.0.11
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 +58 -0
- package/lib/experimental_agents/data_agent.d.ts +3 -0
- package/lib/experimental_agents/data_agent.js +25 -0
- package/lib/experimental_agents/index.d.ts +5 -0
- package/lib/experimental_agents/index.js +21 -0
- package/lib/experimental_agents/nested_agent.d.ts +6 -0
- package/lib/experimental_agents/nested_agent.js +22 -0
- package/lib/experimental_agents/slashgpt_agent.d.ts +9 -0
- package/lib/experimental_agents/slashgpt_agent.js +32 -0
- package/lib/experimental_agents/sleeper_agent.d.ts +10 -0
- package/lib/experimental_agents/sleeper_agent.js +28 -0
- package/lib/experimental_agents/string_agent.d.ts +7 -0
- package/lib/experimental_agents/string_agent.js +15 -0
- package/lib/graphai.d.ts +7 -2
- package/lib/graphai.js +3 -0
- package/lib/graphai_cli.js +2 -1
- package/lib/utils/utils.d.ts +1 -0
- package/lib/utils/utils.js +7 -0
- package/package.json +13 -3
- package/.eslintrc.js +0 -30
- package/.github/workflows/node.js.yml +0 -28
- package/.prettierrc +0 -3
- package/samples/agents/arxiv_agent.ts +0 -45
- package/samples/agents/parroting_agent.ts +0 -5
- package/samples/agents/slashgpt_agent.ts +0 -50
- package/samples/express.ts +0 -47
- package/samples/graphs/arxiv.yml +0 -25
- package/samples/graphs/slash_gpt.yml +0 -19
- package/samples/interaction.ts +0 -49
- package/samples/sample_co2.ts +0 -85
- package/samples/sample_gpt.ts +0 -40
- package/samples/sample_paper_ai.ts +0 -22
- package/src/graphai.ts +0 -417
- package/src/graphai_cli.ts +0 -35
- package/src/index.ts +0 -3
- package/tests/agents/agents.ts +0 -23
- package/tests/graphai/test_dispatch.ts +0 -41
- package/tests/graphai/test_fork.ts +0 -106
- package/tests/graphai/test_http_client.ts +0 -40
- package/tests/graphai/test_multiple_functions.ts +0 -46
- package/tests/graphai/test_sample_flow.ts +0 -71
- package/tests/graphs/test_base.yml +0 -19
- package/tests/graphs/test_cli.yaml +0 -4
- package/tests/graphs/test_dispatch.yml +0 -29
- package/tests/graphs/test_error.yml +0 -22
- package/tests/graphs/test_multiple_functions_1.yml +0 -30
- package/tests/graphs/test_retry.yml +0 -23
- package/tests/graphs/test_source.yml +0 -18
- package/tests/graphs/test_source2.yml +0 -19
- package/tests/graphs/test_timeout.yml +0 -22
- package/tests/http-server/README.md +0 -10
- package/tests/http-server/docs/llm.json +0 -4
- package/tests/http-server/docs/llm2.json +0 -4
- package/tests/utils/file_utils.ts +0 -33
- package/tests/utils/runner.ts +0 -40
- package/tests/utils/utils.ts +0 -3
- package/tsconfig.json +0 -113
package/samples/interaction.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { GraphAI, GraphData } from "@/graphai";
|
|
2
|
-
import * as readline from "readline";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import * as fs from "fs";
|
|
5
|
-
import { testAgent } from "~/agents/agents";
|
|
6
|
-
|
|
7
|
-
const getUserInput = async (question: string): Promise<string> => {
|
|
8
|
-
return new Promise((resolve, reject) => {
|
|
9
|
-
const rl = readline.createInterface({
|
|
10
|
-
input: process.stdin,
|
|
11
|
-
output: process.stdout,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
rl.question(question, (answer) => {
|
|
15
|
-
rl.close();
|
|
16
|
-
resolve(answer);
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const graph_data: GraphData = {
|
|
22
|
-
nodes: {
|
|
23
|
-
node1: {
|
|
24
|
-
source: true,
|
|
25
|
-
},
|
|
26
|
-
node2: {
|
|
27
|
-
inputs: ["node1"],
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const runAgent = async (query: string) => {
|
|
33
|
-
console.log("query=", query);
|
|
34
|
-
graph_data.nodes.node1.result = { query };
|
|
35
|
-
const graph = new GraphAI(graph_data, testAgent);
|
|
36
|
-
// graph.injectResult("node1", { query });
|
|
37
|
-
const result = await graph.run();
|
|
38
|
-
const log_path = path.resolve(__dirname) + "/../tests/logs/interaction.log";
|
|
39
|
-
fs.writeFileSync(log_path, JSON.stringify(graph.transactionLogs(), null, 2));
|
|
40
|
-
console.log(result);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const main = async () => {
|
|
44
|
-
const query = await getUserInput("Please enter your question: ");
|
|
45
|
-
await runAgent(query);
|
|
46
|
-
console.log("COMPLETE 1");
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
main();
|
package/samples/sample_co2.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
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
|
-
fork: 10,
|
|
55
|
-
params: {
|
|
56
|
-
function_data_key: "methods",
|
|
57
|
-
result_key: 0,
|
|
58
|
-
},
|
|
59
|
-
inputs: ["slashGPTAgent"],
|
|
60
|
-
agentId: "slashGPTFuncitons2TextAgent",
|
|
61
|
-
},
|
|
62
|
-
slashGPTAgent0: {
|
|
63
|
-
agentId: "slashGPTAgent",
|
|
64
|
-
fork: 10,
|
|
65
|
-
params: {
|
|
66
|
-
debug: true,
|
|
67
|
-
manifest: {
|
|
68
|
-
prompt: "ユーザの問い合わせにある文章の専門家です。専門家として、ユーザのアイデアに対して実現可能なシナリオを800文字で書いてください。",
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
inputs: ["function2prompt0"],
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
const runAgent = async () => {
|
|
76
|
-
const graph = new GraphAI(graph_data, { slashGPTAgent, slashGPTFuncitons2TextAgent });
|
|
77
|
-
const result = await graph.run();
|
|
78
|
-
console.log(result);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const main = async () => {
|
|
82
|
-
await runAgent();
|
|
83
|
-
console.log("COMPLETE 1");
|
|
84
|
-
};
|
|
85
|
-
main();
|
package/samples/sample_gpt.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import { GraphAI, AgentFunction } from "@/graphai";
|
|
4
|
-
import { ChatSession, ChatConfig, ManifestData } from "slashgpt";
|
|
5
|
-
import { readGraphaiData } from "~/utils/file_utils";
|
|
6
|
-
|
|
7
|
-
const config = new ChatConfig(path.resolve(__dirname));
|
|
8
|
-
|
|
9
|
-
const slashGPTAgent: AgentFunction<{ manifest: ManifestData; prompt: string }, { content: string }> = async (context) => {
|
|
10
|
-
console.log("executing", context.nodeId, context.params);
|
|
11
|
-
const session = new ChatSession(config, context.params.manifest ?? {});
|
|
12
|
-
const prompt = context.inputs.reduce((prompt, input, index) => {
|
|
13
|
-
return prompt.replace("${" + index + "}", input["content"]);
|
|
14
|
-
}, context.params.prompt);
|
|
15
|
-
session.append_user_question(prompt);
|
|
16
|
-
|
|
17
|
-
await session.call_loop(() => {});
|
|
18
|
-
const message = session.history.last_message();
|
|
19
|
-
if (message === undefined) {
|
|
20
|
-
throw new Error("No message in the history");
|
|
21
|
-
}
|
|
22
|
-
return message;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const runAgent = async (file: string) => {
|
|
26
|
-
const file_path = path.resolve(__dirname) + file;
|
|
27
|
-
const graph_data = readGraphaiData(file_path);
|
|
28
|
-
const graph = new GraphAI(graph_data, slashGPTAgent);
|
|
29
|
-
const results = (await graph.run()) as Record<string, any>;
|
|
30
|
-
|
|
31
|
-
const log_path = path.resolve(__dirname) + "/../tests/logs/" + path.basename(file_path).replace(/\.yml$/, ".log");
|
|
32
|
-
console.log(log_path);
|
|
33
|
-
fs.writeFileSync(log_path, JSON.stringify(graph.transactionLogs(), null, 2));
|
|
34
|
-
console.log(results["node3"]["content"]);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const main = async () => {
|
|
38
|
-
await runAgent("/graphs/slash_gpt.yml");
|
|
39
|
-
};
|
|
40
|
-
main();
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import search from "arXiv-api-ts";
|
|
3
|
-
|
|
4
|
-
import { GraphAI, AgentFunction } from "@/graphai";
|
|
5
|
-
import { readGraphaiData } from "~/utils/file_utils";
|
|
6
|
-
|
|
7
|
-
import { slashGPTAgent } from "./agents/slashgpt_agent";
|
|
8
|
-
import { arxivAgent, arxiv2TextAgent } from "./agents/arxiv_agent";
|
|
9
|
-
|
|
10
|
-
const runAgent = async (file: string) => {
|
|
11
|
-
const file_path = path.resolve(__dirname) + file;
|
|
12
|
-
const graph_data = readGraphaiData(file_path);
|
|
13
|
-
const graph = new GraphAI(graph_data, { arxivAgent: arxivAgent, arxiv2TextAgent, slashGPTAgent });
|
|
14
|
-
const result = await graph.run();
|
|
15
|
-
console.log(result);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const main = async () => {
|
|
19
|
-
await runAgent("/graphs/arxiv.yml");
|
|
20
|
-
console.log("COMPLETE 1");
|
|
21
|
-
};
|
|
22
|
-
main();
|
package/src/graphai.ts
DELETED
|
@@ -1,417 +0,0 @@
|
|
|
1
|
-
export enum NodeState {
|
|
2
|
-
Waiting = "waiting",
|
|
3
|
-
Executing = "executing",
|
|
4
|
-
Failed = "failed",
|
|
5
|
-
TimedOut = "timed-out",
|
|
6
|
-
Completed = "completed",
|
|
7
|
-
Injected = "injected",
|
|
8
|
-
Dispatched = "dispatched",
|
|
9
|
-
}
|
|
10
|
-
type ResultData<ResultType = Record<string, any>> = ResultType | undefined;
|
|
11
|
-
type ResultDataDictonary<ResultType = Record<string, any>> = Record<string, ResultData<ResultType>>;
|
|
12
|
-
|
|
13
|
-
export type NodeDataParams<ParamsType = Record<string, any>> = ParamsType; // Agent-specific parameters
|
|
14
|
-
|
|
15
|
-
type NodeData = {
|
|
16
|
-
inputs?: Array<string>;
|
|
17
|
-
params?: NodeDataParams;
|
|
18
|
-
retry?: number;
|
|
19
|
-
timeout?: number; // msec
|
|
20
|
-
agentId?: string;
|
|
21
|
-
fork?: number;
|
|
22
|
-
source?: boolean;
|
|
23
|
-
result?: ResultData; // preset result for source node.
|
|
24
|
-
outputs?: Record<string, string>; // mapping from routeId to nodeId
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export type GraphData = {
|
|
28
|
-
nodes: Record<string, NodeData>;
|
|
29
|
-
concurrency?: number;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export type TransactionLog = {
|
|
33
|
-
nodeId: string;
|
|
34
|
-
state: NodeState;
|
|
35
|
-
startTime: number;
|
|
36
|
-
endTime?: number;
|
|
37
|
-
retryCount: number;
|
|
38
|
-
agentId?: string;
|
|
39
|
-
params?: NodeDataParams;
|
|
40
|
-
inputs?: Array<ResultData>;
|
|
41
|
-
errorMessage?: string;
|
|
42
|
-
result?: ResultData;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export type AgentFunctionContext<ParamsType, ResultType, PreviousResultType> = {
|
|
46
|
-
nodeId: string;
|
|
47
|
-
forkIndex?: number;
|
|
48
|
-
retry: number;
|
|
49
|
-
params: NodeDataParams<ParamsType>;
|
|
50
|
-
inputs: Array<PreviousResultType>;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export type AgentFunction<ParamsType = Record<string, any>, ResultType = Record<string, any>, PreviousResultType = Record<string, any>> = (
|
|
54
|
-
context: AgentFunctionContext<ParamsType, ResultType, PreviousResultType>,
|
|
55
|
-
) => Promise<ResultData<ResultType>>;
|
|
56
|
-
|
|
57
|
-
export type AgentFunctionDictonary = Record<string, AgentFunction<any, any, any>>;
|
|
58
|
-
|
|
59
|
-
class Node {
|
|
60
|
-
public nodeId: string;
|
|
61
|
-
public params: NodeDataParams; // Agent-specific parameters
|
|
62
|
-
public inputs: Array<string>; // List of nodes this node needs data from.
|
|
63
|
-
public pendings: Set<string>; // List of nodes this node is waiting data from.
|
|
64
|
-
public waitlist = new Set<string>(); // List of nodes which need data from this node.
|
|
65
|
-
public state = NodeState.Waiting;
|
|
66
|
-
public agentId?: string;
|
|
67
|
-
public fork?: number;
|
|
68
|
-
public forkIndex?: number;
|
|
69
|
-
public result: ResultData = undefined;
|
|
70
|
-
public retryLimit: number;
|
|
71
|
-
public retryCount: number = 0;
|
|
72
|
-
public transactionId: undefined | number; // To reject callbacks from timed-out transactions
|
|
73
|
-
public timeout?: number; // msec
|
|
74
|
-
public error?: Error;
|
|
75
|
-
public source: boolean;
|
|
76
|
-
public outputs?: Record<string, string>; // Mapping from routeId to nodeId
|
|
77
|
-
|
|
78
|
-
private graph: GraphAI;
|
|
79
|
-
|
|
80
|
-
constructor(nodeId: string, forkIndex: number | undefined, data: NodeData, graph: GraphAI) {
|
|
81
|
-
this.nodeId = nodeId;
|
|
82
|
-
this.forkIndex = forkIndex;
|
|
83
|
-
this.inputs = data.inputs ?? [];
|
|
84
|
-
this.pendings = new Set(this.inputs);
|
|
85
|
-
this.params = data.params ?? {};
|
|
86
|
-
this.agentId = data.agentId;
|
|
87
|
-
this.fork = data.fork;
|
|
88
|
-
this.retryLimit = data.retry ?? 0;
|
|
89
|
-
this.timeout = data.timeout;
|
|
90
|
-
this.source = data.source === true;
|
|
91
|
-
this.outputs = data.outputs;
|
|
92
|
-
this.graph = graph;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
public asString() {
|
|
96
|
-
return `${this.nodeId}: ${this.state} ${[...this.waitlist]}`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private retry(state: NodeState, error: Error) {
|
|
100
|
-
if (this.retryCount < this.retryLimit) {
|
|
101
|
-
this.retryCount++;
|
|
102
|
-
this.execute();
|
|
103
|
-
} else {
|
|
104
|
-
this.state = state;
|
|
105
|
-
this.result = undefined;
|
|
106
|
-
this.error = error;
|
|
107
|
-
this.transactionId = undefined; // This is necessary for timeout case
|
|
108
|
-
this.graph.removeRunning(this);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public removePending(nodeId: string) {
|
|
113
|
-
this.pendings.delete(nodeId);
|
|
114
|
-
if (this.graph.isRunning) {
|
|
115
|
-
this.pushQueueIfReady();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public pushQueueIfReady() {
|
|
120
|
-
if (this.pendings.size === 0 && !this.source) {
|
|
121
|
-
this.graph.pushQueue(this);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
public injectResult(result: ResultData) {
|
|
126
|
-
if (this.source) {
|
|
127
|
-
const log: TransactionLog = {
|
|
128
|
-
nodeId: this.nodeId,
|
|
129
|
-
retryCount: this.retryCount,
|
|
130
|
-
state: NodeState.Injected,
|
|
131
|
-
startTime: Date.now(),
|
|
132
|
-
endTime: Date.now(),
|
|
133
|
-
result,
|
|
134
|
-
};
|
|
135
|
-
this.graph.appendLog(log);
|
|
136
|
-
this.setResult(result, NodeState.Injected);
|
|
137
|
-
} else {
|
|
138
|
-
console.error("- injectResult called on non-source node.", this.nodeId);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private setResult(result: ResultData, state: NodeState) {
|
|
143
|
-
this.state = state;
|
|
144
|
-
this.result = result;
|
|
145
|
-
this.waitlist.forEach((nodeId) => {
|
|
146
|
-
const node = this.graph.nodes[nodeId];
|
|
147
|
-
// Todo: Avoid running before Run()
|
|
148
|
-
node.removePending(this.nodeId);
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
public async execute() {
|
|
153
|
-
const results = this.graph.resultsOf(this.inputs);
|
|
154
|
-
const transactionId = Date.now();
|
|
155
|
-
const log: TransactionLog = {
|
|
156
|
-
nodeId: this.nodeId,
|
|
157
|
-
retryCount: this.retryCount,
|
|
158
|
-
state: NodeState.Executing,
|
|
159
|
-
startTime: transactionId,
|
|
160
|
-
agentId: this.agentId,
|
|
161
|
-
params: this.params,
|
|
162
|
-
inputs: results,
|
|
163
|
-
};
|
|
164
|
-
this.graph.appendLog(log);
|
|
165
|
-
this.state = NodeState.Executing;
|
|
166
|
-
this.transactionId = transactionId;
|
|
167
|
-
|
|
168
|
-
if (this.timeout && this.timeout > 0) {
|
|
169
|
-
setTimeout(() => {
|
|
170
|
-
if (this.state === NodeState.Executing && this.transactionId === transactionId) {
|
|
171
|
-
console.log(`-- ${this.nodeId}: timeout ${this.timeout}`);
|
|
172
|
-
log.errorMessage = "Timeout";
|
|
173
|
-
log.state = NodeState.TimedOut;
|
|
174
|
-
log.endTime = Date.now();
|
|
175
|
-
this.retry(NodeState.TimedOut, Error("Timeout"));
|
|
176
|
-
}
|
|
177
|
-
}, this.timeout);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
const callback = this.graph.getCallback(this.agentId);
|
|
182
|
-
const result = await callback({
|
|
183
|
-
nodeId: this.nodeId,
|
|
184
|
-
retry: this.retryCount,
|
|
185
|
-
params: this.params,
|
|
186
|
-
inputs: results,
|
|
187
|
-
forkIndex: this.forkIndex,
|
|
188
|
-
});
|
|
189
|
-
if (this.transactionId !== transactionId) {
|
|
190
|
-
console.log(`-- ${this.nodeId}: transactionId mismatch`);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
log.endTime = Date.now();
|
|
195
|
-
log.result = result;
|
|
196
|
-
|
|
197
|
-
const outputs = this.outputs;
|
|
198
|
-
if (outputs !== undefined) {
|
|
199
|
-
Object.keys(result).forEach((outputId) => {
|
|
200
|
-
const nodeId = outputs[outputId];
|
|
201
|
-
this.graph.injectResult(nodeId, result[outputId]);
|
|
202
|
-
});
|
|
203
|
-
log.state = NodeState.Dispatched;
|
|
204
|
-
this.state = NodeState.Dispatched;
|
|
205
|
-
this.graph.removeRunning(this);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
log.state = NodeState.Completed;
|
|
209
|
-
this.setResult(result, NodeState.Completed);
|
|
210
|
-
this.graph.removeRunning(this);
|
|
211
|
-
} catch (error) {
|
|
212
|
-
if (this.transactionId !== transactionId) {
|
|
213
|
-
console.log(`-- ${this.nodeId}: transactionId mismatch(error)`);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
log.state = NodeState.Failed;
|
|
217
|
-
log.endTime = Date.now();
|
|
218
|
-
if (error instanceof Error) {
|
|
219
|
-
log.errorMessage = error.message;
|
|
220
|
-
this.retry(NodeState.Failed, error);
|
|
221
|
-
} else {
|
|
222
|
-
console.error(`-- ${this.nodeId}: Unexpecrted error was caught`);
|
|
223
|
-
log.errorMessage = "Unknown";
|
|
224
|
-
this.retry(NodeState.Failed, Error("Unknown"));
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
type GraphNodes = Record<string, Node>;
|
|
231
|
-
|
|
232
|
-
const defaultConcurrency = 8;
|
|
233
|
-
|
|
234
|
-
export class GraphAI {
|
|
235
|
-
public nodes: GraphNodes;
|
|
236
|
-
public callbackDictonary: AgentFunctionDictonary;
|
|
237
|
-
public isRunning = false;
|
|
238
|
-
private runningNodes = new Set<string>();
|
|
239
|
-
private nodeQueue: Array<Node> = [];
|
|
240
|
-
private onComplete: () => void;
|
|
241
|
-
private concurrency: number;
|
|
242
|
-
private logs: Array<TransactionLog> = [];
|
|
243
|
-
|
|
244
|
-
constructor(data: GraphData, callbackDictonary: AgentFunctionDictonary | AgentFunction<any, any, any>) {
|
|
245
|
-
this.callbackDictonary = typeof callbackDictonary === "function" ? { _default: callbackDictonary } : callbackDictonary;
|
|
246
|
-
this.concurrency = data.concurrency ?? defaultConcurrency;
|
|
247
|
-
this.onComplete = () => {
|
|
248
|
-
console.error("-- SOMETHING IS WRONG: onComplete is called without run()");
|
|
249
|
-
};
|
|
250
|
-
const nodeId2forkedNodeIds: Record<string, string[]> = {};
|
|
251
|
-
const forkedNodeId2Index: Record<string, number> = {};
|
|
252
|
-
|
|
253
|
-
// Create node instances from data.nodes
|
|
254
|
-
this.nodes = Object.keys(data.nodes).reduce((nodes: GraphNodes, nodeId: string) => {
|
|
255
|
-
const fork = data.nodes[nodeId].fork;
|
|
256
|
-
if (fork) {
|
|
257
|
-
// For fork, change the nodeId and increase the node
|
|
258
|
-
nodeId2forkedNodeIds[nodeId] = new Array(fork).fill(undefined).map((_, i) => {
|
|
259
|
-
const forkedNodeId = `${nodeId}_${i}`;
|
|
260
|
-
nodes[forkedNodeId] = new Node(forkedNodeId, i, data.nodes[nodeId], this);
|
|
261
|
-
// Data for pending and waiting
|
|
262
|
-
forkedNodeId2Index[forkedNodeId] = i;
|
|
263
|
-
return forkedNodeId;
|
|
264
|
-
});
|
|
265
|
-
} else {
|
|
266
|
-
nodes[nodeId] = new Node(nodeId, undefined, data.nodes[nodeId], this);
|
|
267
|
-
}
|
|
268
|
-
return nodes;
|
|
269
|
-
}, {});
|
|
270
|
-
|
|
271
|
-
// Generate the waitlist for each node, and update the pendings in case of forked node.
|
|
272
|
-
Object.keys(this.nodes).forEach((nodeId) => {
|
|
273
|
-
const node = this.nodes[nodeId];
|
|
274
|
-
node.pendings.forEach((pending) => {
|
|
275
|
-
// If the pending(previous) node is forking
|
|
276
|
-
if (nodeId2forkedNodeIds[pending]) {
|
|
277
|
-
// update node.pending and pending(previous) node.wailtlist
|
|
278
|
-
if (node.fork) {
|
|
279
|
-
// 1:1 if current nodes are also forking.
|
|
280
|
-
const newPendingId = nodeId2forkedNodeIds[pending][forkedNodeId2Index[nodeId]];
|
|
281
|
-
this.nodes[newPendingId].waitlist.add(nodeId); // previousNode
|
|
282
|
-
node.pendings.add(newPendingId);
|
|
283
|
-
} else {
|
|
284
|
-
// 1:n if current node is not forking.
|
|
285
|
-
nodeId2forkedNodeIds[pending].forEach((newPendingId) => {
|
|
286
|
-
this.nodes[newPendingId].waitlist.add(nodeId); // previousNode
|
|
287
|
-
node.pendings.add(newPendingId);
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
node.pendings.delete(pending);
|
|
291
|
-
} else {
|
|
292
|
-
this.nodes[pending].waitlist.add(nodeId); // previousNode
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
node.inputs = Array.from(node.pendings); // for fork.
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
// If the result property is specified, inject it.
|
|
299
|
-
// NOTE: This must be done at the end of this constructor
|
|
300
|
-
Object.keys(data.nodes).forEach((nodeId) => {
|
|
301
|
-
const result = data.nodes[nodeId].result;
|
|
302
|
-
if (result) {
|
|
303
|
-
this.injectResult(nodeId, result);
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
public getCallback(_agentId?: string) {
|
|
309
|
-
const agentId = _agentId ?? "_default";
|
|
310
|
-
if (this.callbackDictonary[agentId]) {
|
|
311
|
-
return this.callbackDictonary[agentId];
|
|
312
|
-
}
|
|
313
|
-
throw new Error("No agent: " + agentId);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
public asString() {
|
|
317
|
-
return Object.keys(this.nodes)
|
|
318
|
-
.map((nodeId) => {
|
|
319
|
-
return this.nodes[nodeId].asString();
|
|
320
|
-
})
|
|
321
|
-
.join("\n");
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
public results() {
|
|
325
|
-
return Object.keys(this.nodes).reduce((results: ResultDataDictonary, nodeId) => {
|
|
326
|
-
const node = this.nodes[nodeId];
|
|
327
|
-
if (node.result !== undefined) {
|
|
328
|
-
results[nodeId] = node.result;
|
|
329
|
-
}
|
|
330
|
-
return results;
|
|
331
|
-
}, {});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
public errors() {
|
|
335
|
-
return Object.keys(this.nodes).reduce((errors: Record<string, Error>, nodeId) => {
|
|
336
|
-
const node = this.nodes[nodeId];
|
|
337
|
-
if (node.error !== undefined) {
|
|
338
|
-
errors[nodeId] = node.error;
|
|
339
|
-
}
|
|
340
|
-
return errors;
|
|
341
|
-
}, {});
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
public async run() {
|
|
345
|
-
if (this.isRunning) {
|
|
346
|
-
console.error("-- Already Running");
|
|
347
|
-
}
|
|
348
|
-
this.isRunning = true;
|
|
349
|
-
// Nodes without pending data should run immediately.
|
|
350
|
-
Object.keys(this.nodes).forEach((nodeId) => {
|
|
351
|
-
const node = this.nodes[nodeId];
|
|
352
|
-
node.pushQueueIfReady();
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
return new Promise((resolve, reject) => {
|
|
356
|
-
this.onComplete = () => {
|
|
357
|
-
this.isRunning = false;
|
|
358
|
-
const errors = this.errors();
|
|
359
|
-
const nodeIds = Object.keys(errors);
|
|
360
|
-
if (nodeIds.length > 0) {
|
|
361
|
-
reject(errors[nodeIds[0]]);
|
|
362
|
-
} else {
|
|
363
|
-
resolve(this.results());
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
private runNode(node: Node) {
|
|
370
|
-
this.runningNodes.add(node.nodeId);
|
|
371
|
-
node.execute();
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
public pushQueue(node: Node) {
|
|
375
|
-
if (this.runningNodes.size < this.concurrency) {
|
|
376
|
-
this.runNode(node);
|
|
377
|
-
} else {
|
|
378
|
-
this.nodeQueue.push(node);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
public removeRunning(node: Node) {
|
|
383
|
-
this.runningNodes.delete(node.nodeId);
|
|
384
|
-
if (this.nodeQueue.length > 0) {
|
|
385
|
-
const n = this.nodeQueue.shift();
|
|
386
|
-
if (n) {
|
|
387
|
-
this.runNode(n);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (this.runningNodes.size === 0) {
|
|
391
|
-
this.onComplete();
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
public appendLog(log: TransactionLog) {
|
|
396
|
-
this.logs.push(log);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
public transactionLogs() {
|
|
400
|
-
return this.logs;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
public injectResult(nodeId: string, result: ResultData) {
|
|
404
|
-
const node = this.nodes[nodeId];
|
|
405
|
-
if (node) {
|
|
406
|
-
node.injectResult(result);
|
|
407
|
-
} else {
|
|
408
|
-
console.error("-- Invalid nodeId", nodeId);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
public resultsOf(nodeIds: Array<string>) {
|
|
413
|
-
return nodeIds.map((nodeId) => {
|
|
414
|
-
return this.nodes[nodeId].result;
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
}
|
package/src/graphai_cli.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { GraphAI, AgentFunction } from "./graphai";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
6
|
-
import YAML from "yaml";
|
|
7
|
-
|
|
8
|
-
const testAgent: AgentFunction<{ delay: number; fail: boolean }> = async (context) => {
|
|
9
|
-
return {};
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const main = async () => {
|
|
13
|
-
const file = process.argv[2];
|
|
14
|
-
if (file === undefined) {
|
|
15
|
-
console.log("no file");
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const file_path = path.resolve(process.cwd() + "/" + file);
|
|
19
|
-
if (!fs.existsSync(file_path)) {
|
|
20
|
-
console.log("no file");
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
const graph_data_file = fs.readFileSync(file_path, "utf8");
|
|
25
|
-
const graph_data = YAML.parse(graph_data_file);
|
|
26
|
-
|
|
27
|
-
const graph = new GraphAI(graph_data, { test: testAgent });
|
|
28
|
-
const results = await graph.run();
|
|
29
|
-
console.log(results);
|
|
30
|
-
} catch (e) {
|
|
31
|
-
console.log("error", e);
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
main();
|
package/src/index.ts
DELETED
package/tests/agents/agents.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { AgentFunction } from "@/graphai";
|
|
2
|
-
import { sleep } from "~/utils/utils";
|
|
3
|
-
|
|
4
|
-
export const testAgent: AgentFunction<{ delay: number; fail: boolean }> = async (context) => {
|
|
5
|
-
const { nodeId, retry, params, inputs } = context;
|
|
6
|
-
console.log("executing", nodeId);
|
|
7
|
-
await sleep(params.delay / (retry + 1));
|
|
8
|
-
|
|
9
|
-
if (params.fail && retry < 2) {
|
|
10
|
-
const result = { [nodeId]: "failed" };
|
|
11
|
-
console.log("failed (intentional)", nodeId, retry);
|
|
12
|
-
throw new Error("Intentional Failure");
|
|
13
|
-
} else {
|
|
14
|
-
const result = inputs.reduce(
|
|
15
|
-
(result: Record<string, any>, input: Record<string, any>) => {
|
|
16
|
-
return { ...result, ...input };
|
|
17
|
-
},
|
|
18
|
-
{ [nodeId]: "output" },
|
|
19
|
-
);
|
|
20
|
-
console.log("completing", nodeId);
|
|
21
|
-
return result;
|
|
22
|
-
}
|
|
23
|
-
};
|