Skip to content

Commit 2209409

Browse files
leehuwujcoderabbitai[bot]marcusschiesser
authored
Feature: Update multi-agent template to use financial report use case (#386)
--------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Marcus Schiesser <[email protected]>
1 parent 623f8b8 commit 2209409

27 files changed

+643
-115
lines changed

.changeset/spicy-apricots-double.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-llama": patch
3+
---
4+
5+
Add financial report as the default use case in the multi-agent template (Python).

create-app.ts

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export async function createApp({
4141
tools,
4242
useLlamaParse,
4343
observability,
44+
agents,
4445
}: InstallAppArgs): Promise<void> {
4546
const root = path.resolve(appPath);
4647

@@ -86,6 +87,7 @@ export async function createApp({
8687
tools,
8788
useLlamaParse,
8889
observability,
90+
agents,
8991
};
9092

9193
if (frontend) {

e2e/shared/multiagent_template.spec.ts

+58-54
Original file line numberDiff line numberDiff line change
@@ -18,68 +18,72 @@ const templateUI: TemplateUI = "shadcn";
1818
const templatePostInstallAction: TemplatePostInstallAction = "runApp";
1919
const appType: AppType = templateFramework === "nextjs" ? "" : "--frontend";
2020
const userMessage = "Write a blog post about physical standards for letters";
21+
const templateAgents = ["financial_report", "blog"];
2122

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

35-
test.beforeAll(async () => {
36-
port = Math.floor(Math.random() * 10000) + 10000;
37-
externalPort = port + 1;
38-
cwd = await createTestDir();
39-
const result = await runCreateLlama({
40-
cwd,
41-
templateType: "multiagent",
42-
templateFramework,
43-
dataSource,
44-
vectorDb,
45-
port,
46-
externalPort,
47-
postInstallAction: templatePostInstallAction,
48-
templateUI,
49-
appType,
37+
test.beforeAll(async () => {
38+
port = Math.floor(Math.random() * 10000) + 10000;
39+
externalPort = port + 1;
40+
cwd = await createTestDir();
41+
const result = await runCreateLlama({
42+
cwd,
43+
templateType: "multiagent",
44+
templateFramework,
45+
dataSource,
46+
vectorDb,
47+
port,
48+
externalPort,
49+
postInstallAction: templatePostInstallAction,
50+
templateUI,
51+
appType,
52+
agents,
53+
});
54+
name = result.projectName;
55+
appProcess = result.appProcess;
5056
});
51-
name = result.projectName;
52-
appProcess = result.appProcess;
53-
});
5457

55-
test("App folder should exist", async () => {
56-
const dirExists = fs.existsSync(path.join(cwd, name));
57-
expect(dirExists).toBeTruthy();
58-
});
58+
test("App folder should exist", async () => {
59+
const dirExists = fs.existsSync(path.join(cwd, name));
60+
expect(dirExists).toBeTruthy();
61+
});
5962

60-
test("Frontend should have a title", async ({ page }) => {
61-
await page.goto(`http://localhost:${port}`);
62-
await expect(page.getByText("Built by LlamaIndex")).toBeVisible();
63-
});
63+
test("Frontend should have a title", async ({ page }) => {
64+
await page.goto(`http://localhost:${port}`);
65+
await expect(page.getByText("Built by LlamaIndex")).toBeVisible();
66+
});
6467

65-
test("Frontend should be able to submit a message and receive the start of a streamed response", async ({
66-
page,
67-
}) => {
68-
await page.goto(`http://localhost:${port}`);
69-
await page.fill("form textarea", userMessage);
68+
test("Frontend should be able to submit a message and receive the start of a streamed response", async ({
69+
page,
70+
}) => {
71+
await page.goto(`http://localhost:${port}`);
72+
await page.fill("form textarea", userMessage);
7073

71-
const responsePromise = page.waitForResponse((res) =>
72-
res.url().includes("/api/chat"),
73-
);
74+
const responsePromise = page.waitForResponse((res) =>
75+
res.url().includes("/api/chat"),
76+
);
7477

75-
await page.click("form button[type=submit]");
78+
await page.click("form button[type=submit]");
7679

77-
const response = await responsePromise;
78-
expect(response.ok()).toBeTruthy();
79-
});
80+
const response = await responsePromise;
81+
expect(response.ok()).toBeTruthy();
82+
});
8083

81-
// clean processes
82-
test.afterAll(async () => {
83-
appProcess?.kill();
84+
// clean processes
85+
test.afterAll(async () => {
86+
appProcess?.kill();
87+
});
8488
});
85-
});
89+
}

e2e/utils.ts

+5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export type RunCreateLlamaOptions = {
3434
tools?: string;
3535
useLlamaParse?: boolean;
3636
observability?: string;
37+
agents?: string;
3738
};
3839

3940
export async function runCreateLlama({
@@ -52,6 +53,7 @@ export async function runCreateLlama({
5253
tools,
5354
useLlamaParse,
5455
observability,
56+
agents,
5557
}: RunCreateLlamaOptions): Promise<CreateLlamaResult> {
5658
if (!process.env.OPENAI_API_KEY || !process.env.LLAMA_CLOUD_API_KEY) {
5759
throw new Error(
@@ -119,6 +121,9 @@ export async function runCreateLlama({
119121
if (observability) {
120122
commandArgs.push("--observability", observability);
121123
}
124+
if (templateType === "multiagent" && agents) {
125+
commandArgs.push("--agents", agents);
126+
}
122127

123128
const command = commandArgs.join(" ");
124129
console.log(`running command '${command}' in ${cwd}`);

helpers/datasources.ts

+19
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@ export const EXAMPLE_FILE: TemplateDataSource = {
1111
},
1212
};
1313

14+
export const EXAMPLE_10K_SEC_FILES: TemplateDataSource[] = [
15+
{
16+
type: "file",
17+
config: {
18+
url: new URL(
19+
"https://s2.q4cdn.com/470004039/files/doc_earnings/2023/q4/filing/_10-K-Q4-2023-As-Filed.pdf",
20+
),
21+
},
22+
},
23+
{
24+
type: "file",
25+
config: {
26+
url: new URL(
27+
"https://ir.tesla.com/_flysystem/s3/sec/000162828024002390/tsla-20231231-gen.pdf",
28+
),
29+
},
30+
},
31+
];
32+
1433
export function getDataSources(
1534
files?: string,
1635
exampleFile?: boolean,

helpers/index.ts

+28-6
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,41 @@ async function generateContextData(
9696
}
9797
}
9898

99+
const downloadFile = async (url: string, destPath: string) => {
100+
const response = await fetch(url);
101+
const fileBuffer = await response.arrayBuffer();
102+
await fsExtra.writeFile(destPath, Buffer.from(fileBuffer));
103+
};
104+
99105
const prepareContextData = async (
100106
root: string,
101107
dataSources: TemplateDataSource[],
102108
) => {
103109
await makeDir(path.join(root, "data"));
104110
for (const dataSource of dataSources) {
105111
const dataSourceConfig = dataSource?.config as FileSourceConfig;
106-
// Copy local data
107-
const dataPath = dataSourceConfig.path;
108-
109-
const destPath = path.join(root, "data", path.basename(dataPath));
110-
console.log("Copying data from path:", dataPath);
111-
await fsExtra.copy(dataPath, destPath);
112+
// If the path is URLs, download the data and save it to the data directory
113+
if ("url" in dataSourceConfig) {
114+
console.log(
115+
"Downloading file from URL:",
116+
dataSourceConfig.url.toString(),
117+
);
118+
const destPath = path.join(
119+
root,
120+
"data",
121+
path.basename(dataSourceConfig.url.toString()),
122+
);
123+
await downloadFile(dataSourceConfig.url.toString(), destPath);
124+
} else {
125+
// Copy local data
126+
console.log("Copying data from path:", dataSourceConfig.path);
127+
const destPath = path.join(
128+
root,
129+
"data",
130+
path.basename(dataSourceConfig.path),
131+
);
132+
await fsExtra.copy(dataSourceConfig.path, destPath);
133+
}
112134
}
113135
};
114136

helpers/python.ts

+20
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ export const installPythonTemplate = async ({
362362
postInstallAction,
363363
observability,
364364
modelConfig,
365+
agents,
365366
}: Pick<
366367
InstallTemplateArgs,
367368
| "root"
@@ -373,6 +374,7 @@ export const installPythonTemplate = async ({
373374
| "postInstallAction"
374375
| "observability"
375376
| "modelConfig"
377+
| "agents"
376378
>) => {
377379
console.log("\nInitializing Python project with template:", template, "\n");
378380
let templatePath;
@@ -443,6 +445,24 @@ export const installPythonTemplate = async ({
443445
cwd: path.join(compPath, "engines", "python", engine),
444446
});
445447

448+
// Copy agent code
449+
if (template === "multiagent") {
450+
if (agents) {
451+
await copy("**", path.join(root), {
452+
parents: true,
453+
cwd: path.join(compPath, "agents", "python", agents),
454+
rename: assetRelocator,
455+
});
456+
} else {
457+
console.log(
458+
red(
459+
"There is no agent selected for multi-agent template. Please pick an agent to use via --agents flag.",
460+
),
461+
);
462+
process.exit(1);
463+
}
464+
}
465+
446466
// Copy router code
447467
await copyRouterCode(root, tools ?? []);
448468
}

helpers/types.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,15 @@ export type TemplateDataSource = {
4848
};
4949
export type TemplateDataSourceType = "file" | "web" | "db";
5050
export type TemplateObservability = "none" | "traceloop" | "llamatrace";
51+
export type TemplateAgents = "financial_report" | "blog";
5152
// Config for both file and folder
52-
export type FileSourceConfig = {
53-
path: string;
54-
};
53+
export type FileSourceConfig =
54+
| {
55+
path: string;
56+
}
57+
| {
58+
url: URL;
59+
};
5560
export type WebSourceConfig = {
5661
baseUrl?: string;
5762
prefix?: string;
@@ -94,4 +99,5 @@ export interface InstallTemplateArgs {
9499
postInstallAction?: TemplatePostInstallAction;
95100
tools?: Tool[];
96101
observability?: TemplateObservability;
102+
agents?: TemplateAgents;
97103
}

index.ts

+7
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ const program = new Command(packageJson.name)
208208
`,
209209
false,
210210
)
211+
.option(
212+
"--agents <agents>",
213+
`
214+
215+
Select which agents to use for the multi-agent template (e.g: financial_report, blog).
216+
`,
217+
)
211218
.allowUnknownOption()
212219
.parse(process.argv);
213220

0 commit comments

Comments
 (0)