Skip to content

Commit

Permalink
feat: Add llama-agent template (#150)
Browse files Browse the repository at this point in the history
---------
Co-authored-by: Marcus Schiesser <mail@marcusschiesser.de>
  • Loading branch information
leehuwuj authored Jul 3, 2024
1 parent 344d832 commit 9ecd061
Show file tree
Hide file tree
Showing 17 changed files with 494 additions and 52 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-horses-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-llama": patch
---

Add new template for a multi-agents app
34 changes: 34 additions & 0 deletions helpers/env-variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ModelConfig,
TemplateDataSource,
TemplateFramework,
TemplateType,
TemplateVectorDB,
} from "./types";

Expand Down Expand Up @@ -378,6 +379,36 @@ const getSystemPromptEnv = (tools?: Tool[]): EnvVar => {
};
};

const getTemplateEnvs = (template?: TemplateType): EnvVar[] => {
if (template === "multiagent") {
return [
{
name: "MESSAGE_QUEUE_PORT",
},
{
name: "CONTROL_PLANE_PORT",
},
{
name: "HUMAN_CONSUMER_PORT",
},
{
name: "AGENT_QUERY_ENGINE_PORT",
value: "8003",
},
{
name: "AGENT_QUERY_ENGINE_DESCRIPTION",
value: "Query information from the provided data",
},
{
name: "AGENT_DUMMY_PORT",
value: "8004",
},
];
} else {
return [];
}
};

export const createBackendEnvFile = async (
root: string,
opts: {
Expand All @@ -386,6 +417,7 @@ export const createBackendEnvFile = async (
modelConfig: ModelConfig;
framework: TemplateFramework;
dataSources?: TemplateDataSource[];
template?: TemplateType;
port?: number;
tools?: Tool[];
},
Expand All @@ -406,6 +438,8 @@ export const createBackendEnvFile = async (
...getVectorDBEnvs(opts.vectorDb, opts.framework),
...getFrameworkEnvs(opts.framework, opts.port),
...getToolEnvs(opts.tools),
// Add template environment variables
...getTemplateEnvs(opts.template),
getSystemPromptEnv(opts.tools),
];
// Render and write env file
Expand Down
21 changes: 12 additions & 9 deletions helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,18 @@ export const installTemplate = async (
// This is a backend, so we need to copy the test data and create the env file.

// Copy the environment file to the target directory.
await createBackendEnvFile(props.root, {
modelConfig: props.modelConfig,
llamaCloudKey: props.llamaCloudKey,
vectorDb: props.vectorDb,
framework: props.framework,
dataSources: props.dataSources,
port: props.externalPort,
tools: props.tools,
});
if (props.template === "streaming" || props.template === "multiagent") {
await createBackendEnvFile(props.root, {
modelConfig: props.modelConfig,
llamaCloudKey: props.llamaCloudKey,
vectorDb: props.vectorDb,
framework: props.framework,
dataSources: props.dataSources,
port: props.externalPort,
tools: props.tools,
template: props.template,
});
}

if (props.dataSources.length > 0) {
console.log("\nGenerating context data...\n");
Expand Down
31 changes: 19 additions & 12 deletions helpers/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,20 +320,27 @@ export const installPythonTemplate = async ({
cwd: path.join(compPath, "loaders", "python"),
});

// Select and copy engine code based on data sources and tools
let engine;
tools = tools ?? [];
if (dataSources.length > 0 && tools.length === 0) {
console.log("\nNo tools selected - use optimized context chat engine\n");
engine = "chat";
} else {
engine = "agent";
}
await copy("**", enginePath, {
parents: true,
cwd: path.join(compPath, "engines", "python", engine),
// Copy settings.py to app
await copy("**", path.join(root, "app"), {
cwd: path.join(compPath, "settings", "python"),
});

if (template === "streaming") {
// For the streaming template only:
// Select and copy engine code based on data sources and tools
let engine;
if (dataSources.length > 0 && (!tools || tools.length === 0)) {
console.log("\nNo tools selected - use optimized context chat engine\n");
engine = "chat";
} else {
engine = "agent";
}
await copy("**", enginePath, {
parents: true,
cwd: path.join(compPath, "engines", "python", engine),
});
}

console.log("Adding additional dependencies");

const addOnDependencies = getAdditionalDependencies(
Expand Down
6 changes: 5 additions & 1 deletion helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ export type ModelConfig = {
dimensions: number;
isConfigured(): boolean;
};
export type TemplateType = "streaming" | "community" | "llamapack";
export type TemplateType =
| "streaming"
| "community"
| "llamapack"
| "multiagent";
export type TemplateFramework = "nextjs" | "express" | "fastapi";
export type TemplateUI = "html" | "shadcn";
export type TemplateVectorDB =
Expand Down
73 changes: 46 additions & 27 deletions questions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
TemplateDataSource,
TemplateDataSourceType,
TemplateFramework,
TemplateType,
} from "./helpers";
import { COMMUNITY_OWNER, COMMUNITY_REPO } from "./helpers/constant";
import { EXAMPLE_FILE } from "./helpers/datasources";
Expand Down Expand Up @@ -122,6 +123,7 @@ const getVectorDbChoices = (framework: TemplateFramework) => {
export const getDataSourceChoices = (
framework: TemplateFramework,
selectedDataSource: TemplateDataSource[],
template?: TemplateType,
) => {
// If LlamaCloud is already selected, don't show any other options
if (selectedDataSource.find((s) => s.type === "llamacloud")) {
Expand All @@ -137,10 +139,12 @@ export const getDataSourceChoices = (
});
}
if (selectedDataSource === undefined || selectedDataSource.length === 0) {
choices.push({
title: "No data, just a simple chat or agent",
value: "none",
});
if (template !== "multiagent") {
choices.push({
title: "No data, just a simple chat or agent",
value: "none",
});
}
choices.push({
title:
process.platform !== "linux"
Expand Down Expand Up @@ -281,25 +285,27 @@ export const askQuestions = async (
},
];

const modelConfigured =
!program.llamapack && program.modelConfig.isConfigured();
// If using LlamaParse, require LlamaCloud API key
const llamaCloudKeyConfigured = program.useLlamaParse
? program.llamaCloudKey || process.env["LLAMA_CLOUD_API_KEY"]
: true;
const hasVectorDb = program.vectorDb && program.vectorDb !== "none";
// Can run the app if all tools do not require configuration
if (
!hasVectorDb &&
modelConfigured &&
llamaCloudKeyConfigured &&
!toolsRequireConfig(program.tools)
) {
actionChoices.push({
title:
"Generate code, install dependencies, and run the app (~2 min)",
value: "runApp",
});
if (program.template !== "multiagent") {
const modelConfigured =
!program.llamapack && program.modelConfig.isConfigured();
// If using LlamaParse, require LlamaCloud API key
const llamaCloudKeyConfigured = program.useLlamaParse
? program.llamaCloudKey || process.env["LLAMA_CLOUD_API_KEY"]
: true;
const hasVectorDb = program.vectorDb && program.vectorDb !== "none";
// Can run the app if all tools do not require configuration
if (
!hasVectorDb &&
modelConfigured &&
llamaCloudKeyConfigured &&
!toolsRequireConfig(program.tools)
) {
actionChoices.push({
title:
"Generate code, install dependencies, and run the app (~2 min)",
value: "runApp",
});
}
}

const { action } = await prompts(
Expand Down Expand Up @@ -331,7 +337,11 @@ export const askQuestions = async (
name: "template",
message: "Which template would you like to use?",
choices: [
{ title: "Chat", value: "streaming" },
{ title: "Agentic RAG (single agent)", value: "streaming" },
{
title: "Multi-agent app (using llama-agents)",
value: "multiagent",
},
{
title: `Community template from ${styledRepo}`,
value: "community",
Expand Down Expand Up @@ -395,6 +405,10 @@ export const askQuestions = async (
return; // early return - no further questions needed for llamapack projects
}

if (program.template === "multiagent") {
// TODO: multi-agents currently only supports FastAPI
program.framework = preferences.framework = "fastapi";
}
if (!program.framework) {
if (ciInfo.isCI) {
program.framework = getPrefOrDefault("framework");
Expand All @@ -420,7 +434,10 @@ export const askQuestions = async (
}
}

if (program.framework === "express" || program.framework === "fastapi") {
if (
(program.framework === "express" || program.framework === "fastapi") &&
program.template === "streaming"
) {
// if a backend-only framework is selected, ask whether we should create a frontend
if (program.frontend === undefined) {
if (ciInfo.isCI) {
Expand Down Expand Up @@ -457,7 +474,7 @@ export const askQuestions = async (
}
}

if (!program.observability) {
if (!program.observability && program.template === "streaming") {
if (ciInfo.isCI) {
program.observability = getPrefOrDefault("observability");
} else {
Expand Down Expand Up @@ -501,6 +518,7 @@ export const askQuestions = async (
const choices = getDataSourceChoices(
program.framework,
program.dataSources,
program.template,
);
if (choices.length === 0) break;
const { selectedSource } = await prompts(
Expand Down Expand Up @@ -695,7 +713,8 @@ export const askQuestions = async (
}
}

if (!program.tools) {
if (!program.tools && program.template === "streaming") {
// TODO: allow to select tools also for multi-agent framework
if (ciInfo.isCI) {
program.tools = getPrefOrDefault("tools");
} else {
Expand Down
61 changes: 61 additions & 0 deletions templates/components/settings/python/llmhub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core.settings import Settings
from typing import Dict
import os

DEFAULT_MODEL = "gpt-3.5-turbo"
DEFAULT_EMBEDDING_MODEL = "text-embedding-3-large"

class TSIEmbedding(OpenAIEmbedding):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._query_engine = self._text_engine = self.model_name

def llm_config_from_env() -> Dict:
from llama_index.core.constants import DEFAULT_TEMPERATURE

model = os.getenv("MODEL", DEFAULT_MODEL)
temperature = os.getenv("LLM_TEMPERATURE", DEFAULT_TEMPERATURE)
max_tokens = os.getenv("LLM_MAX_TOKENS")
api_key = os.getenv("T_SYSTEMS_LLMHUB_API_KEY")
api_base = os.getenv("T_SYSTEMS_LLMHUB_BASE_URL")

config = {
"model": model,
"api_key": api_key,
"api_base": api_base,
"temperature": float(temperature),
"max_tokens": int(max_tokens) if max_tokens is not None else None,
}
return config


def embedding_config_from_env() -> Dict:
from llama_index.core.constants import DEFAULT_EMBEDDING_DIM

model = os.getenv("EMBEDDING_MODEL", DEFAULT_EMBEDDING_MODEL)
dimension = os.getenv("EMBEDDING_DIM", DEFAULT_EMBEDDING_DIM)
api_key = os.getenv("T_SYSTEMS_LLMHUB_API_KEY")
api_base = os.getenv("T_SYSTEMS_LLMHUB_BASE_URL")

config = {
"model_name": model,
"dimension": int(dimension) if dimension is not None else None,
"api_key": api_key,
"api_base": api_base,
}
return config

def init_llmhub():
from llama_index.llms.openai_like import OpenAILike

llm_configs = llm_config_from_env()
embedding_configs = embedding_config_from_env()

Settings.embed_model = TSIEmbedding(**embedding_configs)
Settings.llm = OpenAILike(
**llm_configs,
is_chat_model=True,
is_function_calling_model=False,
context_window=4096,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Dict

from llama_index.core.settings import Settings
from .llmhub import init_llmhub


def init_settings():
model_provider = os.getenv("MODEL_PROVIDER")
Expand All @@ -20,6 +20,8 @@ def init_settings():
case "azure-openai":
init_azure_openai()
case "t-systems":
from .llmhub import init_llmhub

init_llmhub()
case _:
raise ValueError(f"Invalid model provider: {model_provider}")
Expand Down Expand Up @@ -147,5 +149,3 @@ def init_gemini():

Settings.llm = Gemini(model=model_name)
Settings.embed_model = GeminiEmbedding(model_name=embed_model_name)


Loading

0 comments on commit 9ecd061

Please sign in to comment.