Compare commits

..

8 Commits

Author SHA1 Message Date
Marcus Schiesser 4589a84643 RELEASING: Releasing 1 package(s)
Releases:
  create-llama@0.0.28

[skip ci]
2024-03-12 13:41:36 +07:00
Huu Le (Lee) e6b7f52d3e fix: add missing check env logic (#636) 2024-03-12 12:29:00 +07:00
Marcus Schiesser b169db617a refactor: use a function for webpack config (#634) 2024-03-12 11:10:31 +07:00
Huu Le (Lee) 89a49f4f4f feat: Add more. env variables to config host, port, llm and embedding (#630) 2024-03-12 09:22:21 +07:00
Marcus Schiesser 58490715fe refactor: clean nextjs config generation (use JSON) (#631) 2024-03-11 14:16:15 +07:00
Huu Le (Lee) 4c2283c4e5 fix: Rename folder e2e/.cache to e2e/cache (#632) 2024-03-11 14:15:13 +07:00
Eka Prasetia a059070dec docs: Fix typo in transformations.md (#625) 2024-03-11 12:16:23 +07:00
Emanuel Ferreira 20dfeb4cfa chore: remove comment (#624) 2024-03-08 15:54:24 -03:00
25 changed files with 397 additions and 179 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"create-llama": patch
---
Add "Start in VSCode" option to postInstallAction
-5
View File
@@ -1,5 +0,0 @@
---
"create-llama": patch
---
Add devcontainers to generated code
+1
View File
@@ -45,6 +45,7 @@ playwright-report/
blob-report/
playwright/.cache/
.tsbuildinfo
packages/create-llama/e2e/cache
# intellij
**/.idea
+1
View File
@@ -4,3 +4,4 @@ pnpm-lock.yaml
lib/
dist/
.docusaurus/
packages/create-llama/e2e/cache/
@@ -1,6 +1,6 @@
# Transformations
A transformation is something that takes a list of nodes as an input, and returns a list of nodes. Each component that implements the Transformatio class has both a `transform` definition responsible for transforming the nodes
A transformation is something that takes a list of nodes as an input, and returns a list of nodes. Each component that implements the Transformation class has both a `transform` definition responsible for transforming the nodes.
Currently, the following components are Transformation objects:
+8
View File
@@ -1,5 +1,13 @@
# create-llama
## 0.0.28
### Patch Changes
- 89a49f4: Add more config variables to .env file
- fdf48dd: Add "Start in VSCode" option to postInstallAction
- fdf48dd: Add devcontainers to generated code
## 0.0.27
### Patch Changes
+1 -1
View File
@@ -113,7 +113,7 @@ export async function createApp({
path.join(root, "README.md"),
);
} else {
await installTemplate({ ...args, backend: true, forBackend: framework });
await installTemplate({ ...args, backend: true });
}
process.chdir(root);
+1 -1
View File
@@ -175,7 +175,7 @@ export async function runCreateLlama(
}
export async function createTestDir() {
const cwd = path.join(__dirname, ".cache", crypto.randomUUID());
const cwd = path.join(__dirname, "cache", crypto.randomUUID());
await mkdir(cwd, { recursive: true });
return cwd;
}
@@ -0,0 +1,241 @@
import fs from "fs/promises";
import path from "path";
import {
FileSourceConfig,
TemplateDataSource,
TemplateFramework,
TemplateVectorDB,
} from "./types";
type EnvVar = {
name?: string;
description?: string;
value?: string;
};
const renderEnvVar = (envVars: EnvVar[]): string => {
return envVars.reduce(
(prev, env) =>
prev +
(env.description
? `# ${env.description.replaceAll("\n", "\n# ")}\n`
: "") +
(env.name
? env.value
? `${env.name}=${env.value}\n\n`
: `# ${env.name}=\n\n`
: ""),
"",
);
};
const getVectorDBEnvs = (vectorDb: TemplateVectorDB) => {
switch (vectorDb) {
case "mongo":
return [
{
name: "MONGO_URI",
description:
"For generating a connection URI, see https://docs.timescale.com/use-timescale/latest/services/create-a-service\nThe MongoDB connection URI.",
},
{
name: "MONGODB_DATABASE",
},
{
name: "MONGODB_VECTORS",
},
{
name: "MONGODB_VECTOR_INDEX",
},
];
case "pg":
return [
{
name: "PG_CONNECTION_STRING",
description:
"For generating a connection URI, see https://docs.timescale.com/use-timescale/latest/services/create-a-service\nThe PostgreSQL connection string.",
},
];
case "pinecone":
return [
{
name: "PINECONE_API_KEY",
description:
"Configuration for Pinecone vector store\nThe Pinecone API key.",
},
{
name: "PINECONE_ENVIRONMENT",
},
{
name: "PINECONE_INDEX_NAME",
},
];
default:
return [];
}
};
const getDataSourceEnvs = (dataSource: TemplateDataSource) => {
switch (dataSource.type) {
case "web":
return [
{
name: "BASE_URL",
description: "The base URL to start web scraping.",
},
{
name: "URL_PREFIX",
description: "The prefix of the URL to start web scraping.",
},
{
name: "MAX_DEPTH",
description: "The maximum depth to scrape.",
},
];
default:
return [];
}
};
export const createBackendEnvFile = async (
root: string,
opts: {
openAiKey?: string;
llamaCloudKey?: string;
vectorDb?: TemplateVectorDB;
model?: string;
embeddingModel?: string;
framework?: TemplateFramework;
dataSource?: TemplateDataSource;
port?: number;
},
) => {
// Init env values
const envFileName = ".env";
const defaultEnvs = [
{
render: true,
name: "MODEL",
description: "The name of LLM model to use.",
value: opts.model || "gpt-3.5-turbo",
},
{
render: true,
name: "OPENAI_API_KEY",
description: "The OpenAI API key to use.",
value: opts.openAiKey,
},
// Add vector database environment variables
...(opts.vectorDb ? getVectorDBEnvs(opts.vectorDb) : []),
// Add data source environment variables
...(opts.dataSource ? getDataSourceEnvs(opts.dataSource) : []),
];
let envVars: EnvVar[] = [];
if (opts.framework === "fastapi") {
envVars = [
...defaultEnvs,
...[
{
name: "APP_HOST",
description: "The address to start the backend app.",
value: "0.0.0.0",
},
{
name: "APP_PORT",
description: "The port to start the backend app.",
value: opts.port?.toString() || "8000",
},
{
name: "EMBEDDING_MODEL",
description: "Name of the embedding model to use.",
value: opts.embeddingModel,
},
{
name: "EMBEDDING_DIM",
description: "Dimension of the embedding model to use.",
},
{
name: "LLM_TEMPERATURE",
description: "Temperature for sampling from the model.",
},
{
name: "LLM_MAX_TOKENS",
description: "Maximum number of tokens to generate.",
},
{
name: "TOP_K",
description:
"The number of similar embeddings to return when retrieving documents.",
value: "3",
},
{
name: "SYSTEM_PROMPT",
description: `Custom system prompt.
Example:
SYSTEM_PROMPT="
We have provided context information below.
---------------------
{context_str}
---------------------
Given this information, please answer the question: {query_str}
"`,
},
(opts?.dataSource?.config as FileSourceConfig).useLlamaParse
? {
name: "LLAMA_CLOUD_API_KEY",
description: `The Llama Cloud API key.`,
value: opts.llamaCloudKey,
}
: {},
],
];
} else {
envVars = [
...defaultEnvs,
...[
opts.framework === "nextjs"
? {
name: "NEXT_PUBLIC_MODEL",
description:
"The LLM model to use (hardcode to front-end artifact).",
}
: {},
],
];
}
// Render and write env file
const content = renderEnvVar(envVars);
await fs.writeFile(path.join(root, envFileName), content);
console.log(`Created '${envFileName}' file. Please check the settings.`);
};
export const createFrontendEnvFile = async (
root: string,
opts: {
customApiPath?: string;
model?: string;
},
) => {
const defaultFrontendEnvs = [
{
name: "MODEL",
description: "The OpenAI model to use.",
value: opts.model,
},
{
name: "NEXT_PUBLIC_MODEL",
description: "The OpenAI model to use (hardcode to front-end artifact).",
value: opts.model,
},
{
name: "NEXT_PUBLIC_CHAT_API",
description: "The backend API for chat endpoint.",
value: opts.customApiPath
? opts.customApiPath
: "http://localhost:8000/api/chat",
},
];
const content = renderEnvVar(defaultFrontendEnvs);
await fs.writeFile(path.join(root, ".env"), content);
};
+7 -83
View File
@@ -7,6 +7,7 @@ import { cyan } from "picocolors";
import { COMMUNITY_OWNER, COMMUNITY_REPO } from "./constant";
import { templatesDir } from "./dir";
import { createBackendEnvFile, createFrontendEnvFile } from "./env-variables";
import { PackageManager } from "./get-pkg-manager";
import { installLlamapackProject } from "./llama-pack";
import { isHavingPoetryLockFile, tryPoetryRun } from "./poetry";
@@ -18,89 +19,9 @@ import {
TemplateDataSource,
TemplateFramework,
TemplateVectorDB,
WebSourceConfig,
} from "./types";
import { installTSTemplate } from "./typescript";
const createEnvLocalFile = async (
root: string,
opts?: {
openAiKey?: string;
llamaCloudKey?: string;
vectorDb?: TemplateVectorDB;
model?: string;
embeddingModel?: string;
framework?: TemplateFramework;
dataSource?: TemplateDataSource;
},
) => {
const envFileName = ".env";
let content = "";
const model = opts?.model || "gpt-3.5-turbo";
content += `MODEL=${model}\n`;
if (opts?.framework === "nextjs") {
content += `NEXT_PUBLIC_MODEL=${model}\n`;
}
console.log("\nUsing OpenAI model: ", model, "\n");
if (opts?.openAiKey) {
content += `OPENAI_API_KEY=${opts?.openAiKey}\n`;
}
if (opts?.embeddingModel) {
content += `EMBEDDING_MODEL=${opts?.embeddingModel}\n`;
}
if ((opts?.dataSource?.config as FileSourceConfig).useLlamaParse) {
if (opts?.llamaCloudKey) {
content += `LLAMA_CLOUD_API_KEY=${opts?.llamaCloudKey}\n`;
} else {
content += `# Please obtain the Llama Cloud API key from https://cloud.llamaindex.ai/api-key
# and set it to the LLAMA_CLOUD_API_KEY variable below.
# LLAMA_CLOUD_API_KEY=`;
}
}
switch (opts?.vectorDb) {
case "mongo": {
content += `# For generating a connection URI, see https://www.mongodb.com/docs/guides/atlas/connection-string\n`;
content += `MONGO_URI=\n`;
content += `MONGODB_DATABASE=\n`;
content += `MONGODB_VECTORS=\n`;
content += `MONGODB_VECTOR_INDEX=\n`;
break;
}
case "pg": {
content += `# For generating a connection URI, see https://docs.timescale.com/use-timescale/latest/services/create-a-service\n`;
content += `PG_CONNECTION_STRING=\n`;
break;
}
case "pinecone": {
content += `PINECONE_API_KEY=\n`;
content += `PINECONE_ENVIRONMENT=\n`;
content += `PINECONE_INDEX_NAME=\n`;
break;
}
}
switch (opts?.dataSource?.type) {
case "web": {
const webConfig = opts?.dataSource.config as WebSourceConfig;
content += `# web loader config\n`;
content += `BASE_URL=${webConfig.baseUrl}\n`;
content += `URL_PREFIX=${webConfig.baseUrl}\n`;
content += `MAX_DEPTH=${webConfig.depth}\n`;
break;
}
}
if (content) {
await fs.writeFile(path.join(root, envFileName), content);
console.log(`Created '${envFileName}' file. Please check the settings.`);
}
};
// eslint-disable-next-line max-params
async function generateContextData(
framework: TemplateFramework,
@@ -232,7 +153,7 @@ 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 createEnvLocalFile(props.root, {
await createBackendEnvFile(props.root, {
openAiKey: props.openAiKey,
llamaCloudKey: props.llamaCloudKey,
vectorDb: props.vectorDb,
@@ -240,6 +161,7 @@ export const installTemplate = async (
embeddingModel: props.embeddingModel,
framework: props.framework,
dataSource: props.dataSource,
port: props.externalPort,
});
if (props.engine === "context") {
@@ -260,8 +182,10 @@ export const installTemplate = async (
}
} else {
// this is a frontend for a full-stack app, create .env file with model information
const content = `MODEL=${props.model}\nNEXT_PUBLIC_MODEL=${props.model}\n`;
await fs.writeFile(path.join(props.root, ".env"), content);
createFrontendEnvFile(props.root, {
model: props.model,
customApiPath: props.customApiPath,
});
}
};
-1
View File
@@ -41,7 +41,6 @@ export interface InstallTemplateArgs {
customApiPath?: string;
openAiKey?: string;
llamaCloudKey?: string;
forBackend?: string;
model: string;
embeddingModel: string;
communityProjectPath?: string;
+15 -22
View File
@@ -61,10 +61,10 @@ export const installTSTemplate = async ({
ui,
eslint,
customApiPath,
forBackend,
vectorDb,
postInstallAction,
}: InstallTemplateArgs) => {
backend,
}: InstallTemplateArgs & { backend: boolean }) => {
console.log(bold(`Using ${packageManager}.`));
/**
@@ -82,23 +82,20 @@ export const installTSTemplate = async ({
});
/**
* If the backend is next.js, rename next.config.app.js to next.config.js
* If not, rename next.config.static.js to next.config.js
* If next.js is not used as a backend, update next.config.js to use static site generation.
*/
if (framework == "nextjs" && forBackend === "nextjs") {
const nextConfigAppPath = path.join(root, "next.config.app.js");
const nextConfigPath = path.join(root, "next.config.js");
await fs.rename(nextConfigAppPath, nextConfigPath);
// delete next.config.static.js
const nextConfigStaticPath = path.join(root, "next.config.static.js");
await fs.rm(nextConfigStaticPath);
} else if (framework == "nextjs" && typeof forBackend === "undefined") {
const nextConfigStaticPath = path.join(root, "next.config.static.js");
const nextConfigPath = path.join(root, "next.config.js");
await fs.rename(nextConfigStaticPath, nextConfigPath);
// delete next.config.app.js
const nextConfigAppPath = path.join(root, "next.config.app.js");
await fs.rm(nextConfigAppPath);
if (framework === "nextjs" && !backend) {
// update next.config.json for static site generation
const nextConfigJsonFile = path.join(root, "next.config.json");
const nextConfigJson: any = JSON.parse(
await fs.readFile(nextConfigJsonFile, "utf8"),
);
nextConfigJson.output = "export";
nextConfigJson.images = { unoptimized: true };
await fs.writeFile(
nextConfigJsonFile,
JSON.stringify(nextConfigJson, null, 2) + os.EOL,
);
}
/**
@@ -174,10 +171,6 @@ export const installTSTemplate = async ({
const apiPath = path.join(root, "app", "api");
await fs.rm(apiPath, { recursive: true });
// modify the dev script to use the custom api path
packageJson.scripts = {
...packageJson.scripts,
dev: `cross-env NEXT_PUBLIC_CHAT_API=${customApiPath} next dev`,
};
}
if (engine === "context" && relativeEngineDestPath) {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "create-llama",
"version": "0.0.27",
"version": "0.0.28",
"keywords": [
"rag",
"llamaindex",
@@ -23,7 +23,7 @@
"clean": "rimraf --glob ./dist ./templates/**/__pycache__ ./templates/**/node_modules ./templates/**/poetry.lock",
"dev": "ncc build ./index.ts -w -o dist/",
"build": "npm run clean && ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
"lint": "eslint . --ignore-pattern dist",
"lint": "eslint . --ignore-pattern dist --ignore-pattern e2e/cache",
"e2e": "playwright test",
"prepublishOnly": "cd ../../ && pnpm run build:release"
},
@@ -1,3 +1,4 @@
import os
from llama_index.core.settings import Settings
from llama_index.core.agent import AgentRunner
from llama_index.core.tools.query_engine import QueryEngineTool
@@ -6,11 +7,13 @@ from app.engine.index import get_index
def get_chat_engine():
system_prompt = os.getenv("SYSTEM_PROMPT")
top_k = os.getenv("TOP_K", "3")
tools = []
# Add query tool
index = get_index()
query_engine = index.as_query_engine(similarity_top_k=3)
query_engine = index.as_query_engine(similarity_top_k=int(top_k))
query_engine_tool = QueryEngineTool.from_defaults(query_engine=query_engine)
tools.append(query_engine_tool)
@@ -20,5 +23,6 @@ def get_chat_engine():
return AgentRunner.from_llm(
llm=Settings.llm,
tools=tools,
system_prompt=system_prompt,
verbose=True,
)
@@ -1,7 +1,13 @@
import os
from app.engine.index import get_index
def get_chat_engine():
system_prompt = os.getenv("SYSTEM_PROMPT")
top_k = os.getenv("TOP_K", 3)
return get_index().as_chat_engine(
similarity_top_k=3, chat_mode="condense_plus_context"
similarity_top_k=int(top_k),
system_prompt=system_prompt,
chat_mode="condense_plus_context",
)
@@ -1,3 +1,4 @@
import os
from llama_parse import LlamaParse
from llama_index.core import SimpleDirectoryReader
@@ -5,6 +6,11 @@ DATA_DIR = "data" # directory containing the documents
def get_documents():
if os.getenv("LLAMA_CLOUD_API_KEY") is None:
raise ValueError(
"LLAMA_CLOUD_API_KEY environment variable is not set. "
"Please set it in .env file or in your shell environment then run again!"
)
parser = LlamaParse(result_type="markdown", verbose=True, language="en")
reader = SimpleDirectoryReader(DATA_DIR, file_extractor={".pdf": parser})
@@ -1,14 +1,41 @@
import os
from typing import Dict
from llama_index.core.settings import Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core.settings import Settings
def llm_config_from_env() -> Dict:
from llama_index.core.constants import DEFAULT_TEMPERATURE
model = os.getenv("MODEL")
temperature = os.getenv("LLM_TEMPERATURE", DEFAULT_TEMPERATURE)
max_tokens = os.getenv("LLM_MAX_TOKENS")
config = {
"model": model,
"temperature": float(temperature),
"max_tokens": int(max_tokens) if max_tokens is not None else None,
}
return config
def embedding_config_from_env() -> Dict:
model = os.getenv("EMBEDDING_MODEL")
dimension = os.getenv("EMBEDDING_DIM")
config = {
"model": model,
"dimension": int(dimension) if dimension is not None else None,
}
return config
def init_settings():
llm_model = os.getenv("MODEL", "gpt-3.5-turbo")
embedding_model = os.getenv("EMBEDDING_MODEL", "text-embedding-ada-002")
llm_configs = llm_config_from_env()
embedding_configs = embedding_config_from_env()
Settings.llm = OpenAI(model=llm_model)
Settings.embed_model = OpenAIEmbedding(model=embedding_model)
Settings.chunk_size = 1024
Settings.chunk_overlap = 20
Settings.llm = OpenAI(**llm_configs)
Settings.embed_model = OpenAIEmbedding(**embedding_configs)
Settings.chunk_size = int(os.getenv("CHUNK_SIZE", "1024"))
Settings.chunk_overlap = int(os.getenv("CHUNK_OVERLAP", "20"))
@@ -32,4 +32,7 @@ app.include_router(chat_router, prefix="/api/chat")
if __name__ == "__main__":
uvicorn.run(app="main:app", host="0.0.0.0", reload=True)
app_host = os.getenv("APP_HOST", "0.0.0.0")
app_port = int(os.getenv("APP_PORT", "8000"))
uvicorn.run(app="main:app", host=app_host, port=app_port, reload=True)
@@ -1,14 +1,41 @@
import os
from typing import Dict
from llama_index.core.settings import Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core.settings import Settings
def llm_config_from_env() -> Dict:
from llama_index.core.constants import DEFAULT_TEMPERATURE
model = os.getenv("MODEL")
temperature = os.getenv("LLM_TEMPERATURE", DEFAULT_TEMPERATURE)
max_tokens = os.getenv("LLM_MAX_TOKENS")
config = {
"model": model,
"temperature": float(temperature),
"max_tokens": int(max_tokens) if max_tokens is not None else None,
}
return config
def embedding_config_from_env() -> Dict:
model = os.getenv("EMBEDDING_MODEL")
dimension = os.getenv("EMBEDDING_DIM")
config = {
"model": model,
"dimension": int(dimension) if dimension is not None else None,
}
return config
def init_settings():
llm_model = os.getenv("MODEL", "gpt-3.5-turbo")
embedding_model = os.getenv("EMBEDDING_MODEL", "text-embedding-ada-002")
llm_configs = llm_config_from_env()
embedding_configs = embedding_config_from_env()
Settings.llm = OpenAI(model=llm_model)
Settings.embed_model = OpenAIEmbedding(model=embedding_model)
Settings.chunk_size = 1024
Settings.chunk_overlap = 20
Settings.llm = OpenAI(**llm_configs)
Settings.embed_model = OpenAIEmbedding(**embedding_configs)
Settings.chunk_size = int(os.getenv("CHUNK_SIZE", "1024"))
Settings.chunk_overlap = int(os.getenv("CHUNK_OVERLAP", "20"))
@@ -33,4 +33,7 @@ app.include_router(chat_router, prefix="/api/chat")
if __name__ == "__main__":
uvicorn.run(app="main:app", host="0.0.0.0", reload=True)
app_host = os.getenv("APP_HOST", "0.0.0.0")
app_port = int(os.getenv("APP_PORT", "8000"))
uvicorn.run(app="main:app", host=app_host, port=app_port, reload=True)
@@ -1,19 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config) => {
// See https://webpack.js.org/configuration/resolve/#resolvealias
config.resolve.alias = {
...config.resolve.alias,
sharp$: false,
"onnxruntime-node$": false,
};
return config;
},
experimental: {
outputFileTracingIncludes: {
"/*": ["./cache/**/*"],
},
},
};
module.exports = nextConfig;
@@ -0,0 +1,7 @@
{
"experimental": {
"outputFileTracingIncludes": {
"/*": ["./cache/**/*"]
}
}
}
@@ -0,0 +1,8 @@
/** @type {import('next').NextConfig} */
import fs from "fs";
import webpack from "./webpack.config.mjs";
const nextConfig = JSON.parse(fs.readFileSync("./next.config.json", "utf-8"));
nextConfig.webpack = webpack;
export default nextConfig;
@@ -1,21 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
images: { unoptimized: true },
webpack: (config) => {
// See https://webpack.js.org/configuration/resolve/#resolvealias
config.resolve.alias = {
...config.resolve.alias,
sharp$: false,
"onnxruntime-node$": false,
};
return config;
},
experimental: {
outputFileTracingIncludes: {
"/*": ["./cache/**/*"],
},
},
};
module.exports = nextConfig;
@@ -0,0 +1,10 @@
// webpack config must be a function in NextJS that is used to patch the default webpack config provided by NextJS, see https://nextjs.org/docs/pages/api-reference/next-config-js/webpack
export default function webpack(config) {
// See https://webpack.js.org/configuration/resolve/#resolvealias
config.resolve.alias = {
...config.resolve.alias,
sharp$: false,
"onnxruntime-node$": false,
};
return config;
}