Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Update multi-agent template to use financial report use case #386

Merged
merged 14 commits into from
Oct 23, 2024
5 changes: 5 additions & 0 deletions .changeset/spicy-apricots-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-llama": patch
---

Add financial report as the default use case in the multi-agent template (Python).
2 changes: 2 additions & 0 deletions create-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export async function createApp({
tools,
useLlamaParse,
observability,
agents,
}: InstallAppArgs): Promise<void> {
const root = path.resolve(appPath);

Expand Down Expand Up @@ -86,6 +87,7 @@ export async function createApp({
tools,
useLlamaParse,
observability,
agents,
};

if (frontend) {
Expand Down
112 changes: 58 additions & 54 deletions e2e/shared/multiagent_template.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,68 +18,72 @@ const templateUI: TemplateUI = "shadcn";
const templatePostInstallAction: TemplatePostInstallAction = "runApp";
const appType: AppType = templateFramework === "nextjs" ? "" : "--frontend";
const userMessage = "Write a blog post about physical standards for letters";
const templateAgents = ["financial_report", "blog"];

test.describe(`Test multiagent template ${templateFramework} ${dataSource} ${templateUI} ${appType} ${templatePostInstallAction}`, async () => {
test.skip(
process.platform !== "linux" || process.env.DATASOURCE === "--no-files",
"The multiagent template currently only works with files. We also only run on Linux to speed up tests.",
);
let port: number;
let externalPort: number;
let cwd: string;
let name: string;
let appProcess: ChildProcess;
// Only test without using vector db for now
const vectorDb = "none";
for (const agents of templateAgents) {
test.describe(`Test multiagent template ${agents} ${templateFramework} ${dataSource} ${templateUI} ${appType} ${templatePostInstallAction}`, async () => {
test.skip(
process.platform !== "linux" || process.env.DATASOURCE === "--no-files",
"The multiagent template currently only works with files. We also only run on Linux to speed up tests.",
);
let port: number;
let externalPort: number;
let cwd: string;
let name: string;
let appProcess: ChildProcess;
// Only test without using vector db for now
const vectorDb = "none";

test.beforeAll(async () => {
port = Math.floor(Math.random() * 10000) + 10000;
externalPort = port + 1;
cwd = await createTestDir();
const result = await runCreateLlama({
cwd,
templateType: "multiagent",
templateFramework,
dataSource,
vectorDb,
port,
externalPort,
postInstallAction: templatePostInstallAction,
templateUI,
appType,
test.beforeAll(async () => {
port = Math.floor(Math.random() * 10000) + 10000;
externalPort = port + 1;
cwd = await createTestDir();
const result = await runCreateLlama({
cwd,
templateType: "multiagent",
templateFramework,
dataSource,
vectorDb,
port,
externalPort,
postInstallAction: templatePostInstallAction,
templateUI,
appType,
agents,
});
name = result.projectName;
appProcess = result.appProcess;
});
name = result.projectName;
appProcess = result.appProcess;
});

test("App folder should exist", async () => {
const dirExists = fs.existsSync(path.join(cwd, name));
expect(dirExists).toBeTruthy();
});
test("App folder should exist", async () => {
const dirExists = fs.existsSync(path.join(cwd, name));
expect(dirExists).toBeTruthy();
});

test("Frontend should have a title", async ({ page }) => {
await page.goto(`http://localhost:${port}`);
await expect(page.getByText("Built by LlamaIndex")).toBeVisible();
});
test("Frontend should have a title", async ({ page }) => {
await page.goto(`http://localhost:${port}`);
await expect(page.getByText("Built by LlamaIndex")).toBeVisible();
});

test("Frontend should be able to submit a message and receive the start of a streamed response", async ({
page,
}) => {
await page.goto(`http://localhost:${port}`);
await page.fill("form textarea", userMessage);
test("Frontend should be able to submit a message and receive the start of a streamed response", async ({
page,
}) => {
await page.goto(`http://localhost:${port}`);
await page.fill("form textarea", userMessage);

const responsePromise = page.waitForResponse((res) =>
res.url().includes("/api/chat"),
);
const responsePromise = page.waitForResponse((res) =>
res.url().includes("/api/chat"),
);

await page.click("form button[type=submit]");
await page.click("form button[type=submit]");

const response = await responsePromise;
expect(response.ok()).toBeTruthy();
});
const response = await responsePromise;
expect(response.ok()).toBeTruthy();
});

// clean processes
test.afterAll(async () => {
appProcess?.kill();
// clean processes
test.afterAll(async () => {
appProcess?.kill();
});
});
});
}
5 changes: 5 additions & 0 deletions e2e/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type RunCreateLlamaOptions = {
tools?: string;
useLlamaParse?: boolean;
observability?: string;
agents?: string;
};

export async function runCreateLlama({
Expand All @@ -52,6 +53,7 @@ export async function runCreateLlama({
tools,
useLlamaParse,
observability,
agents,
}: RunCreateLlamaOptions): Promise<CreateLlamaResult> {
if (!process.env.OPENAI_API_KEY || !process.env.LLAMA_CLOUD_API_KEY) {
throw new Error(
Expand Down Expand Up @@ -119,6 +121,9 @@ export async function runCreateLlama({
if (observability) {
commandArgs.push("--observability", observability);
}
if (templateType === "multiagent" && agents) {
commandArgs.push("--agents", agents);
}

const command = commandArgs.join(" ");
console.log(`running command '${command}' in ${cwd}`);
Expand Down
19 changes: 19 additions & 0 deletions helpers/datasources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ export const EXAMPLE_FILE: TemplateDataSource = {
},
};

export const EXAMPLE_10K_SEC_FILES: TemplateDataSource[] = [
{
type: "file",
config: {
url: new URL(
"https://s2.q4cdn.com/470004039/files/doc_earnings/2023/q4/filing/_10-K-Q4-2023-As-Filed.pdf",
),
},
},
{
type: "file",
config: {
url: new URL(
"https://ir.tesla.com/_flysystem/s3/sec/000162828024002390/tsla-20231231-gen.pdf",
),
},
},
];

export function getDataSources(
files?: string,
exampleFile?: boolean,
Expand Down
34 changes: 28 additions & 6 deletions helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,41 @@ async function generateContextData(
}
}

const downloadFile = async (url: string, destPath: string) => {
const response = await fetch(url);
const fileBuffer = await response.arrayBuffer();
await fsExtra.writeFile(destPath, Buffer.from(fileBuffer));
};

const prepareContextData = async (
root: string,
dataSources: TemplateDataSource[],
) => {
await makeDir(path.join(root, "data"));
for (const dataSource of dataSources) {
const dataSourceConfig = dataSource?.config as FileSourceConfig;
// Copy local data
const dataPath = dataSourceConfig.path;

const destPath = path.join(root, "data", path.basename(dataPath));
console.log("Copying data from path:", dataPath);
await fsExtra.copy(dataPath, destPath);
// If the path is URLs, download the data and save it to the data directory
if ("url" in dataSourceConfig) {
console.log(
"Downloading file from URL:",
dataSourceConfig.url.toString(),
);
const destPath = path.join(
root,
"data",
path.basename(dataSourceConfig.url.toString()),
);
await downloadFile(dataSourceConfig.url.toString(), destPath);
} else {
// Copy local data
console.log("Copying data from path:", dataSourceConfig.path);
const destPath = path.join(
root,
"data",
path.basename(dataSourceConfig.path),
);
await fsExtra.copy(dataSourceConfig.path, destPath);
}
}
};

Expand Down
20 changes: 20 additions & 0 deletions helpers/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ export const installPythonTemplate = async ({
postInstallAction,
observability,
modelConfig,
agents,
}: Pick<
InstallTemplateArgs,
| "root"
Expand All @@ -373,6 +374,7 @@ export const installPythonTemplate = async ({
| "postInstallAction"
| "observability"
| "modelConfig"
| "agents"
>) => {
console.log("\nInitializing Python project with template:", template, "\n");
let templatePath;
Expand Down Expand Up @@ -443,6 +445,24 @@ export const installPythonTemplate = async ({
cwd: path.join(compPath, "engines", "python", engine),
});

// Copy agent code
if (template === "multiagent") {
if (agents) {
await copy("**", path.join(root), {
parents: true,
cwd: path.join(compPath, "agents", "python", agents),
rename: assetRelocator,
});
} else {
console.log(
red(
"There is no agent selected for multi-agent template. Please pick an agent to use via --agents flag.",
),
);
process.exit(1);
}
}

// Copy router code
await copyRouterCode(root, tools ?? []);
}
Expand Down
12 changes: 9 additions & 3 deletions helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,15 @@ export type TemplateDataSource = {
};
export type TemplateDataSourceType = "file" | "web" | "db";
export type TemplateObservability = "none" | "traceloop" | "llamatrace";
export type TemplateAgents = "financial_report" | "blog";
// Config for both file and folder
export type FileSourceConfig = {
path: string;
};
export type FileSourceConfig =
| {
path: string;
}
| {
url: URL;
};
export type WebSourceConfig = {
baseUrl?: string;
prefix?: string;
Expand Down Expand Up @@ -94,4 +99,5 @@ export interface InstallTemplateArgs {
postInstallAction?: TemplatePostInstallAction;
tools?: Tool[];
observability?: TemplateObservability;
agents?: TemplateAgents;
}
7 changes: 7 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ const program = new Command(packageJson.name)
`,
false,
)
.option(
"--agents <agents>",
`

Select which agents to use for the multi-agent template (e.g: financial_report, blog).
`,
)
.allowUnknownOption()
.parse(process.argv);

Expand Down
Loading
Loading