allow multi-file projects

This commit is contained in:
Marcus Schiesser
2024-09-15 19:05:45 +07:00
parent 4c1672d227
commit 4f1e03bf3e
6 changed files with 115 additions and 21 deletions
+2
View File
@@ -7,12 +7,14 @@ The multi-agents are implemented using [Workflows](https://ts.llamaindex.ai/modu
## Getting Started
1. Clone the repository:
```
git clone https://github.com/run-llama/app-creator.git
cd app-creator
```
2. Install dependencies:
```
pnpm install
```
+8 -4
View File
@@ -6,13 +6,14 @@ import {
WorkflowEvent,
} from "@llamaindex/core/workflow";
import { OpenAI, Settings } from "llamaindex";
import { PackageEvent, packager } from "./packager";
const MAX_REVIEWS = 3;
// Create custom event types
export class MessageEvent extends WorkflowEvent<{ msg: string }> {}
export class CodeEvent extends WorkflowEvent<{ code: string }> {}
export class ReviewEvent extends WorkflowEvent<{
class CodeEvent extends WorkflowEvent<{ code: string }> {}
class ReviewEvent extends WorkflowEvent<{
review: string;
code: string;
}> {}
@@ -88,7 +89,7 @@ const reviewer = async (context: Context, ev: CodeEvent) => {
msg: `Reviewer says: ${review}`,
}),
);
return new StopEvent({ result: code });
return new PackageEvent({ code });
}
return new ReviewEvent({ review, code });
@@ -98,7 +99,10 @@ export function createAgent(model: string): Workflow {
const codeAgent = new Workflow({ validate: true });
codeAgent.addStep(StartEvent, architect, { outputs: CodeEvent });
codeAgent.addStep(ReviewEvent, coder, { outputs: CodeEvent });
codeAgent.addStep(CodeEvent, reviewer, { outputs: ReviewEvent });
codeAgent.addStep(CodeEvent, reviewer, {
outputs: [ReviewEvent, PackageEvent],
});
codeAgent.addStep(PackageEvent, packager, { outputs: StopEvent });
// Update the llm model with the provided model
Settings.llm = new OpenAI({ model, temperature: 1 });
+33 -15
View File
@@ -1,6 +1,7 @@
import { createAgent, MessageEvent } from "./agent";
import * as fs from 'fs/promises';
import * as path from 'path';
import * as fs from "fs/promises";
import * as path from "path";
import { PackageResult } from "./packager";
const apps = [
{
@@ -9,22 +10,45 @@ const apps = [
database where they are mapped to answers. If there is a close match, it retrieves
the matched answer. If there isn't, it asks the user to provide an answer and
stores the question/answer pair in the database.`,
models: ["gpt-4o-mini", "gpt-4o", "o1-mini", "o1-preview"]
models: ["gpt-4o-mini", "gpt-4o", "o1-mini", "o1-preview"],
},
{
name: "JavaScript Todo App",
spec: `Create a full-stack NextJS todo app with a TailwindCSS frontend, that allows users to add,
remove, and mark tasks as complete. Use a Postgres database to persist the tasks.`,
models: ["gpt-4o-mini", "gpt-4o", "o1-mini"] // didn't complete with o1-preview
models: ["gpt-4o-mini", "gpt-4o", "o1-mini"], // didn't complete with o1-preview
},
{
name: "Iphone calculator",
spec: `Iphone style scientific calculator in one html file, using tailwind css and javascript.`,
models: ["gpt-4o-mini", "gpt-4o", "o1-mini", "o1-preview"]
}
models: ["gpt-4o-mini", "gpt-4o", "o1-mini", "o1-preview"],
},
// Add more specifications as needed
];
async function outputResult(
name: string,
model: string,
packageResult: PackageResult,
) {
// Create output directory if it doesn't exist
const outputDir = path.join("output", name, model);
await fs.mkdir(outputDir, { recursive: true });
// Iterate over all files in the packageResult and store each file with their correct path
for (const file of packageResult.files) {
const filePath = path.join(outputDir, file.path);
const fileDir = path.dirname(filePath);
// Create directory if it doesn't exist
await fs.mkdir(fileDir, { recursive: true });
// Write file content
await fs.writeFile(filePath, file.content);
console.log(`File written to: ${filePath}`);
}
}
async function runGeneration(name: string, spec: string, model: string) {
console.log(`Running generation with model: ${model}`);
const codeAgent = createAgent(model);
@@ -34,15 +58,9 @@ async function runGeneration(name: string, spec: string, model: string) {
console.log(`${msg}\n`);
}
const result = await run;
// Create output directory if it doesn't exist
const outputDir = path.join('output', name);
await fs.mkdir(outputDir, { recursive: true });
// Write the generated code to a file
const outputFile = path.join(outputDir, `${model}.code`);
await fs.writeFile(outputFile, result.data.result);
console.log(`Generated code written to: ${outputFile}\n`);
const packageResult = result.data.result as unknown as PackageResult;
await outputResult(name, model, packageResult);
}
async function main() {
+4 -2
View File
@@ -13,10 +13,12 @@
"license": "ISC",
"dependencies": {
"@llamaindex/core": "^0.2.0",
"llamaindex": "^0.6.0"
"llamaindex": "^0.6.0",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.23.3"
},
"devDependencies": {
"prettier": "^3.3.3",
"tsx": "^4.19.1"
}
}
}
+50
View File
@@ -0,0 +1,50 @@
import { zodToJsonSchema } from "zod-to-json-schema";
import { Context, StopEvent, WorkflowEvent } from "@llamaindex/core/workflow";
import { OpenAI } from "llamaindex";
import { z } from "zod";
export class PackageEvent extends WorkflowEvent<{
code: string;
}> {}
const FileSchema = z.object({
path: z.string().describe("Path to the filename, e.g., 'app/main.py'"),
content: z.string().describe("Complete content of the file"),
});
const PackageResultSchema = z.object({
files: z.array(FileSchema),
});
export type PackageResult = z.infer<typeof PackageResultSchema>;
export const packager = async (context: Context, ev: PackageEvent) => {
const { code } = ev.data;
// use own llm for extracting the files
const llm = new OpenAI({
model: "gpt-4o-mini",
additionalChatOptions: { response_format: { type: "json_object" } },
});
const schema = JSON.stringify(zodToJsonSchema(PackageResultSchema));
const response = await llm.chat({
messages: [
{
role: "system",
content: `You are an expert in extracting single files (path and content) from one large string.\n\nGenerate a valid JSON following the given schema below:\n\n${schema}`,
},
{
role: "user",
content: `Here is the large string: \n------\n${code}\n------`,
},
],
});
const json = response.message.content as string;
const result = PackageResultSchema.parse(JSON.parse(json));
// TODO: allow different types of outputs in LlamaIndexTS
return new StopEvent({ result: result as unknown as string });
};
+18
View File
@@ -13,6 +13,12 @@ importers:
llamaindex:
specifier: ^0.6.0
version: 0.6.0(@aws-sdk/client-sso-oidc@3.650.0(@aws-sdk/client-sts@3.650.0))(@aws-sdk/credential-providers@3.650.0(@aws-sdk/client-sso-oidc@3.650.0(@aws-sdk/client-sts@3.650.0)))(@notionhq/client@2.2.15(encoding@0.1.13))(encoding@0.1.13)(typescript@5.6.2)
zod:
specifier: ^3.23.8
version: 3.23.8
zod-to-json-schema:
specifier: ^3.23.3
version: 3.23.3(zod@3.23.8)
devDependencies:
prettier:
specifier: ^3.3.3
@@ -3738,6 +3744,14 @@ packages:
}
engines: { node: ">=12" }
zod-to-json-schema@3.23.3:
resolution:
{
integrity: sha512-TYWChTxKQbRJp5ST22o/Irt9KC5nj7CdBKYB/AosCRdj/wxEMvv4NNaj9XVUHDOIp53ZxArGhnw5HMZziPFjog==,
}
peerDependencies:
zod: ^3.23.3
zod@3.23.8:
resolution:
{
@@ -6532,4 +6546,8 @@ snapshots:
y18n: 5.0.8
yargs-parser: 21.1.1
zod-to-json-schema@3.23.3(zod@3.23.8):
dependencies:
zod: 3.23.8
zod@3.23.8: {}