-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathagent.ts
189 lines (175 loc) · 5.1 KB
/
agent.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import assert from "assert";
import { Model } from "./model_call";
import inquirer from "inquirer";
import config from "./config/main";
import ts from "typescript";
import fs from "fs/promises";
import path from "path";
import chalk from "chalk";
import hljs from "cli-highlight";
import getAllDiagnostics from "./diagnostics";
const isValidSchema = (schema: Record<string, any>) => {
return true; // TODO
};
function highlightCode(code: string, language = "typescript") {
const highlightedCode = hljs(code, { language });
return highlightedCode;
}
const getFunction = async (code: string) => {
const compiledFilePath = "./compiled.ts";
await fs.writeFile(compiledFilePath, code);
return async () => {
const res = await import(compiledFilePath);
return res.default;
};
};
const correctDiagnostics = async (
model: Model,
code: string,
task: string,
maxDiagnosticRetries = 3,
maxDiagnostics = 5,
): Promise<string | null> => {
let diagnosticResults = await getAllDiagnostics(code);
let diagnostics = diagnosticResults.diagnostics;
if (diagnostics.length === 0) {
console.log(chalk.green("No lints!"));
return code;
}
if (diagnostics.length > maxDiagnostics) {
console.log(chalk.red(`Too many lints! (${diagnostics.length})`));
return null;
}
while (maxDiagnosticRetries > 0 && diagnostics.length > 0) {
if (diagnostics.length === 0) {
console.log(chalk.green("No lints!"));
} else {
console.log(chalk.red(diagnosticResults.diagnosticsString));
model.addUserMessage(
`
I got these errors when I tried to run your code. Give me the updated version to fix them.
Don't tell me to install anything, do it with code or create an new Agent.
Errors from your code given by TypeScript and ESLint:
${diagnosticResults.diagnosticsString}
Reason step-by-step on why your code might be wrong and give me the updated version.
`,
);
console.log(chalk.blueBright("Trying to fix them..."));
code = await model.run();
console.log(chalk.blueBright("Corrected Candidte Plan:"));
console.log(`---\n${chalk.white(highlightCode(code))}\n---`);
diagnosticResults = await getAllDiagnostics(code);
diagnostics = diagnosticResults.diagnostics;
}
maxDiagnosticRetries--;
}
if (diagnostics.length > 0) {
console.log(
chalk.red(
`Failed to fix all lints! (${diagnostics.length}) and max retries reached`,
),
);
return null;
}
return code;
};
const getValidPlan = async (task: string, model: Model) => {
let parsedPlan = null as null | (() => Promise<any>);
let rawPlan = null as null | string;
while (!parsedPlan) {
model.reset();
rawPlan = await model.run();
let diagnosticResults = await getAllDiagnostics(rawPlan);
console.log(chalk.blueBright("Candidte Plan:"));
console.log(
`\n${chalk.white(highlightCode(diagnosticResults.lintedCode))}\n`,
);
if (diagnosticResults.diagnostics.length > 0) {
rawPlan = await correctDiagnostics(model, rawPlan, task);
}
if (rawPlan === null) {
console.log(chalk.red("Failed to fix lints, retrying..."));
continue;
}
if (rawPlan === "") {
console.log(chalk.red("Empty plan, retrying..."));
continue;
}
try {
parsedPlan = await getFunction(rawPlan);
} catch (e) {
console.log(e);
console.log(chalk.red("Invalid plan, retrying..."));
}
}
// uses an emoji to make it easier to find in the logs
console.log(chalk.green("✅ Plan validated successfully!"));
return {
rawPlan,
parsedPlan,
} as {
rawPlan: string;
parsedPlan: () => Promise<any>;
};
};
async function getAction() {
const { action } = await inquirer.prompt([
{
type: "list",
name: "action",
message: "What do you want to do with this plan?",
choices: [
{ name: "Execute", value: "e" },
{ name: "Quit", value: "q" },
{ name: "Retry", value: "r" },
],
},
]);
return action;
}
class Agent {
task: string;
input?: Record<string, any>;
outputSchema?: Record<string, any>;
constructor({
task: task,
input,
outputSchema,
}: {
task: string;
input?: Record<string, any>;
outputSchema?: Record<string, any>;
}) {
this.task = task;
this.input = input;
this.outputSchema = outputSchema;
if (this.outputSchema) {
assert(isValidSchema(this.outputSchema));
}
}
async run() {
const agentSystemMessage = config.agentPrompt.system();
const agentUserMessage = config.agentPrompt.user(
this.task,
this.input,
this.outputSchema,
);
const model = new Model(agentSystemMessage, agentUserMessage, false);
let answer = "";
let parsedPlan: null | (() => Promise<any>) = null;
while (parsedPlan === null) {
const result = await getValidPlan(this.task, model);
parsedPlan = result.parsedPlan;
answer = await getAction();
if (answer === "q") {
return;
}
if (answer === "r") {
parsedPlan = null;
}
}
const output = await (await parsedPlan());
return output;
}
}
export default Agent;