Skip to content

Commit 2883aa5

Browse files
committed
feature: improve base script
lower chalk version fix error with history length (lost in prev merge) add comments code style add prompt files
1 parent 0596626 commit 2883aa5

File tree

5 files changed

+222
-38
lines changed

5 files changed

+222
-38
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<h1 align="center"><big>Fail1</big></h1>
22

3-
<p align="center"><img src="assets/logo.png" alt="logo"/></p>
3+
<p align="center"><img src="assets/logo.png" alt="logo" width="200"/></p>
44

55
> This project is built to fail
66
> (until it doesn't)
@@ -81,7 +81,7 @@ OPENAI_API_KEY=your_api_key_here
8181
To start the code generation process, run the following command:
8282

8383
```shell
84-
node generation-000.js -G "<goal>" -g <generations> -p "<persona>" -t <temperature> -c -m "<model>"
84+
node generation-000.js -G "<goal>" -g <generations> -p "<persona>" -t <temperature> -c -m "<model>" -n "<negative_prompt>"
8585
```
8686

8787
## Options
@@ -94,6 +94,7 @@ node generation-000.js -G "<goal>" -g <generations> -p "<persona>" -t <temperatu
9494
| `--temperature` | `-t` | `number` | `0.2` | Sets the temperature for the generated code |
9595
| `--clean` | `-c` | `boolean` | `false` | Set to `true` if you want to remove any previously generated code |
9696
| `--model` | `-m` | `string` | `"gpt-3.5-turbo"` | Sets the model to use for generating the code |
97+
| `--negative` | `-n` | `string` | | Sets the negative prompt for the generated code |
9798

9899
## Functionality
99100

assets/logo.png

242 KB
Loading

base.js

Lines changed: 123 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ Examples
4545
alias: "p",
4646
default: "expert node.js developer, creative, code optimizer, interaction expert",
4747
},
48+
neg: {
49+
type: "string",
50+
alias: "n",
51+
},
4852
temperature: {
4953
type: "number",
5054
alias: "t",
@@ -73,51 +77,67 @@ const configuration = new Configuration({
7377
export const openai = new OpenAIApi(configuration);
7478

7579
const instructions = `
76-
The code should ALWAYS be IMPROVED or EXTENDED or REFACTORED or FIXED
77-
be creative and add new features
78-
The GOAL must be completed
79-
80-
GOAL: ${flags.goal}
80+
GOAL: ${flags.goal}${
81+
flags.neg
82+
? `, ${flags.neg
83+
.split(",")
84+
.map(neg => `no ${neg.trim()}`)
85+
.join(", ")}`
86+
: ""
87+
}
8188
89+
RULES:
90+
The code should ALWAYS be EXTENDED or REFACTORED or FIXED
91+
The GOAL must be completed
8292
increment the generation constant ONCE per generation
83-
Keep track of changes, EXTEND the CHANGELOG
93+
EXTEND the CHANGELOG
8494
NEVER use external apis with secret or key
85-
ONLY use es module syntax (with imports)
95+
EXCLUSIVELY use esm (imports)
8696
NEVER explain anything
87-
ALWAYS output ONLY JavaScript
97+
NEVER output markdown
98+
EXCLUSIVELY output JavaScript
99+
EVERYTHING happens in one file
100+
VERY IMPORTANT: the entire answer has to be valid JavaScript
88101
`;
89102

90103
const history = [];
91104

92105
export const generations = flags.generations;
93-
const maxSpawns = 3;
94-
let spawns = maxSpawns;
95106
let run = 0;
96107

108+
/**
109+
*
110+
* @param {number} generation
111+
* @returns {Promise<void>}
112+
*/
97113
export async function evolve(generation) {
98114
if (flags.help) {
99115
return;
100116
}
101-
if (spawns <= 0) {
102-
spinner.fail("Maximum retries reached");
103-
return;
104-
}
117+
105118
const nextGeneration = generation + 1;
119+
spinner.start(`Evolution ${generation} -> ${nextGeneration}`);
106120

107121
try {
108122
const filename = buildFilename(generation);
109123
const code = await fs.readFile(filename, "utf-8");
110-
spinner.start(`Generation ${generation} | ${spawns} spawns left`);
124+
125+
// Reduce history length
126+
history.shift();
127+
history.shift();
111128

112129
if (flags.clean) {
113130
// Remove all older generations
114-
const files = (await globby(["generation-*.js", "!generation-000.js"])).filter(
115-
file => file > buildFilename(generation)
116-
);
131+
const files = (
132+
await globby(["generation-*.js", "generation-*.md", "!generation-000.js"])
133+
).filter(file => file > buildFilename(generation));
117134
await Promise.all(files.map(async file => await fs.unlink(file)));
118135
}
119136

120137
if (run === 0) {
138+
const promptFilename = buildPromptFilename(generation);
139+
await fs.writeFile(promptFilename, buildPrompt(flags.goal, flags.neg));
140+
121141
history.push(
122142
{
123143
role: "user",
@@ -129,12 +149,14 @@ export async function evolve(generation) {
129149
}
130150
);
131151
}
152+
132153
run++;
154+
133155
history.push({
134156
role: "user",
135157
content: "continue the code",
136158
});
137-
spinner.start(`Evolution ${generation} -> ${generation + 1}`);
159+
138160
const completion = await openai.createChatCompletion({
139161
// model: "gpt-4",
140162
model: flags.model,
@@ -148,47 +170,121 @@ export async function evolve(generation) {
148170
max_tokens: 2048,
149171
temperature: flags.temperature,
150172
});
151-
spinner.stop();
173+
152174
const { content } = completion.data.choices[0].message;
153-
const cleanContent = content
175+
176+
// Clean GPT output (might return code block)
177+
const cleanContent = minify(content)
154178
.replace("```javascript", "")
155179
.replace("```js", "")
156-
.replace("```", "");
180+
.replace("```", "")
181+
.trim();
182+
183+
// Code should start with a comment (changelog). If it doesn't it is often not JavaScrip but
184+
// a human language response
157185
if (cleanContent.startsWith("/*")) {
158186
history.push({
159187
role: "assistant",
160188
content: cleanContent,
161189
});
190+
162191
const nextFilename = buildFilename(nextGeneration);
163192
await fs.writeFile(nextFilename, prettify(cleanContent));
164-
spinner.succeed(`Evolution ${generation} -> ${generation + 1}`);
193+
194+
spinner.succeed(`Evolution ${generation} -> ${nextGeneration}`);
195+
165196
await import(`./${nextFilename}`);
166197
} else {
167-
throw new Error("NOT_JAVASCRIPT");
198+
spinner.fail(`Evolution ${generation} -> ${nextGeneration}`);
199+
await handleError(new Error("NOT_JAVASCRIPT"), generation);
168200
}
169201
} catch (error) {
170-
spawns--;
171-
spinner.fail(`Evolution ${generation} -> ${generation + 1}`);
202+
spinner.fail(`Evolution ${generation} -> ${nextGeneration}`);
172203
await handleError(error, generation);
173204
}
174205
}
175206

207+
/**
208+
* Pads the given number or string with zeros to a length of 3 characters.
209+
*
210+
* @param {number} n - The input number or string to be padded.
211+
* @returns {string} - The padded string.
212+
*/
176213
export function pad(n) {
177214
return n.toString().padStart(3, "0");
178215
}
179216

217+
/**
218+
* Builds a filename string for the given generation number.
219+
*
220+
* @param {number} currentGeneration - The input generation number.
221+
* @returns {string} - The generated filename string.
222+
*/
180223
export function buildFilename(currentGeneration) {
181224
return path.join(".", `generation-${pad(currentGeneration)}.js`);
182225
}
183226

227+
/**
228+
* Builds a prompt filename string for the given generation number.
229+
*
230+
* @param {number} currentGeneration - The input generation number.
231+
* @returns {string} - The generated prompt filename string.
232+
*/
233+
export function buildPromptFilename(currentGeneration) {
234+
return path.join(".", `generation-${pad(currentGeneration)}.md`);
235+
}
236+
237+
/**
238+
* Builds a formatted string combining the given prompt and optional negativePrompt.
239+
*
240+
* @param {string} prompt - The main prompt to be included in the output.
241+
* @param {string} [negativePrompt] - The optional negative prompt to be included in the output.
242+
* @returns {string} - The formatted string combining the prompts.
243+
*/
244+
export function buildPrompt(prompt, negativePrompt = "") {
245+
return `# Configuration
246+
247+
## Prompt
248+
249+
\`\`\`shell
250+
${prompt}
251+
\`\`\`
252+
253+
## Negative Prompt
254+
255+
\`\`\`shell
256+
${negativePrompt}
257+
\`\`\`
258+
`;
259+
}
260+
261+
/**
262+
* Minifies the given code string by removing leading whitespace.
263+
*
264+
* @param {string} code - The input code to be minified.
265+
* @returns {string} - The minified code.
266+
*/
184267
export function minify(code) {
185-
return code.replace(/^\s+/gim, "");
268+
return code.replace(/^\s+/g, "");
186269
}
187270

271+
/**
272+
* Prettifies the given code string using Prettier.
273+
*
274+
* @param {string} code - The input code to be prettified.
275+
* @returns {string} - The prettified code.
276+
*/
188277
export function prettify(code) {
189278
return prettier.format(code, { semi: false, parser: "babel" });
190279
}
191280

281+
/**
282+
* Handles errors that occur during the code generation process.
283+
*
284+
* @param {Error} error - The error object containing information about the error.
285+
* @param {number} generation - The current generation number.
286+
* @returns {Promise<void>} - A promise that resolves when the error is handled.
287+
*/
192288
export async function handleError(error, generation) {
193289
const message = (
194290
error.response?.data?.error.message ??

0 commit comments

Comments
 (0)