Skip to content

Commit 04ddebc

Browse files
feat: Add publisher agent, merge code with streaming template (#324)
--------- Co-authored-by: Marcus Schiesser <[email protected]>
1 parent 3e8057a commit 04ddebc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1408
-884
lines changed

.changeset/flat-singers-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-llama": patch
3+
---
4+
5+
Add publisher agent to multi-agents for generating documents (PDF and HTML)

.changeset/gorgeous-penguins-shout.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-llama": patch
3+
---
4+
5+
Allow tool selection for multi-agents (Python and TS)

e2e/python/resolve_dependencies.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ if (
3333
const toolOptions = [
3434
"wikipedia.WikipediaToolSpec",
3535
"google.GoogleSearchToolSpec",
36+
"document_generator",
3637
];
3738

3839
const dataSources = [

e2e/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ export async function runCreateLlama({
109109
if (appType) {
110110
commandArgs.push(appType);
111111
}
112-
if (!useLlamaParse) {
112+
if (useLlamaParse) {
113+
commandArgs.push("--use-llama-parse");
114+
} else {
113115
commandArgs.push("--no-llama-parse");
114116
}
115117

helpers/env-variables.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -426,34 +426,35 @@ const getToolEnvs = (tools?: Tool[]): EnvVar[] => {
426426
const getSystemPromptEnv = (
427427
tools?: Tool[],
428428
dataSources?: TemplateDataSource[],
429-
framework?: TemplateFramework,
429+
template?: TemplateType,
430430
): EnvVar[] => {
431431
const defaultSystemPrompt =
432432
"You are a helpful assistant who helps users with their questions.";
433433

434+
const systemPromptEnv: EnvVar[] = [];
434435
// build tool system prompt by merging all tool system prompts
435-
let toolSystemPrompt = "";
436-
tools?.forEach((tool) => {
437-
const toolSystemPromptEnv = tool.envVars?.find(
438-
(env) => env.name === TOOL_SYSTEM_PROMPT_ENV_VAR,
439-
);
440-
if (toolSystemPromptEnv) {
441-
toolSystemPrompt += toolSystemPromptEnv.value + "\n";
442-
}
443-
});
436+
// multiagent template doesn't need system prompt
437+
if (template !== "multiagent") {
438+
let toolSystemPrompt = "";
439+
tools?.forEach((tool) => {
440+
const toolSystemPromptEnv = tool.envVars?.find(
441+
(env) => env.name === TOOL_SYSTEM_PROMPT_ENV_VAR,
442+
);
443+
if (toolSystemPromptEnv) {
444+
toolSystemPrompt += toolSystemPromptEnv.value + "\n";
445+
}
446+
});
444447

445-
const systemPrompt = toolSystemPrompt
446-
? `\"${toolSystemPrompt}\"`
447-
: defaultSystemPrompt;
448+
const systemPrompt = toolSystemPrompt
449+
? `\"${toolSystemPrompt}\"`
450+
: defaultSystemPrompt;
448451

449-
const systemPromptEnv = [
450-
{
452+
systemPromptEnv.push({
451453
name: "SYSTEM_PROMPT",
452454
description: "The system prompt for the AI model.",
453455
value: systemPrompt,
454-
},
455-
];
456-
456+
});
457+
}
457458
if (tools?.length == 0 && (dataSources?.length ?? 0 > 0)) {
458459
const citationPrompt = `'You have provided information from a knowledge base that has been passed to you in nodes of information.
459460
Each node has useful metadata such as node ID, file name, page, etc.
@@ -559,7 +560,7 @@ export const createBackendEnvFile = async (
559560
...getToolEnvs(opts.tools),
560561
...getTemplateEnvs(opts.template),
561562
...getObservabilityEnvs(opts.observability),
562-
...getSystemPromptEnv(opts.tools, opts.dataSources, opts.framework),
563+
...getSystemPromptEnv(opts.tools, opts.dataSources, opts.template),
563564
];
564565
// Render and write env file
565566
const content = renderEnvVar(envVars);

helpers/python.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,12 @@ export const installPythonTemplate = async ({
364364
| "modelConfig"
365365
>) => {
366366
console.log("\nInitializing Python project with template:", template, "\n");
367-
const templatePath = path.join(templatesDir, "types", template, framework);
367+
let templatePath;
368+
if (template === "extractor") {
369+
templatePath = path.join(templatesDir, "types", "extractor", framework);
370+
} else {
371+
templatePath = path.join(templatesDir, "types", "streaming", framework);
372+
}
368373
await copy("**", root, {
369374
parents: true,
370375
cwd: templatePath,
@@ -401,23 +406,42 @@ export const installPythonTemplate = async ({
401406
cwd: path.join(compPath, "services", "python"),
402407
});
403408
}
404-
405-
if (template === "streaming") {
406-
// For the streaming template only:
409+
// Copy engine code
410+
if (template === "streaming" || template === "multiagent") {
407411
// Select and copy engine code based on data sources and tools
408412
let engine;
409-
if (dataSources.length > 0 && (!tools || tools.length === 0)) {
410-
console.log("\nNo tools selected - use optimized context chat engine\n");
411-
engine = "chat";
412-
} else {
413+
// Multiagent always uses agent engine
414+
if (template === "multiagent") {
413415
engine = "agent";
416+
} else {
417+
// For streaming, use chat engine by default
418+
// Unless tools are selected, in which case use agent engine
419+
if (dataSources.length > 0 && (!tools || tools.length === 0)) {
420+
console.log(
421+
"\nNo tools selected - use optimized context chat engine\n",
422+
);
423+
engine = "chat";
424+
} else {
425+
engine = "agent";
426+
}
414427
}
428+
429+
// Copy engine code
415430
await copy("**", enginePath, {
416431
parents: true,
417432
cwd: path.join(compPath, "engines", "python", engine),
418433
});
419434
}
420435

436+
if (template === "multiagent") {
437+
// Copy multi-agent code
438+
await copy("**", path.join(root), {
439+
parents: true,
440+
cwd: path.join(compPath, "multiagent", "python"),
441+
rename: assetRelocator,
442+
});
443+
}
444+
421445
console.log("Adding additional dependencies");
422446

423447
const addOnDependencies = getAdditionalDependencies(

helpers/tools.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,29 @@ For better results, you can specify the region parameter to get results from a s
110110
},
111111
],
112112
},
113+
{
114+
display: "Document generator",
115+
name: "document_generator",
116+
supportedFrameworks: ["fastapi", "nextjs", "express"],
117+
dependencies: [
118+
{
119+
name: "xhtml2pdf",
120+
version: "^0.2.14",
121+
},
122+
{
123+
name: "markdown",
124+
version: "^3.7",
125+
},
126+
],
127+
type: ToolType.LOCAL,
128+
envVars: [
129+
{
130+
name: TOOL_SYSTEM_PROMPT_ENV_VAR,
131+
description: "System prompt for document generator tool.",
132+
value: `If user request for a report or a post, use document generator tool to create a file and reply with the link to the file.`,
133+
},
134+
],
135+
},
113136
{
114137
display: "Code Interpreter",
115138
name: "interpreter",

helpers/typescript.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,10 @@ export const installTSTemplate = async ({
157157
// Select and copy engine code based on data sources and tools
158158
let engine;
159159
tools = tools ?? [];
160-
if (dataSources.length > 0 && tools.length === 0) {
160+
// multiagent template always uses agent engine
161+
if (template === "multiagent") {
162+
engine = "agent";
163+
} else if (dataSources.length > 0 && tools.length === 0) {
161164
console.log("\nNo tools selected - use optimized context chat engine\n");
162165
engine = "chat";
163166
} else {

questions.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,10 @@ export const getDataSourceChoices = (
141141
});
142142
}
143143
if (selectedDataSource === undefined || selectedDataSource.length === 0) {
144-
if (template !== "multiagent") {
145-
choices.push({
146-
title: "No datasource",
147-
value: "none",
148-
});
149-
}
144+
choices.push({
145+
title: "No datasource",
146+
value: "none",
147+
});
150148
choices.push({
151149
title:
152150
process.platform !== "linux"
@@ -734,8 +732,10 @@ export const askQuestions = async (
734732
}
735733
}
736734

737-
if (!program.tools && program.template === "streaming") {
738-
// TODO: allow to select tools also for multi-agent framework
735+
if (
736+
!program.tools &&
737+
(program.template === "streaming" || program.template === "multiagent")
738+
) {
739739
if (ciInfo.isCI) {
740740
program.tools = getPrefOrDefault("tools");
741741
} else {

templates/components/engines/python/agent/engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from llama_index.core.tools.query_engine import QueryEngineTool
99

1010

11-
def get_chat_engine(filters=None, params=None, event_handlers=None):
11+
def get_chat_engine(filters=None, params=None, event_handlers=None, **kwargs):
1212
system_prompt = os.getenv("SYSTEM_PROMPT")
1313
top_k = int(os.getenv("TOP_K", 0))
1414
tools = []

templates/components/engines/python/agent/tools/__init__.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import importlib
12
import os
3+
24
import yaml
3-
import importlib
4-
from llama_index.core.tools.tool_spec.base import BaseToolSpec
55
from llama_index.core.tools.function_tool import FunctionTool
6+
from llama_index.core.tools.tool_spec.base import BaseToolSpec
67

78

89
class ToolType:
@@ -40,14 +41,26 @@ def load_tools(tool_type: str, tool_name: str, config: dict) -> list[FunctionToo
4041
raise ValueError(f"Failed to load tool {tool_name}: {e}")
4142

4243
@staticmethod
43-
def from_env() -> list[FunctionTool]:
44-
tools = []
44+
def from_env(
45+
map_result: bool = False,
46+
) -> list[FunctionTool] | dict[str, FunctionTool]:
47+
"""
48+
Load tools from the configured file.
49+
Params:
50+
- use_map: if True, return map of tool name and the tool itself
51+
"""
52+
if map_result:
53+
tools = {}
54+
else:
55+
tools = []
4556
if os.path.exists("config/tools.yaml"):
4657
with open("config/tools.yaml", "r") as f:
4758
tool_configs = yaml.safe_load(f)
4859
for tool_type, config_entries in tool_configs.items():
4960
for tool_name, config in config_entries.items():
50-
tools.extend(
51-
ToolFactory.load_tools(tool_type, tool_name, config)
52-
)
61+
tool = ToolFactory.load_tools(tool_type, tool_name, config)
62+
if map_result:
63+
tools[tool_name] = tool
64+
else:
65+
tools.extend(tool)
5366
return tools

0 commit comments

Comments
 (0)