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

feat: Add llama-agent template #150

Merged
merged 13 commits into from
Jul 3, 2024
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 [];
}
marcusschiesser marked this conversation as resolved.
Show resolved Hide resolved
};

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),
});
marcusschiesser marked this conversation as resolved.
Show resolved Hide resolved
}

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
marcusschiesser marked this conversation as resolved.
Show resolved Hide resolved
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
marcusschiesser marked this conversation as resolved.
Show resolved Hide resolved
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
Loading