mirror of
https://github.com/run-llama/create-llama.git
synced 2026-07-19 15:03:35 -04:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73dfac54c4 | |||
| 589e1e8cd9 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
||||||
"changelog": "@changesets/cli/changelog",
|
"changelog": "@changesets/cli/changelog",
|
||||||
"commit": false,
|
"commit": true,
|
||||||
"fixed": [],
|
"fixed": [],
|
||||||
"linked": [],
|
"linked": [],
|
||||||
"access": "public",
|
"access": "public",
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-llama": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Use `gpt-4-turbo` model as default. Upgrade Python llama-index to 0.10.28
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-llama": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Remove asking for AI models and use defaults instead (OpenAIs GPT-4 Vision Preview and Embeddings v3). Use `--ask-models` CLI parameter to select models.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-llama": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add observability for Python
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-llama": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Use poetry run generate to generate embeddings for FastAPI
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"create-llama": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Add support E2B code interpreter tool for FastAPI
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-llama": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Use Settings object for LlamaIndex configuration
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-llama": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add Qdrant support
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up python ${{ matrix.python-version }}
|
- name: Set up python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
@@ -35,13 +35,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
version: ${{ env.POETRY_VERSION }}
|
version: ${{ env.POETRY_VERSION }}
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js ${{ matrix.node-version }}
|
- name: Setup Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: "pnpm"
|
|
||||||
|
- uses: pnpm/action-setup@v3
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|||||||
@@ -14,13 +14,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: "pnpm"
|
|
||||||
|
- uses: pnpm/action-setup@v3
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
name: Publish to GitHub Releases
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: ".nvmrc"
|
|
||||||
cache: "pnpm"
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Build tarball
|
|
||||||
run: |
|
|
||||||
pnpm pack
|
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
uses: ncipollo/release-action@v1
|
|
||||||
with:
|
|
||||||
artifacts: "create-llama-*.tgz"
|
|
||||||
name: Release ${{ github.ref }}
|
|
||||||
bodyFile: "CHANGELOG.md"
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -15,41 +15,17 @@ jobs:
|
|||||||
- name: Checkout Repo
|
- name: Checkout Repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v3
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: "pnpm"
|
|
||||||
|
- uses: pnpm/action-setup@v3
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Add auth token to .npmrc file
|
- name: Create Release Pull Request
|
||||||
run: |
|
|
||||||
cat << EOF >> ".npmrc"
|
|
||||||
//registry.npmjs.org/:_authToken=$NPM_TOKEN
|
|
||||||
EOF
|
|
||||||
env:
|
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
|
|
||||||
- name: Get changeset status
|
|
||||||
id: get-changeset-status
|
|
||||||
run: |
|
|
||||||
pnpm changeset status --output .changeset/status.json
|
|
||||||
new_version=$(jq -r '.releases[0].newVersion' < .changeset/status.json)
|
|
||||||
rm -v .changeset/status.json
|
|
||||||
echo "new-version=${new_version}" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Create Release Pull Request or Publish to npm
|
|
||||||
id: changesets
|
|
||||||
uses: changesets/action@v1
|
uses: changesets/action@v1
|
||||||
with:
|
|
||||||
commit: Release ${{ steps.get-changeset-status.outputs.new-version }}
|
|
||||||
title: Release ${{ steps.get-changeset-status.outputs.new-version }}
|
|
||||||
# build package and call changeset publish
|
|
||||||
publish: pnpm release
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
|
|||||||
@@ -1,76 +1,5 @@
|
|||||||
# create-llama
|
# create-llama
|
||||||
|
|
||||||
## 0.1.8
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- cd50a33: Add interpreter tool for TS using e2b.dev
|
|
||||||
|
|
||||||
## 0.1.7
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 260d37a: Add system prompt env variable for TS
|
|
||||||
- bbd5b8d: Fix postgres connection leaking issue
|
|
||||||
- bb53425: Support HTTP proxies by setting the GLOBAL_AGENT_HTTP_PROXY env variable
|
|
||||||
- 69c2e16: Fix streaming for Express
|
|
||||||
- 7873bfb: Update Ollama provider to run with the base URL from the environment variable
|
|
||||||
|
|
||||||
## 0.1.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 56537a1: Display PDF files in source nodes
|
|
||||||
|
|
||||||
## 0.1.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 84db798: feat: support display latex in chat markdown
|
|
||||||
|
|
||||||
## 0.1.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 0bc8e75: Use ingestion pipeline for dedicated vector stores (Python only)
|
|
||||||
- cb1001d: Add ChromaDB vector store
|
|
||||||
|
|
||||||
## 0.1.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 416073d: Directly import vector stores to work with NextJS
|
|
||||||
|
|
||||||
## 0.1.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 056e376: Add support for displaying tool outputs (including weather widget as example)
|
|
||||||
|
|
||||||
## 0.1.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 7bd3ed5: Support Anthropic and Gemini as model providers
|
|
||||||
- 7bd3ed5: Support new agents from LITS 0.3
|
|
||||||
- cfb5257: Display events (e.g. retrieving nodes) per chat message
|
|
||||||
|
|
||||||
## 0.1.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- f1c3e8d: Add Llama3 and Phi3 support using Ollama
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- a0dec80: Use `gpt-4-turbo` model as default. Upgrade Python llama-index to 0.10.28
|
|
||||||
- 753229d: Remove asking for AI models and use defaults instead (OpenAIs GPT-4 Vision Preview and Embeddings v3). Use `--ask-models` CLI parameter to select models.
|
|
||||||
- 1d78202: Add observability for Python
|
|
||||||
- 6acccd2: Use poetry run generate to generate embeddings for FastAPI
|
|
||||||
- 9efcffe: Use Settings object for LlamaIndex configuration
|
|
||||||
- 418bf9b: refactor: use tsx instead of ts-node
|
|
||||||
- 1be69a5: Add Qdrant support
|
|
||||||
|
|
||||||
## 0.0.32
|
## 0.0.32
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ Install NodeJS. Preferably v18 using nvm or n.
|
|||||||
Inside the `create-llama` directory:
|
Inside the `create-llama` directory:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm i -g pnpm
|
npm i -g pnpm ts-node
|
||||||
pnpm install
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -30,8 +30,10 @@ export async function createApp({
|
|||||||
appPath,
|
appPath,
|
||||||
packageManager,
|
packageManager,
|
||||||
frontend,
|
frontend,
|
||||||
modelConfig,
|
openAiKey,
|
||||||
llamaCloudKey,
|
llamaCloudKey,
|
||||||
|
model,
|
||||||
|
embeddingModel,
|
||||||
communityProjectConfig,
|
communityProjectConfig,
|
||||||
llamapack,
|
llamapack,
|
||||||
vectorDb,
|
vectorDb,
|
||||||
@@ -75,8 +77,10 @@ export async function createApp({
|
|||||||
ui,
|
ui,
|
||||||
packageManager,
|
packageManager,
|
||||||
isOnline,
|
isOnline,
|
||||||
modelConfig,
|
openAiKey,
|
||||||
llamaCloudKey,
|
llamaCloudKey,
|
||||||
|
model,
|
||||||
|
embeddingModel,
|
||||||
communityProjectConfig,
|
communityProjectConfig,
|
||||||
llamapack,
|
llamapack,
|
||||||
vectorDb,
|
vectorDb,
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ export const copy = async (
|
|||||||
export const assetRelocator = (name: string) => {
|
export const assetRelocator = (name: string) => {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "gitignore":
|
case "gitignore":
|
||||||
case "npmrc":
|
|
||||||
case "eslintrc.json": {
|
case "eslintrc.json": {
|
||||||
return `.${name}`;
|
return `.${name}`;
|
||||||
}
|
}
|
||||||
|
|||||||
+94
-201
@@ -1,14 +1,12 @@
|
|||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { TOOL_SYSTEM_PROMPT_ENV_VAR, Tool } from "./tools";
|
|
||||||
import {
|
import {
|
||||||
ModelConfig,
|
|
||||||
TemplateDataSource,
|
TemplateDataSource,
|
||||||
TemplateFramework,
|
TemplateFramework,
|
||||||
TemplateVectorDB,
|
TemplateVectorDB,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
export type EnvVar = {
|
type EnvVar = {
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
@@ -30,20 +28,14 @@ const renderEnvVar = (envVars: EnvVar[]): string => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getVectorDBEnvs = (
|
const getVectorDBEnvs = (vectorDb: TemplateVectorDB) => {
|
||||||
vectorDb?: TemplateVectorDB,
|
|
||||||
framework?: TemplateFramework,
|
|
||||||
): EnvVar[] => {
|
|
||||||
if (!vectorDb || !framework) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
switch (vectorDb) {
|
switch (vectorDb) {
|
||||||
case "mongo":
|
case "mongo":
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: "MONGODB_URI",
|
name: "MONGO_URI",
|
||||||
description:
|
description:
|
||||||
"For generating a connection URI, see https://www.mongodb.com/docs/manual/reference/connection-string/ \nThe MongoDB connection URI.",
|
"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_DATABASE",
|
||||||
@@ -133,124 +125,61 @@ const getVectorDBEnvs = (
|
|||||||
"Optional API key for authenticating requests to Qdrant.",
|
"Optional API key for authenticating requests to Qdrant.",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
case "chroma":
|
|
||||||
const envs = [
|
|
||||||
{
|
|
||||||
name: "CHROMA_COLLECTION",
|
|
||||||
description: "The name of the collection in your Chroma database",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CHROMA_HOST",
|
|
||||||
description: "The API endpoint for your Chroma database",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CHROMA_PORT",
|
|
||||||
description: "The port for your Chroma database",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
// TS Version doesn't support config local storage path
|
|
||||||
if (framework === "fastapi") {
|
|
||||||
envs.push({
|
|
||||||
name: "CHROMA_PATH",
|
|
||||||
description: `The local path to the Chroma database.
|
|
||||||
Specify this if you are using a local Chroma database.
|
|
||||||
Otherwise, use CHROMA_HOST and CHROMA_PORT config above`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return envs;
|
|
||||||
default:
|
default:
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getModelEnvs = (modelConfig: ModelConfig): EnvVar[] => {
|
export const createBackendEnvFile = async (
|
||||||
return [
|
root: string,
|
||||||
{
|
opts: {
|
||||||
name: "MODEL_PROVIDER",
|
openAiKey?: string;
|
||||||
description: "The provider for the AI models to use.",
|
llamaCloudKey?: string;
|
||||||
value: modelConfig.provider,
|
vectorDb?: TemplateVectorDB;
|
||||||
},
|
model?: string;
|
||||||
|
embeddingModel?: string;
|
||||||
|
framework?: TemplateFramework;
|
||||||
|
dataSources?: TemplateDataSource[];
|
||||||
|
port?: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
// Init env values
|
||||||
|
const envFileName = ".env";
|
||||||
|
const defaultEnvs = [
|
||||||
{
|
{
|
||||||
|
render: true,
|
||||||
name: "MODEL",
|
name: "MODEL",
|
||||||
description: "The name of LLM model to use.",
|
description: "The name of LLM model to use.",
|
||||||
value: modelConfig.model,
|
value: opts.model,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
render: true,
|
||||||
|
name: "OPENAI_API_KEY",
|
||||||
|
description: "The OpenAI API key to use.",
|
||||||
|
value: opts.openAiKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "LLAMA_CLOUD_API_KEY",
|
||||||
|
description: `The Llama Cloud API key.`,
|
||||||
|
value: opts.llamaCloudKey,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "EMBEDDING_MODEL",
|
name: "EMBEDDING_MODEL",
|
||||||
description: "Name of the embedding model to use.",
|
description: "Name of the embedding model to use.",
|
||||||
value: modelConfig.embeddingModel,
|
value: opts.embeddingModel,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "EMBEDDING_DIM",
|
name: "EMBEDDING_DIM",
|
||||||
description: "Dimension of the embedding model to use.",
|
description: "Dimension of the embedding model to use.",
|
||||||
value: modelConfig.dimensions.toString(),
|
value: 1536,
|
||||||
},
|
},
|
||||||
...(modelConfig.provider === "openai"
|
// Add vector database environment variables
|
||||||
? [
|
...(opts.vectorDb ? getVectorDBEnvs(opts.vectorDb) : []),
|
||||||
{
|
|
||||||
name: "OPENAI_API_KEY",
|
|
||||||
description: "The OpenAI API key to use.",
|
|
||||||
value: modelConfig.apiKey,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "LLM_TEMPERATURE",
|
|
||||||
description: "Temperature for sampling from the model.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "LLM_MAX_TOKENS",
|
|
||||||
description: "Maximum number of tokens to generate.",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(modelConfig.provider === "anthropic"
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
name: "ANTHROPIC_API_KEY",
|
|
||||||
description: "The Anthropic API key to use.",
|
|
||||||
value: modelConfig.apiKey,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(modelConfig.provider === "gemini"
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
name: "GOOGLE_API_KEY",
|
|
||||||
description: "The Google API key to use.",
|
|
||||||
value: modelConfig.apiKey,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(modelConfig.provider === "ollama"
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
name: "OLLAMA_BASE_URL",
|
|
||||||
description:
|
|
||||||
"The base URL for the Ollama API. Eg: http://127.0.0.1:11434",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
];
|
];
|
||||||
};
|
let envVars: EnvVar[] = [];
|
||||||
|
if (opts.framework === "fastapi") {
|
||||||
const getFrameworkEnvs = (
|
envVars = [
|
||||||
framework: TemplateFramework,
|
...defaultEnvs,
|
||||||
port?: number,
|
|
||||||
): EnvVar[] => {
|
|
||||||
const sPort = port?.toString() || "8000";
|
|
||||||
const result: EnvVar[] = [
|
|
||||||
{
|
|
||||||
name: "FILESERVER_URL_PREFIX",
|
|
||||||
description:
|
|
||||||
"FILESERVER_URL_PREFIX is the URL prefix of the server storing the images generated by the interpreter.",
|
|
||||||
value:
|
|
||||||
framework === "nextjs"
|
|
||||||
? // FIXME: if we are using nextjs, port should be 3000
|
|
||||||
"http://localhost:3000/api/files"
|
|
||||||
: `http://localhost:${sPort}/api/files`,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
if (framework === "fastapi") {
|
|
||||||
result.push(
|
|
||||||
...[
|
...[
|
||||||
{
|
{
|
||||||
name: "APP_HOST",
|
name: "APP_HOST",
|
||||||
@@ -260,98 +189,51 @@ const getFrameworkEnvs = (
|
|||||||
{
|
{
|
||||||
name: "APP_PORT",
|
name: "APP_PORT",
|
||||||
description: "The port to start the backend app.",
|
description: "The port to start the backend app.",
|
||||||
value: sPort,
|
value: opts.port?.toString() || "8000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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}
|
||||||
|
"`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
];
|
||||||
|
} else {
|
||||||
|
envVars = [
|
||||||
|
...defaultEnvs,
|
||||||
|
...[
|
||||||
|
opts.framework === "nextjs"
|
||||||
|
? {
|
||||||
|
name: "NEXT_PUBLIC_MODEL",
|
||||||
|
description:
|
||||||
|
"The LLM model to use (hardcode to front-end artifact).",
|
||||||
|
value: opts.model,
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getEngineEnvs = (): EnvVar[] => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: "TOP_K",
|
|
||||||
description:
|
|
||||||
"The number of similar embeddings to return when retrieving documents.",
|
|
||||||
value: "3",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
const getToolEnvs = (tools?: Tool[]): EnvVar[] => {
|
|
||||||
if (!tools?.length) return [];
|
|
||||||
const toolEnvs: EnvVar[] = [];
|
|
||||||
tools.forEach((tool) => {
|
|
||||||
if (tool.envVars?.length) {
|
|
||||||
toolEnvs.push(
|
|
||||||
// Don't include the system prompt env var here
|
|
||||||
// It should be handled separately by merging with the default system prompt
|
|
||||||
...tool.envVars.filter(
|
|
||||||
(env) => env.name !== TOOL_SYSTEM_PROMPT_ENV_VAR,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return toolEnvs;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSystemPromptEnv = (tools?: Tool[]): EnvVar => {
|
|
||||||
const defaultSystemPrompt =
|
|
||||||
"You are a helpful assistant who helps users with their questions.";
|
|
||||||
|
|
||||||
// build tool system prompt by merging all tool system prompts
|
|
||||||
let toolSystemPrompt = "";
|
|
||||||
tools?.forEach((tool) => {
|
|
||||||
const toolSystemPromptEnv = tool.envVars?.find(
|
|
||||||
(env) => env.name === TOOL_SYSTEM_PROMPT_ENV_VAR,
|
|
||||||
);
|
|
||||||
if (toolSystemPromptEnv) {
|
|
||||||
toolSystemPrompt += toolSystemPromptEnv.value + "\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const systemPrompt = toolSystemPrompt
|
|
||||||
? `\"${toolSystemPrompt}\"`
|
|
||||||
: defaultSystemPrompt;
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: "SYSTEM_PROMPT",
|
|
||||||
description: "The system prompt for the AI model.",
|
|
||||||
value: systemPrompt,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createBackendEnvFile = async (
|
|
||||||
root: string,
|
|
||||||
opts: {
|
|
||||||
llamaCloudKey?: string;
|
|
||||||
vectorDb?: TemplateVectorDB;
|
|
||||||
modelConfig: ModelConfig;
|
|
||||||
framework: TemplateFramework;
|
|
||||||
dataSources?: TemplateDataSource[];
|
|
||||||
port?: number;
|
|
||||||
tools?: Tool[];
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
// Init env values
|
|
||||||
const envFileName = ".env";
|
|
||||||
const envVars: EnvVar[] = [
|
|
||||||
{
|
|
||||||
name: "LLAMA_CLOUD_API_KEY",
|
|
||||||
description: `The Llama Cloud API key.`,
|
|
||||||
value: opts.llamaCloudKey,
|
|
||||||
},
|
|
||||||
// Add model environment variables
|
|
||||||
...getModelEnvs(opts.modelConfig),
|
|
||||||
// Add engine environment variables
|
|
||||||
...getEngineEnvs(),
|
|
||||||
// Add vector database environment variables
|
|
||||||
...getVectorDBEnvs(opts.vectorDb, opts.framework),
|
|
||||||
...getFrameworkEnvs(opts.framework, opts.port),
|
|
||||||
...getToolEnvs(opts.tools),
|
|
||||||
getSystemPromptEnv(opts.tools),
|
|
||||||
];
|
|
||||||
// Render and write env file
|
// Render and write env file
|
||||||
const content = renderEnvVar(envVars);
|
const content = renderEnvVar(envVars);
|
||||||
await fs.writeFile(path.join(root, envFileName), content);
|
await fs.writeFile(path.join(root, envFileName), content);
|
||||||
@@ -362,9 +244,20 @@ export const createFrontendEnvFile = async (
|
|||||||
root: string,
|
root: string,
|
||||||
opts: {
|
opts: {
|
||||||
customApiPath?: string;
|
customApiPath?: string;
|
||||||
|
model?: string;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const defaultFrontendEnvs = [
|
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",
|
name: "NEXT_PUBLIC_CHAT_API",
|
||||||
description: "The backend API for chat endpoint.",
|
description: "The backend API for chat endpoint.",
|
||||||
|
|||||||
+9
-13
@@ -15,7 +15,6 @@ import { ConfigFileType, writeToolsConfig } from "./tools";
|
|||||||
import {
|
import {
|
||||||
FileSourceConfig,
|
FileSourceConfig,
|
||||||
InstallTemplateArgs,
|
InstallTemplateArgs,
|
||||||
ModelConfig,
|
|
||||||
TemplateDataSource,
|
TemplateDataSource,
|
||||||
TemplateFramework,
|
TemplateFramework,
|
||||||
TemplateVectorDB,
|
TemplateVectorDB,
|
||||||
@@ -25,8 +24,8 @@ import { installTSTemplate } from "./typescript";
|
|||||||
// eslint-disable-next-line max-params
|
// eslint-disable-next-line max-params
|
||||||
async function generateContextData(
|
async function generateContextData(
|
||||||
framework: TemplateFramework,
|
framework: TemplateFramework,
|
||||||
modelConfig: ModelConfig,
|
|
||||||
packageManager?: PackageManager,
|
packageManager?: PackageManager,
|
||||||
|
openAiKey?: string,
|
||||||
vectorDb?: TemplateVectorDB,
|
vectorDb?: TemplateVectorDB,
|
||||||
llamaCloudKey?: string,
|
llamaCloudKey?: string,
|
||||||
useLlamaParse?: boolean,
|
useLlamaParse?: boolean,
|
||||||
@@ -37,12 +36,12 @@ async function generateContextData(
|
|||||||
? "poetry run generate"
|
? "poetry run generate"
|
||||||
: `${packageManager} run generate`,
|
: `${packageManager} run generate`,
|
||||||
)}`;
|
)}`;
|
||||||
const modelConfigured = modelConfig.isConfigured();
|
const openAiKeyConfigured = openAiKey || process.env["OPENAI_API_KEY"];
|
||||||
const llamaCloudKeyConfigured = useLlamaParse
|
const llamaCloudKeyConfigured = useLlamaParse
|
||||||
? llamaCloudKey || process.env["LLAMA_CLOUD_API_KEY"]
|
? llamaCloudKey || process.env["LLAMA_CLOUD_API_KEY"]
|
||||||
: true;
|
: true;
|
||||||
const hasVectorDb = vectorDb && vectorDb !== "none";
|
const hasVectorDb = vectorDb && vectorDb !== "none";
|
||||||
if (modelConfigured && llamaCloudKeyConfigured && !hasVectorDb) {
|
if (openAiKeyConfigured && llamaCloudKeyConfigured && !hasVectorDb) {
|
||||||
// If all the required environment variables are set, run the generate script
|
// If all the required environment variables are set, run the generate script
|
||||||
if (framework === "fastapi") {
|
if (framework === "fastapi") {
|
||||||
if (isHavingPoetryLockFile()) {
|
if (isHavingPoetryLockFile()) {
|
||||||
@@ -64,7 +63,7 @@ async function generateContextData(
|
|||||||
|
|
||||||
// generate the message of what to do to run the generate script manually
|
// generate the message of what to do to run the generate script manually
|
||||||
const settings = [];
|
const settings = [];
|
||||||
if (!modelConfigured) settings.push("your model provider API key");
|
if (!openAiKeyConfigured) settings.push("your OpenAI key");
|
||||||
if (!llamaCloudKeyConfigured) settings.push("your Llama Cloud key");
|
if (!llamaCloudKeyConfigured) settings.push("your Llama Cloud key");
|
||||||
if (hasVectorDb) settings.push("your Vector DB environment variables");
|
if (hasVectorDb) settings.push("your Vector DB environment variables");
|
||||||
const settingsMessage =
|
const settingsMessage =
|
||||||
@@ -142,13 +141,14 @@ export const installTemplate = async (
|
|||||||
|
|
||||||
// Copy the environment file to the target directory.
|
// Copy the environment file to the target directory.
|
||||||
await createBackendEnvFile(props.root, {
|
await createBackendEnvFile(props.root, {
|
||||||
modelConfig: props.modelConfig,
|
openAiKey: props.openAiKey,
|
||||||
llamaCloudKey: props.llamaCloudKey,
|
llamaCloudKey: props.llamaCloudKey,
|
||||||
vectorDb: props.vectorDb,
|
vectorDb: props.vectorDb,
|
||||||
|
model: props.model,
|
||||||
|
embeddingModel: props.embeddingModel,
|
||||||
framework: props.framework,
|
framework: props.framework,
|
||||||
dataSources: props.dataSources,
|
dataSources: props.dataSources,
|
||||||
port: props.externalPort,
|
port: props.externalPort,
|
||||||
tools: props.tools,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (props.dataSources.length > 0) {
|
if (props.dataSources.length > 0) {
|
||||||
@@ -163,22 +163,18 @@ export const installTemplate = async (
|
|||||||
) {
|
) {
|
||||||
await generateContextData(
|
await generateContextData(
|
||||||
props.framework,
|
props.framework,
|
||||||
props.modelConfig,
|
|
||||||
props.packageManager,
|
props.packageManager,
|
||||||
|
props.openAiKey,
|
||||||
props.vectorDb,
|
props.vectorDb,
|
||||||
props.llamaCloudKey,
|
props.llamaCloudKey,
|
||||||
props.useLlamaParse,
|
props.useLlamaParse,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create tool-output directory
|
|
||||||
if (props.tools && props.tools.length > 0) {
|
|
||||||
await fsExtra.mkdir(path.join(props.root, "tool-output"));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// this is a frontend for a full-stack app, create .env file with model information
|
// this is a frontend for a full-stack app, create .env file with model information
|
||||||
await createFrontendEnvFile(props.root, {
|
await createFrontendEnvFile(props.root, {
|
||||||
|
model: props.model,
|
||||||
customApiPath: props.customApiPath,
|
customApiPath: props.customApiPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
import ciInfo from "ci-info";
|
|
||||||
import prompts from "prompts";
|
|
||||||
import { ModelConfigParams } from ".";
|
|
||||||
import { questionHandlers, toChoice } from "../../questions";
|
|
||||||
|
|
||||||
const MODELS = [
|
|
||||||
"claude-3-opus",
|
|
||||||
"claude-3-sonnet",
|
|
||||||
"claude-3-haiku",
|
|
||||||
"claude-2.1",
|
|
||||||
"claude-instant-1.2",
|
|
||||||
];
|
|
||||||
const DEFAULT_MODEL = MODELS[0];
|
|
||||||
|
|
||||||
// TODO: get embedding vector dimensions from the anthropic sdk (currently not supported)
|
|
||||||
// Use huggingface embedding models for now
|
|
||||||
enum HuggingFaceEmbeddingModelType {
|
|
||||||
XENOVA_ALL_MINILM_L6_V2 = "all-MiniLM-L6-v2",
|
|
||||||
XENOVA_ALL_MPNET_BASE_V2 = "all-mpnet-base-v2",
|
|
||||||
}
|
|
||||||
type ModelData = {
|
|
||||||
dimensions: number;
|
|
||||||
};
|
|
||||||
const EMBEDDING_MODELS: Record<HuggingFaceEmbeddingModelType, ModelData> = {
|
|
||||||
[HuggingFaceEmbeddingModelType.XENOVA_ALL_MINILM_L6_V2]: {
|
|
||||||
dimensions: 384,
|
|
||||||
},
|
|
||||||
[HuggingFaceEmbeddingModelType.XENOVA_ALL_MPNET_BASE_V2]: {
|
|
||||||
dimensions: 768,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const DEFAULT_EMBEDDING_MODEL = Object.keys(EMBEDDING_MODELS)[0];
|
|
||||||
const DEFAULT_DIMENSIONS = Object.values(EMBEDDING_MODELS)[0].dimensions;
|
|
||||||
|
|
||||||
type AnthropicQuestionsParams = {
|
|
||||||
apiKey?: string;
|
|
||||||
askModels: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function askAnthropicQuestions({
|
|
||||||
askModels,
|
|
||||||
apiKey,
|
|
||||||
}: AnthropicQuestionsParams): Promise<ModelConfigParams> {
|
|
||||||
const config: ModelConfigParams = {
|
|
||||||
apiKey,
|
|
||||||
model: DEFAULT_MODEL,
|
|
||||||
embeddingModel: DEFAULT_EMBEDDING_MODEL,
|
|
||||||
dimensions: DEFAULT_DIMENSIONS,
|
|
||||||
isConfigured(): boolean {
|
|
||||||
if (config.apiKey) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (process.env["ANTHROPIC_API_KEY"]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!config.apiKey) {
|
|
||||||
const { key } = await prompts(
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
name: "key",
|
|
||||||
message:
|
|
||||||
"Please provide your Anthropic API key (or leave blank to use ANTHROPIC_API_KEY env variable):",
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.apiKey = key || process.env.ANTHROPIC_API_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use default model values in CI or if user should not be asked
|
|
||||||
const useDefaults = ciInfo.isCI || !askModels;
|
|
||||||
if (!useDefaults) {
|
|
||||||
const { model } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "model",
|
|
||||||
message: "Which LLM model would you like to use?",
|
|
||||||
choices: MODELS.map(toChoice),
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.model = model;
|
|
||||||
|
|
||||||
const { embeddingModel } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "embeddingModel",
|
|
||||||
message: "Which embedding model would you like to use?",
|
|
||||||
choices: Object.keys(EMBEDDING_MODELS).map(toChoice),
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.embeddingModel = embeddingModel;
|
|
||||||
config.dimensions =
|
|
||||||
EMBEDDING_MODELS[
|
|
||||||
embeddingModel as HuggingFaceEmbeddingModelType
|
|
||||||
].dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
import ciInfo from "ci-info";
|
|
||||||
import prompts from "prompts";
|
|
||||||
import { ModelConfigParams } from ".";
|
|
||||||
import { questionHandlers, toChoice } from "../../questions";
|
|
||||||
|
|
||||||
const MODELS = ["gemini-1.5-pro-latest", "gemini-pro", "gemini-pro-vision"];
|
|
||||||
type ModelData = {
|
|
||||||
dimensions: number;
|
|
||||||
};
|
|
||||||
const EMBEDDING_MODELS: Record<string, ModelData> = {
|
|
||||||
"embedding-001": { dimensions: 768 },
|
|
||||||
"text-embedding-004": { dimensions: 768 },
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_MODEL = MODELS[0];
|
|
||||||
const DEFAULT_EMBEDDING_MODEL = Object.keys(EMBEDDING_MODELS)[0];
|
|
||||||
const DEFAULT_DIMENSIONS = Object.values(EMBEDDING_MODELS)[0].dimensions;
|
|
||||||
|
|
||||||
type GeminiQuestionsParams = {
|
|
||||||
apiKey?: string;
|
|
||||||
askModels: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function askGeminiQuestions({
|
|
||||||
askModels,
|
|
||||||
apiKey,
|
|
||||||
}: GeminiQuestionsParams): Promise<ModelConfigParams> {
|
|
||||||
const config: ModelConfigParams = {
|
|
||||||
apiKey,
|
|
||||||
model: DEFAULT_MODEL,
|
|
||||||
embeddingModel: DEFAULT_EMBEDDING_MODEL,
|
|
||||||
dimensions: DEFAULT_DIMENSIONS,
|
|
||||||
isConfigured(): boolean {
|
|
||||||
if (config.apiKey) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (process.env["GOOGLE_API_KEY"]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!config.apiKey) {
|
|
||||||
const { key } = await prompts(
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
name: "key",
|
|
||||||
message:
|
|
||||||
"Please provide your Google API key (or leave blank to use GOOGLE_API_KEY env variable):",
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.apiKey = key || process.env.GOOGLE_API_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use default model values in CI or if user should not be asked
|
|
||||||
const useDefaults = ciInfo.isCI || !askModels;
|
|
||||||
if (!useDefaults) {
|
|
||||||
const { model } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "model",
|
|
||||||
message: "Which LLM model would you like to use?",
|
|
||||||
choices: MODELS.map(toChoice),
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.model = model;
|
|
||||||
|
|
||||||
const { embeddingModel } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "embeddingModel",
|
|
||||||
message: "Which embedding model would you like to use?",
|
|
||||||
choices: Object.keys(EMBEDDING_MODELS).map(toChoice),
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.embeddingModel = embeddingModel;
|
|
||||||
config.dimensions = EMBEDDING_MODELS[embeddingModel].dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import ciInfo from "ci-info";
|
|
||||||
import prompts from "prompts";
|
|
||||||
import { questionHandlers } from "../../questions";
|
|
||||||
import { ModelConfig, ModelProvider } from "../types";
|
|
||||||
import { askAnthropicQuestions } from "./anthropic";
|
|
||||||
import { askGeminiQuestions } from "./gemini";
|
|
||||||
import { askOllamaQuestions } from "./ollama";
|
|
||||||
import { askOpenAIQuestions } from "./openai";
|
|
||||||
|
|
||||||
const DEFAULT_MODEL_PROVIDER = "openai";
|
|
||||||
|
|
||||||
export type ModelConfigQuestionsParams = {
|
|
||||||
openAiKey?: string;
|
|
||||||
askModels: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ModelConfigParams = Omit<ModelConfig, "provider">;
|
|
||||||
|
|
||||||
export async function askModelConfig({
|
|
||||||
askModels,
|
|
||||||
openAiKey,
|
|
||||||
}: ModelConfigQuestionsParams): Promise<ModelConfig> {
|
|
||||||
let modelProvider: ModelProvider = DEFAULT_MODEL_PROVIDER;
|
|
||||||
if (askModels && !ciInfo.isCI) {
|
|
||||||
const { provider } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "provider",
|
|
||||||
message: "Which model provider would you like to use",
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
title: "OpenAI",
|
|
||||||
value: "openai",
|
|
||||||
},
|
|
||||||
{ title: "Ollama", value: "ollama" },
|
|
||||||
{ title: "Anthropic", value: "anthropic" },
|
|
||||||
{ title: "Gemini", value: "gemini" },
|
|
||||||
],
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
modelProvider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
let modelConfig: ModelConfigParams;
|
|
||||||
switch (modelProvider) {
|
|
||||||
case "ollama":
|
|
||||||
modelConfig = await askOllamaQuestions({ askModels });
|
|
||||||
break;
|
|
||||||
case "anthropic":
|
|
||||||
modelConfig = await askAnthropicQuestions({ askModels });
|
|
||||||
break;
|
|
||||||
case "gemini":
|
|
||||||
modelConfig = await askGeminiQuestions({ askModels });
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
modelConfig = await askOpenAIQuestions({
|
|
||||||
openAiKey,
|
|
||||||
askModels,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...modelConfig,
|
|
||||||
provider: modelProvider,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
import ciInfo from "ci-info";
|
|
||||||
import ollama, { type ModelResponse } from "ollama";
|
|
||||||
import { red } from "picocolors";
|
|
||||||
import prompts from "prompts";
|
|
||||||
import { ModelConfigParams } from ".";
|
|
||||||
import { questionHandlers, toChoice } from "../../questions";
|
|
||||||
|
|
||||||
type ModelData = {
|
|
||||||
dimensions: number;
|
|
||||||
};
|
|
||||||
const MODELS = ["llama3:8b", "wizardlm2:7b", "gemma:7b", "phi3"];
|
|
||||||
const DEFAULT_MODEL = MODELS[0];
|
|
||||||
// TODO: get embedding vector dimensions from the ollama sdk (currently not supported)
|
|
||||||
const EMBEDDING_MODELS: Record<string, ModelData> = {
|
|
||||||
"nomic-embed-text": { dimensions: 768 },
|
|
||||||
"mxbai-embed-large": { dimensions: 1024 },
|
|
||||||
"all-minilm": { dimensions: 384 },
|
|
||||||
};
|
|
||||||
const DEFAULT_EMBEDDING_MODEL: string = Object.keys(EMBEDDING_MODELS)[0];
|
|
||||||
|
|
||||||
type OllamaQuestionsParams = {
|
|
||||||
askModels: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function askOllamaQuestions({
|
|
||||||
askModels,
|
|
||||||
}: OllamaQuestionsParams): Promise<ModelConfigParams> {
|
|
||||||
const config: ModelConfigParams = {
|
|
||||||
model: DEFAULT_MODEL,
|
|
||||||
embeddingModel: DEFAULT_EMBEDDING_MODEL,
|
|
||||||
dimensions: EMBEDDING_MODELS[DEFAULT_EMBEDDING_MODEL].dimensions,
|
|
||||||
isConfigured(): boolean {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// use default model values in CI or if user should not be asked
|
|
||||||
const useDefaults = ciInfo.isCI || !askModels;
|
|
||||||
if (!useDefaults) {
|
|
||||||
const { model } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "model",
|
|
||||||
message: "Which LLM model would you like to use?",
|
|
||||||
choices: MODELS.map(toChoice),
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
await ensureModel(model);
|
|
||||||
config.model = model;
|
|
||||||
|
|
||||||
const { embeddingModel } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "embeddingModel",
|
|
||||||
message: "Which embedding model would you like to use?",
|
|
||||||
choices: Object.keys(EMBEDDING_MODELS).map(toChoice),
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
await ensureModel(embeddingModel);
|
|
||||||
config.embeddingModel = embeddingModel;
|
|
||||||
config.dimensions = EMBEDDING_MODELS[embeddingModel].dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ensureModel(modelName: string) {
|
|
||||||
try {
|
|
||||||
if (modelName.split(":").length === 1) {
|
|
||||||
// model doesn't have a version suffix, use latest
|
|
||||||
modelName = modelName + ":latest";
|
|
||||||
}
|
|
||||||
const { models } = await ollama.list();
|
|
||||||
const found =
|
|
||||||
models.find((model: ModelResponse) => model.name === modelName) !==
|
|
||||||
undefined;
|
|
||||||
if (!found) {
|
|
||||||
console.log(
|
|
||||||
red(
|
|
||||||
`Model ${modelName} was not pulled yet. Call 'ollama pull ${modelName}' and try again.`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(
|
|
||||||
red("Listing Ollama models failed. Is 'ollama' running? " + error),
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
import ciInfo from "ci-info";
|
|
||||||
import got from "got";
|
|
||||||
import ora from "ora";
|
|
||||||
import { red } from "picocolors";
|
|
||||||
import prompts from "prompts";
|
|
||||||
import { ModelConfigParams, ModelConfigQuestionsParams } from ".";
|
|
||||||
import { questionHandlers } from "../../questions";
|
|
||||||
|
|
||||||
const OPENAI_API_URL = "https://api.openai.com/v1";
|
|
||||||
|
|
||||||
const DEFAULT_MODEL = "gpt-3.5-turbo";
|
|
||||||
const DEFAULT_EMBEDDING_MODEL = "text-embedding-3-large";
|
|
||||||
|
|
||||||
export async function askOpenAIQuestions({
|
|
||||||
openAiKey,
|
|
||||||
askModels,
|
|
||||||
}: ModelConfigQuestionsParams): Promise<ModelConfigParams> {
|
|
||||||
const config: ModelConfigParams = {
|
|
||||||
apiKey: openAiKey,
|
|
||||||
model: DEFAULT_MODEL,
|
|
||||||
embeddingModel: DEFAULT_EMBEDDING_MODEL,
|
|
||||||
dimensions: getDimensions(DEFAULT_EMBEDDING_MODEL),
|
|
||||||
isConfigured(): boolean {
|
|
||||||
if (config.apiKey) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (process.env["OPENAI_API_KEY"]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!config.apiKey) {
|
|
||||||
const { key } = await prompts(
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
name: "key",
|
|
||||||
message: askModels
|
|
||||||
? "Please provide your OpenAI API key (or leave blank to use OPENAI_API_KEY env variable):"
|
|
||||||
: "Please provide your OpenAI API key (leave blank to skip):",
|
|
||||||
validate: (value: string) => {
|
|
||||||
if (askModels && !value) {
|
|
||||||
if (process.env.OPENAI_API_KEY) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return "OPENAI_API_KEY env variable is not set - key is required";
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.apiKey = key || process.env.OPENAI_API_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use default model values in CI or if user should not be asked
|
|
||||||
const useDefaults = ciInfo.isCI || !askModels;
|
|
||||||
if (!useDefaults) {
|
|
||||||
const { model } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "model",
|
|
||||||
message: "Which LLM model would you like to use?",
|
|
||||||
choices: await getAvailableModelChoices(false, config.apiKey),
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.model = model;
|
|
||||||
|
|
||||||
const { embeddingModel } = await prompts(
|
|
||||||
{
|
|
||||||
type: "select",
|
|
||||||
name: "embeddingModel",
|
|
||||||
message: "Which embedding model would you like to use?",
|
|
||||||
choices: await getAvailableModelChoices(true, config.apiKey),
|
|
||||||
initial: 0,
|
|
||||||
},
|
|
||||||
questionHandlers,
|
|
||||||
);
|
|
||||||
config.embeddingModel = embeddingModel;
|
|
||||||
config.dimensions = getDimensions(embeddingModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getAvailableModelChoices(
|
|
||||||
selectEmbedding: boolean,
|
|
||||||
apiKey?: string,
|
|
||||||
) {
|
|
||||||
if (!apiKey) {
|
|
||||||
throw new Error("need OpenAI key to retrieve model choices");
|
|
||||||
}
|
|
||||||
const isLLMModel = (modelId: string) => {
|
|
||||||
return modelId.startsWith("gpt");
|
|
||||||
};
|
|
||||||
|
|
||||||
const isEmbeddingModel = (modelId: string) => {
|
|
||||||
return modelId.includes("embedding");
|
|
||||||
};
|
|
||||||
|
|
||||||
const spinner = ora("Fetching available models").start();
|
|
||||||
try {
|
|
||||||
const response = await got(`${OPENAI_API_URL}/models`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: "Bearer " + apiKey,
|
|
||||||
},
|
|
||||||
timeout: 5000,
|
|
||||||
responseType: "json",
|
|
||||||
});
|
|
||||||
const data: any = await response.body;
|
|
||||||
spinner.stop();
|
|
||||||
return data.data
|
|
||||||
.filter((model: any) =>
|
|
||||||
selectEmbedding ? isEmbeddingModel(model.id) : isLLMModel(model.id),
|
|
||||||
)
|
|
||||||
.map((el: any) => {
|
|
||||||
return {
|
|
||||||
title: el.id,
|
|
||||||
value: el.id,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
spinner.stop();
|
|
||||||
if ((error as any).response?.statusCode === 401) {
|
|
||||||
console.log(
|
|
||||||
red(
|
|
||||||
"Invalid OpenAI API key provided! Please provide a valid key and try again!",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log(red("Request failed: " + error));
|
|
||||||
}
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDimensions(modelName: string) {
|
|
||||||
// at 2024-04-24 all OpenAI embedding models support 1536 dimensions except
|
|
||||||
// "text-embedding-3-large", see https://openai.com/blog/new-embedding-models-and-api-updates
|
|
||||||
return modelName === "text-embedding-3-large" ? 1024 : 1536;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/* Function to conditionally load the global-agent/bootstrap module */
|
|
||||||
export async function initializeGlobalAgent() {
|
|
||||||
if (process.env.GLOBAL_AGENT_HTTP_PROXY) {
|
|
||||||
/* Dynamically import global-agent/bootstrap */
|
|
||||||
await import("global-agent/bootstrap");
|
|
||||||
console.log("Proxy enabled via global-agent.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+35
-101
@@ -10,7 +10,6 @@ import { isPoetryAvailable, tryPoetryInstall } from "./poetry";
|
|||||||
import { Tool } from "./tools";
|
import { Tool } from "./tools";
|
||||||
import {
|
import {
|
||||||
InstallTemplateArgs,
|
InstallTemplateArgs,
|
||||||
ModelConfig,
|
|
||||||
TemplateDataSource,
|
TemplateDataSource,
|
||||||
TemplateVectorDB,
|
TemplateVectorDB,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
@@ -22,9 +21,8 @@ interface Dependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAdditionalDependencies = (
|
const getAdditionalDependencies = (
|
||||||
modelConfig: ModelConfig,
|
|
||||||
vectorDb?: TemplateVectorDB,
|
vectorDb?: TemplateVectorDB,
|
||||||
dataSources?: TemplateDataSource[],
|
dataSource?: TemplateDataSource,
|
||||||
tools?: Tool[],
|
tools?: Tool[],
|
||||||
) => {
|
) => {
|
||||||
const dependencies: Dependency[] = [];
|
const dependencies: Dependency[] = [];
|
||||||
@@ -43,7 +41,6 @@ const getAdditionalDependencies = (
|
|||||||
name: "llama-index-vector-stores-postgres",
|
name: "llama-index-vector-stores-postgres",
|
||||||
version: "^0.1.1",
|
version: "^0.1.1",
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case "pinecone": {
|
case "pinecone": {
|
||||||
dependencies.push({
|
dependencies.push({
|
||||||
@@ -70,105 +67,47 @@ const getAdditionalDependencies = (
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "qdrant": {
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-vector-stores-qdrant",
|
|
||||||
version: "^0.2.8",
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "chroma": {
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-vector-stores-chroma",
|
|
||||||
version: "^0.1.8",
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add data source dependencies
|
// Add data source dependencies
|
||||||
if (dataSources) {
|
const dataSourceType = dataSource?.type;
|
||||||
for (const ds of dataSources) {
|
switch (dataSourceType) {
|
||||||
const dsType = ds?.type;
|
case "file":
|
||||||
switch (dsType) {
|
dependencies.push({
|
||||||
case "file":
|
name: "docx2txt",
|
||||||
dependencies.push({
|
version: "^0.8",
|
||||||
name: "docx2txt",
|
});
|
||||||
version: "^0.8",
|
break;
|
||||||
});
|
case "web":
|
||||||
break;
|
dependencies.push({
|
||||||
case "web":
|
name: "llama-index-readers-web",
|
||||||
dependencies.push({
|
version: "^0.1.6",
|
||||||
name: "llama-index-readers-web",
|
});
|
||||||
version: "^0.1.6",
|
break;
|
||||||
});
|
case "db":
|
||||||
break;
|
dependencies.push({
|
||||||
case "db":
|
name: "llama-index-readers-database",
|
||||||
dependencies.push({
|
version: "^0.1.3",
|
||||||
name: "llama-index-readers-database",
|
});
|
||||||
version: "^0.1.3",
|
dependencies.push({
|
||||||
});
|
name: "pymysql",
|
||||||
dependencies.push({
|
version: "^1.1.0",
|
||||||
name: "pymysql",
|
extras: ["rsa"],
|
||||||
version: "^1.1.0",
|
});
|
||||||
extras: ["rsa"],
|
dependencies.push({
|
||||||
});
|
name: "psycopg2",
|
||||||
dependencies.push({
|
version: "^2.9.9",
|
||||||
name: "psycopg2",
|
});
|
||||||
version: "^2.9.9",
|
break;
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tools dependencies
|
// Add tools dependencies
|
||||||
console.log("Adding tools dependencies");
|
|
||||||
tools?.forEach((tool) => {
|
tools?.forEach((tool) => {
|
||||||
tool.dependencies?.forEach((dep) => {
|
tool.dependencies?.forEach((dep) => {
|
||||||
dependencies.push(dep);
|
dependencies.push(dep);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (modelConfig.provider) {
|
|
||||||
case "ollama":
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-llms-ollama",
|
|
||||||
version: "0.1.2",
|
|
||||||
});
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-embeddings-ollama",
|
|
||||||
version: "0.1.2",
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "openai":
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-agent-openai",
|
|
||||||
version: "0.2.2",
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "anthropic":
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-llms-anthropic",
|
|
||||||
version: "0.1.10",
|
|
||||||
});
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-embeddings-huggingface",
|
|
||||||
version: "0.2.0",
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "gemini":
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-llms-gemini",
|
|
||||||
version: "0.1.7",
|
|
||||||
});
|
|
||||||
dependencies.push({
|
|
||||||
name: "llama-index-embeddings-gemini",
|
|
||||||
version: "0.1.6",
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dependencies;
|
return dependencies;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -266,8 +205,8 @@ export const installPythonTemplate = async ({
|
|||||||
dataSources,
|
dataSources,
|
||||||
tools,
|
tools,
|
||||||
postInstallAction,
|
postInstallAction,
|
||||||
|
useLlamaParse,
|
||||||
observability,
|
observability,
|
||||||
modelConfig,
|
|
||||||
}: Pick<
|
}: Pick<
|
||||||
InstallTemplateArgs,
|
InstallTemplateArgs,
|
||||||
| "root"
|
| "root"
|
||||||
@@ -276,9 +215,9 @@ export const installPythonTemplate = async ({
|
|||||||
| "vectorDb"
|
| "vectorDb"
|
||||||
| "dataSources"
|
| "dataSources"
|
||||||
| "tools"
|
| "tools"
|
||||||
|
| "useLlamaParse"
|
||||||
| "postInstallAction"
|
| "postInstallAction"
|
||||||
| "observability"
|
| "observability"
|
||||||
| "modelConfig"
|
|
||||||
>) => {
|
>) => {
|
||||||
console.log("\nInitializing Python project with template:", template, "\n");
|
console.log("\nInitializing Python project with template:", template, "\n");
|
||||||
const templatePath = path.join(templatesDir, "types", template, framework);
|
const templatePath = path.join(templatesDir, "types", template, framework);
|
||||||
@@ -318,14 +257,9 @@ export const installPythonTemplate = async ({
|
|||||||
cwd: path.join(compPath, "engines", "python", engine),
|
cwd: path.join(compPath, "engines", "python", engine),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Adding additional dependencies");
|
const addOnDependencies = dataSources
|
||||||
|
.map((ds) => getAdditionalDependencies(vectorDb, ds, tools))
|
||||||
const addOnDependencies = getAdditionalDependencies(
|
.flat();
|
||||||
modelConfig,
|
|
||||||
vectorDb,
|
|
||||||
dataSources,
|
|
||||||
tools,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (observability === "opentelemetry") {
|
if (observability === "opentelemetry") {
|
||||||
addOnDependencies.push({
|
addOnDependencies.push({
|
||||||
|
|||||||
+2
-82
@@ -2,25 +2,15 @@ import fs from "fs/promises";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { red } from "picocolors";
|
import { red } from "picocolors";
|
||||||
import yaml from "yaml";
|
import yaml from "yaml";
|
||||||
import { EnvVar } from "./env-variables";
|
|
||||||
import { makeDir } from "./make-dir";
|
import { makeDir } from "./make-dir";
|
||||||
import { TemplateFramework } from "./types";
|
import { TemplateFramework } from "./types";
|
||||||
|
|
||||||
export const TOOL_SYSTEM_PROMPT_ENV_VAR = "TOOL_SYSTEM_PROMPT";
|
|
||||||
|
|
||||||
export enum ToolType {
|
|
||||||
LLAMAHUB = "llamahub",
|
|
||||||
LOCAL = "local",
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Tool = {
|
export type Tool = {
|
||||||
display: string;
|
display: string;
|
||||||
name: string;
|
name: string;
|
||||||
config?: Record<string, any>;
|
config?: Record<string, any>;
|
||||||
dependencies?: ToolDependencies[];
|
dependencies?: ToolDependencies[];
|
||||||
supportedFrameworks?: Array<TemplateFramework>;
|
supportedFrameworks?: Array<TemplateFramework>;
|
||||||
type: ToolType;
|
|
||||||
envVars?: EnvVar[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ToolDependencies = {
|
export type ToolDependencies = {
|
||||||
@@ -45,14 +35,6 @@ export const supportedTools: Tool[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
supportedFrameworks: ["fastapi"],
|
supportedFrameworks: ["fastapi"],
|
||||||
type: ToolType.LLAMAHUB,
|
|
||||||
envVars: [
|
|
||||||
{
|
|
||||||
name: TOOL_SYSTEM_PROMPT_ENV_VAR,
|
|
||||||
description: "System prompt for google search tool.",
|
|
||||||
value: `You are a Google search agent. You help users to get information from Google search.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
display: "Wikipedia",
|
display: "Wikipedia",
|
||||||
@@ -64,58 +46,6 @@ export const supportedTools: Tool[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
supportedFrameworks: ["fastapi", "express", "nextjs"],
|
supportedFrameworks: ["fastapi", "express", "nextjs"],
|
||||||
type: ToolType.LLAMAHUB,
|
|
||||||
envVars: [
|
|
||||||
{
|
|
||||||
name: TOOL_SYSTEM_PROMPT_ENV_VAR,
|
|
||||||
description: "System prompt for wiki tool.",
|
|
||||||
value: `You are a Wikipedia agent. You help users to get information from Wikipedia.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
display: "Weather",
|
|
||||||
name: "weather",
|
|
||||||
dependencies: [],
|
|
||||||
supportedFrameworks: ["fastapi", "express", "nextjs"],
|
|
||||||
type: ToolType.LOCAL,
|
|
||||||
envVars: [
|
|
||||||
{
|
|
||||||
name: TOOL_SYSTEM_PROMPT_ENV_VAR,
|
|
||||||
description: "System prompt for weather tool.",
|
|
||||||
value: `You are a weather forecast agent. You help users to get the weather forecast for a given location.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
display: "Code Interpreter",
|
|
||||||
name: "interpreter",
|
|
||||||
dependencies: [
|
|
||||||
{
|
|
||||||
name: "e2b_code_interpreter",
|
|
||||||
version: "0.0.7",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
supportedFrameworks: ["fastapi", "express", "nextjs"],
|
|
||||||
type: ToolType.LOCAL,
|
|
||||||
envVars: [
|
|
||||||
{
|
|
||||||
name: "E2B_API_KEY",
|
|
||||||
description:
|
|
||||||
"E2B_API_KEY key is required to run code interpreter tool. Get it here: https://e2b.dev/docs/getting-started/api-key",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: TOOL_SYSTEM_PROMPT_ENV_VAR,
|
|
||||||
description: "System prompt for code interpreter tool.",
|
|
||||||
value: `You are a Python interpreter.
|
|
||||||
- You are given tasks to complete and you run python code to solve them.
|
|
||||||
- The python code runs in a Jupyter notebook. Every time you call \`interpreter\` tool, the python code is executed in a separate cell. It's okay to make multiple calls to \`interpreter\`.
|
|
||||||
- Display visualizations using matplotlib or any other visualization library directly in the notebook. Shouldn't save the visualizations to a file, just return the base64 encoded data.
|
|
||||||
- You can install any pip package (if it exists) if you need to but the usual packages for data analysis are already preinstalled.
|
|
||||||
- You can run any python code you want in a secure environment.
|
|
||||||
- Use absolute url from result to display images or any other media.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -160,19 +90,9 @@ export const writeToolsConfig = async (
|
|||||||
type: ConfigFileType = ConfigFileType.YAML,
|
type: ConfigFileType = ConfigFileType.YAML,
|
||||||
) => {
|
) => {
|
||||||
if (tools.length === 0) return; // no tools selected, no config need
|
if (tools.length === 0) return; // no tools selected, no config need
|
||||||
const configContent: {
|
const configContent: Record<string, any> = {};
|
||||||
[key in ToolType]: Record<string, any>;
|
|
||||||
} = {
|
|
||||||
local: {},
|
|
||||||
llamahub: {},
|
|
||||||
};
|
|
||||||
tools.forEach((tool) => {
|
tools.forEach((tool) => {
|
||||||
if (tool.type === ToolType.LLAMAHUB) {
|
configContent[tool.name] = tool.config ?? {};
|
||||||
configContent.llamahub[tool.name] = tool.config ?? {};
|
|
||||||
}
|
|
||||||
if (tool.type === ToolType.LOCAL) {
|
|
||||||
configContent.local[tool.name] = tool.config ?? {};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
const configPath = path.join(root, "config");
|
const configPath = path.join(root, "config");
|
||||||
await makeDir(configPath);
|
await makeDir(configPath);
|
||||||
|
|||||||
+4
-12
@@ -1,15 +1,6 @@
|
|||||||
import { PackageManager } from "../helpers/get-pkg-manager";
|
import { PackageManager } from "../helpers/get-pkg-manager";
|
||||||
import { Tool } from "./tools";
|
import { Tool } from "./tools";
|
||||||
|
|
||||||
export type ModelProvider = "openai" | "ollama" | "anthropic" | "gemini";
|
|
||||||
export type ModelConfig = {
|
|
||||||
provider: ModelProvider;
|
|
||||||
apiKey?: string;
|
|
||||||
model: string;
|
|
||||||
embeddingModel: string;
|
|
||||||
dimensions: number;
|
|
||||||
isConfigured(): boolean;
|
|
||||||
};
|
|
||||||
export type TemplateType = "streaming" | "community" | "llamapack";
|
export type TemplateType = "streaming" | "community" | "llamapack";
|
||||||
export type TemplateFramework = "nextjs" | "express" | "fastapi";
|
export type TemplateFramework = "nextjs" | "express" | "fastapi";
|
||||||
export type TemplateUI = "html" | "shadcn";
|
export type TemplateUI = "html" | "shadcn";
|
||||||
@@ -20,8 +11,7 @@ export type TemplateVectorDB =
|
|||||||
| "pinecone"
|
| "pinecone"
|
||||||
| "milvus"
|
| "milvus"
|
||||||
| "astra"
|
| "astra"
|
||||||
| "qdrant"
|
| "qdrant";
|
||||||
| "chroma";
|
|
||||||
export type TemplatePostInstallAction =
|
export type TemplatePostInstallAction =
|
||||||
| "none"
|
| "none"
|
||||||
| "VSCode"
|
| "VSCode"
|
||||||
@@ -69,9 +59,11 @@ export interface InstallTemplateArgs {
|
|||||||
ui: TemplateUI;
|
ui: TemplateUI;
|
||||||
dataSources: TemplateDataSource[];
|
dataSources: TemplateDataSource[];
|
||||||
customApiPath?: string;
|
customApiPath?: string;
|
||||||
modelConfig: ModelConfig;
|
openAiKey?: string;
|
||||||
llamaCloudKey?: string;
|
llamaCloudKey?: string;
|
||||||
useLlamaParse?: boolean;
|
useLlamaParse?: boolean;
|
||||||
|
model: string;
|
||||||
|
embeddingModel: string;
|
||||||
communityProjectConfig?: CommunityProjectConfig;
|
communityProjectConfig?: CommunityProjectConfig;
|
||||||
llamapack?: string;
|
llamapack?: string;
|
||||||
vectorDb?: TemplateVectorDB;
|
vectorDb?: TemplateVectorDB;
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export const installTSTemplate = async ({
|
|||||||
const enginePath = path.join(root, relativeEngineDestPath, "engine");
|
const enginePath = path.join(root, relativeEngineDestPath, "engine");
|
||||||
|
|
||||||
// copy vector db component
|
// copy vector db component
|
||||||
console.log("\nUsing vector DB:", vectorDb ?? "none", "\n");
|
console.log("\nUsing vector DB:", vectorDb, "\n");
|
||||||
await copy("**", enginePath, {
|
await copy("**", enginePath, {
|
||||||
parents: true,
|
parents: true,
|
||||||
cwd: path.join(compPath, "vectordbs", "typescript", vectorDb ?? "none"),
|
cwd: path.join(compPath, "vectordbs", "typescript", vectorDb ?? "none"),
|
||||||
@@ -205,7 +205,7 @@ async function updatePackageJson({
|
|||||||
// add generate script if using context engine
|
// add generate script if using context engine
|
||||||
packageJson.scripts = {
|
packageJson.scripts = {
|
||||||
...packageJson.scripts,
|
...packageJson.scripts,
|
||||||
generate: `tsx ${path.join(
|
generate: `ts-node ${path.join(
|
||||||
relativeEngineDestPath,
|
relativeEngineDestPath,
|
||||||
"engine",
|
"engine",
|
||||||
"generate.ts",
|
"generate.ts",
|
||||||
|
|||||||
@@ -12,16 +12,12 @@ import { createApp } from "./create-app";
|
|||||||
import { getDataSources } from "./helpers/datasources";
|
import { getDataSources } from "./helpers/datasources";
|
||||||
import { getPkgManager } from "./helpers/get-pkg-manager";
|
import { getPkgManager } from "./helpers/get-pkg-manager";
|
||||||
import { isFolderEmpty } from "./helpers/is-folder-empty";
|
import { isFolderEmpty } from "./helpers/is-folder-empty";
|
||||||
import { initializeGlobalAgent } from "./helpers/proxy";
|
|
||||||
import { runApp } from "./helpers/run-app";
|
import { runApp } from "./helpers/run-app";
|
||||||
import { getTools } from "./helpers/tools";
|
import { getTools } from "./helpers/tools";
|
||||||
import { validateNpmName } from "./helpers/validate-pkg";
|
import { validateNpmName } from "./helpers/validate-pkg";
|
||||||
import packageJson from "./package.json";
|
import packageJson from "./package.json";
|
||||||
import { QuestionArgs, askQuestions, onPromptState } from "./questions";
|
import { QuestionArgs, askQuestions, onPromptState } from "./questions";
|
||||||
|
|
||||||
// Run the initialization function
|
|
||||||
initializeGlobalAgent();
|
|
||||||
|
|
||||||
let projectPath: string = "";
|
let projectPath: string = "";
|
||||||
|
|
||||||
const handleSigTerm = () => process.exit(0);
|
const handleSigTerm = () => process.exit(0);
|
||||||
@@ -111,6 +107,20 @@ const program = new Commander.Command(packageJson.name)
|
|||||||
`
|
`
|
||||||
|
|
||||||
Whether to generate a frontend for your backend.
|
Whether to generate a frontend for your backend.
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
"--model <model>",
|
||||||
|
`
|
||||||
|
|
||||||
|
Select OpenAI model to use. E.g. gpt-3.5-turbo.
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
"--embedding-model <embeddingModel>",
|
||||||
|
`
|
||||||
|
|
||||||
|
Select OpenAI embedding model to use. E.g. text-embedding-ada-002.
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
.option(
|
.option(
|
||||||
@@ -191,7 +201,9 @@ if (process.argv.includes("--tools")) {
|
|||||||
if (process.argv.includes("--no-llama-parse")) {
|
if (process.argv.includes("--no-llama-parse")) {
|
||||||
program.useLlamaParse = false;
|
program.useLlamaParse = false;
|
||||||
}
|
}
|
||||||
program.askModels = process.argv.includes("--ask-models");
|
if (process.argv.includes("--ask-models")) {
|
||||||
|
program.askModels = true;
|
||||||
|
}
|
||||||
if (process.argv.includes("--no-files")) {
|
if (process.argv.includes("--no-files")) {
|
||||||
program.dataSources = [];
|
program.dataSources = [];
|
||||||
} else {
|
} else {
|
||||||
@@ -278,11 +290,7 @@ async function run(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const preferences = (conf.get("preferences") || {}) as QuestionArgs;
|
const preferences = (conf.get("preferences") || {}) as QuestionArgs;
|
||||||
await askQuestions(
|
await askQuestions(program as unknown as QuestionArgs, preferences);
|
||||||
program as unknown as QuestionArgs,
|
|
||||||
preferences,
|
|
||||||
program.openAiKey,
|
|
||||||
);
|
|
||||||
|
|
||||||
await createApp({
|
await createApp({
|
||||||
template: program.template,
|
template: program.template,
|
||||||
@@ -291,8 +299,10 @@ async function run(): Promise<void> {
|
|||||||
appPath: resolvedProjectPath,
|
appPath: resolvedProjectPath,
|
||||||
packageManager,
|
packageManager,
|
||||||
frontend: program.frontend,
|
frontend: program.frontend,
|
||||||
modelConfig: program.modelConfig,
|
openAiKey: program.openAiKey,
|
||||||
llamaCloudKey: program.llamaCloudKey,
|
llamaCloudKey: program.llamaCloudKey,
|
||||||
|
model: program.model,
|
||||||
|
embeddingModel: program.embeddingModel,
|
||||||
communityProjectConfig: program.communityProjectConfig,
|
communityProjectConfig: program.communityProjectConfig,
|
||||||
llamapack: program.llamapack,
|
llamapack: program.llamapack,
|
||||||
vectorDb: program.vectorDb,
|
vectorDb: program.vectorDb,
|
||||||
|
|||||||
+22
-26
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "create-llama",
|
"name": "create-llama",
|
||||||
"version": "0.1.8",
|
"version": "0.0.32",
|
||||||
"description": "Create LlamaIndex-powered apps with one command",
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"rag",
|
"rag",
|
||||||
"llamaindex",
|
"llamaindex",
|
||||||
"next.js"
|
"next.js"
|
||||||
],
|
],
|
||||||
|
"description": "Create LlamaIndex-powered apps with one command",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/run-llama/LlamaIndexTS",
|
"url": "https://github.com/run-llama/LlamaIndexTS",
|
||||||
@@ -20,30 +20,32 @@
|
|||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bash ./scripts/build.sh",
|
|
||||||
"build:ncc": "pnpm run clean && ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
|
|
||||||
"clean": "rimraf --glob ./dist ./templates/**/__pycache__ ./templates/**/node_modules ./templates/**/poetry.lock",
|
"clean": "rimraf --glob ./dist ./templates/**/__pycache__ ./templates/**/node_modules ./templates/**/poetry.lock",
|
||||||
"dev": "ncc build ./index.ts -w -o dist/",
|
|
||||||
"e2e": "playwright test",
|
|
||||||
"format": "prettier --ignore-unknown --cache --check .",
|
"format": "prettier --ignore-unknown --cache --check .",
|
||||||
"format:write": "prettier --ignore-unknown --write .",
|
"format:write": "prettier --ignore-unknown --write .",
|
||||||
|
"dev": "ncc build ./index.ts -w -o dist/",
|
||||||
|
"build": "bash ./scripts/build.sh",
|
||||||
|
"build:ncc": "pnpm run clean && ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
|
||||||
"lint": "eslint . --ignore-pattern dist --ignore-pattern e2e/cache",
|
"lint": "eslint . --ignore-pattern dist --ignore-pattern e2e/cache",
|
||||||
"new-snapshot": "pnpm run build && changeset version --snapshot",
|
"e2e": "playwright test",
|
||||||
"new-version": "pnpm run build && changeset version",
|
|
||||||
"pack-install": "bash ./scripts/pack.sh",
|
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"release": "pnpm run build && changeset publish",
|
"release": "pnpm run build && changeset publish",
|
||||||
"release-snapshot": "pnpm run build && changeset publish --tag snapshot"
|
"new-version": "pnpm run build && changeset version",
|
||||||
|
"release-snapshot": "pnpm run build && changeset publish --tag snapshot",
|
||||||
|
"new-snapshot": "pnpm run build && changeset version --snapshot",
|
||||||
|
"pack-install": "bash ./scripts/pack.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.41.1",
|
||||||
"@types/async-retry": "1.4.2",
|
"@types/async-retry": "1.4.2",
|
||||||
"@types/ci-info": "2.0.0",
|
"@types/ci-info": "2.0.0",
|
||||||
"@types/cross-spawn": "6.0.0",
|
"@types/cross-spawn": "6.0.0",
|
||||||
"@types/fs-extra": "11.0.4",
|
|
||||||
"@types/node": "^20.11.7",
|
"@types/node": "^20.11.7",
|
||||||
"@types/prompts": "2.0.1",
|
"@types/prompts": "2.0.1",
|
||||||
"@types/tar": "6.1.5",
|
"@types/tar": "6.1.5",
|
||||||
"@types/validate-npm-package-name": "3.0.0",
|
"@types/validate-npm-package-name": "3.0.0",
|
||||||
|
"@types/fs-extra": "11.0.4",
|
||||||
|
"@vercel/ncc": "0.38.1",
|
||||||
"async-retry": "1.3.1",
|
"async-retry": "1.3.1",
|
||||||
"async-sema": "3.0.1",
|
"async-sema": "3.0.1",
|
||||||
"ci-info": "github:watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540",
|
"ci-info": "github:watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540",
|
||||||
@@ -51,35 +53,29 @@
|
|||||||
"conf": "10.2.0",
|
"conf": "10.2.0",
|
||||||
"cross-spawn": "7.0.3",
|
"cross-spawn": "7.0.3",
|
||||||
"fast-glob": "3.3.1",
|
"fast-glob": "3.3.1",
|
||||||
"fs-extra": "11.2.0",
|
|
||||||
"global-agent": "^3.0.0",
|
|
||||||
"got": "10.7.0",
|
"got": "10.7.0",
|
||||||
"ollama": "^0.5.0",
|
|
||||||
"ora": "^8.0.1",
|
|
||||||
"picocolors": "1.0.0",
|
"picocolors": "1.0.0",
|
||||||
"prompts": "2.1.0",
|
"prompts": "2.1.0",
|
||||||
|
"rimraf": "^5.0.5",
|
||||||
"smol-toml": "^1.1.4",
|
"smol-toml": "^1.1.4",
|
||||||
"tar": "6.1.15",
|
"tar": "6.1.15",
|
||||||
"terminal-link": "^3.0.0",
|
"terminal-link": "^3.0.0",
|
||||||
"update-check": "1.5.4",
|
"update-check": "1.5.4",
|
||||||
"validate-npm-package-name": "3.0.0",
|
"validate-npm-package-name": "3.0.0",
|
||||||
"yaml": "2.4.1"
|
"wait-port": "^1.1.0",
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@changesets/cli": "^2.27.1",
|
"@changesets/cli": "^2.27.1",
|
||||||
"@playwright/test": "^1.41.1",
|
|
||||||
"@vercel/ncc": "0.38.1",
|
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^8.10.0",
|
|
||||||
"husky": "^9.0.10",
|
"husky": "^9.0.10",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^3.2.4",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"rimraf": "^5.0.5",
|
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"wait-port": "^1.1.0"
|
"eslint-config-prettier": "^8.10.0",
|
||||||
|
"ora": "^8.0.1",
|
||||||
|
"fs-extra": "11.2.0",
|
||||||
|
"yaml": "2.4.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.0.5",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.14.0"
|
"node": ">=16.14.0"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@8.15.1"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1975
-2592
File diff suppressed because it is too large
Load Diff
+140
-35
@@ -1,6 +1,8 @@
|
|||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
import ciInfo from "ci-info";
|
import ciInfo from "ci-info";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
import got from "got";
|
||||||
|
import ora from "ora";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { blue, green, red } from "picocolors";
|
import { blue, green, red } from "picocolors";
|
||||||
import prompts from "prompts";
|
import prompts from "prompts";
|
||||||
@@ -14,10 +16,11 @@ import { COMMUNITY_OWNER, COMMUNITY_REPO } from "./helpers/constant";
|
|||||||
import { EXAMPLE_FILE } from "./helpers/datasources";
|
import { EXAMPLE_FILE } from "./helpers/datasources";
|
||||||
import { templatesDir } from "./helpers/dir";
|
import { templatesDir } from "./helpers/dir";
|
||||||
import { getAvailableLlamapackOptions } from "./helpers/llama-pack";
|
import { getAvailableLlamapackOptions } from "./helpers/llama-pack";
|
||||||
import { askModelConfig } from "./helpers/providers";
|
|
||||||
import { getProjectOptions } from "./helpers/repo";
|
import { getProjectOptions } from "./helpers/repo";
|
||||||
import { supportedTools, toolsRequireConfig } from "./helpers/tools";
|
import { supportedTools, toolsRequireConfig } from "./helpers/tools";
|
||||||
|
|
||||||
|
const OPENAI_API_URL = "https://api.openai.com/v1";
|
||||||
|
|
||||||
export type QuestionArgs = Omit<
|
export type QuestionArgs = Omit<
|
||||||
InstallAppArgs,
|
InstallAppArgs,
|
||||||
"appPath" | "packageManager"
|
"appPath" | "packageManager"
|
||||||
@@ -64,13 +67,16 @@ if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK)
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const defaults: Omit<QuestionArgs, "modelConfig"> = {
|
const defaults: QuestionArgs = {
|
||||||
template: "streaming",
|
template: "streaming",
|
||||||
framework: "nextjs",
|
framework: "nextjs",
|
||||||
ui: "shadcn",
|
ui: "shadcn",
|
||||||
frontend: false,
|
frontend: false,
|
||||||
|
openAiKey: "",
|
||||||
llamaCloudKey: "",
|
llamaCloudKey: "",
|
||||||
useLlamaParse: false,
|
useLlamaParse: false,
|
||||||
|
model: "gpt-4-turbo",
|
||||||
|
embeddingModel: "text-embedding-3-large",
|
||||||
communityProjectConfig: undefined,
|
communityProjectConfig: undefined,
|
||||||
llamapack: "",
|
llamapack: "",
|
||||||
postInstallAction: "dependencies",
|
postInstallAction: "dependencies",
|
||||||
@@ -78,7 +84,7 @@ const defaults: Omit<QuestionArgs, "modelConfig"> = {
|
|||||||
tools: [],
|
tools: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const questionHandlers = {
|
const handlers = {
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
console.error("Exiting.");
|
console.error("Exiting.");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -97,7 +103,6 @@ const getVectorDbChoices = (framework: TemplateFramework) => {
|
|||||||
{ title: "Milvus", value: "milvus" },
|
{ title: "Milvus", value: "milvus" },
|
||||||
{ title: "Astra", value: "astra" },
|
{ title: "Astra", value: "astra" },
|
||||||
{ title: "Qdrant", value: "qdrant" },
|
{ title: "Qdrant", value: "qdrant" },
|
||||||
{ title: "ChromaDB", value: "chroma" },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const vectordbLang = framework === "fastapi" ? "python" : "typescript";
|
const vectordbLang = framework === "fastapi" ? "python" : "typescript";
|
||||||
@@ -227,15 +232,63 @@ export const onPromptState = (state: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAvailableModelChoices = async (
|
||||||
|
selectEmbedding: boolean,
|
||||||
|
apiKey?: string,
|
||||||
|
) => {
|
||||||
|
const isLLMModel = (modelId: string) => {
|
||||||
|
return modelId.startsWith("gpt");
|
||||||
|
};
|
||||||
|
|
||||||
|
const isEmbeddingModel = (modelId: string) => {
|
||||||
|
return modelId.includes("embedding");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (apiKey) {
|
||||||
|
const spinner = ora("Fetching available models").start();
|
||||||
|
try {
|
||||||
|
const response = await got(`${OPENAI_API_URL}/models`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + apiKey,
|
||||||
|
},
|
||||||
|
timeout: 5000,
|
||||||
|
responseType: "json",
|
||||||
|
});
|
||||||
|
const data: any = await response.body;
|
||||||
|
spinner.stop();
|
||||||
|
return data.data
|
||||||
|
.filter((model: any) =>
|
||||||
|
selectEmbedding ? isEmbeddingModel(model.id) : isLLMModel(model.id),
|
||||||
|
)
|
||||||
|
.map((el: any) => {
|
||||||
|
return {
|
||||||
|
title: el.id,
|
||||||
|
value: el.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
spinner.stop();
|
||||||
|
if ((error as any).response?.statusCode === 401) {
|
||||||
|
console.log(
|
||||||
|
red(
|
||||||
|
"Invalid OpenAI API key provided! Please provide a valid key and try again!",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(red("Request failed: " + error));
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const askQuestions = async (
|
export const askQuestions = async (
|
||||||
program: QuestionArgs,
|
program: QuestionArgs,
|
||||||
preferences: QuestionArgs,
|
preferences: QuestionArgs,
|
||||||
openAiKey?: string,
|
|
||||||
) => {
|
) => {
|
||||||
const getPrefOrDefault = <K extends keyof Omit<QuestionArgs, "modelConfig">>(
|
const getPrefOrDefault = <K extends keyof QuestionArgs>(
|
||||||
field: K,
|
field: K,
|
||||||
): Omit<QuestionArgs, "modelConfig">[K] =>
|
): QuestionArgs[K] => preferences[field] ?? defaults[field];
|
||||||
preferences[field] ?? defaults[field];
|
|
||||||
|
|
||||||
// Ask for next action after installation
|
// Ask for next action after installation
|
||||||
async function askPostInstallAction() {
|
async function askPostInstallAction() {
|
||||||
@@ -258,8 +311,8 @@ export const askQuestions = async (
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const modelConfigured =
|
const openAiKeyConfigured =
|
||||||
!program.llamapack && program.modelConfig.isConfigured();
|
program.openAiKey || process.env["OPENAI_API_KEY"];
|
||||||
// If using LlamaParse, require LlamaCloud API key
|
// If using LlamaParse, require LlamaCloud API key
|
||||||
const llamaCloudKeyConfigured = program.useLlamaParse
|
const llamaCloudKeyConfigured = program.useLlamaParse
|
||||||
? program.llamaCloudKey || process.env["LLAMA_CLOUD_API_KEY"]
|
? program.llamaCloudKey || process.env["LLAMA_CLOUD_API_KEY"]
|
||||||
@@ -268,9 +321,10 @@ export const askQuestions = async (
|
|||||||
// Can run the app if all tools do not require configuration
|
// Can run the app if all tools do not require configuration
|
||||||
if (
|
if (
|
||||||
!hasVectorDb &&
|
!hasVectorDb &&
|
||||||
modelConfigured &&
|
openAiKeyConfigured &&
|
||||||
llamaCloudKeyConfigured &&
|
llamaCloudKeyConfigured &&
|
||||||
!toolsRequireConfig(program.tools)
|
!toolsRequireConfig(program.tools) &&
|
||||||
|
!program.llamapack
|
||||||
) {
|
) {
|
||||||
actionChoices.push({
|
actionChoices.push({
|
||||||
title:
|
title:
|
||||||
@@ -287,7 +341,7 @@ export const askQuestions = async (
|
|||||||
choices: actionChoices,
|
choices: actionChoices,
|
||||||
initial: 1,
|
initial: 1,
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
|
|
||||||
program.postInstallAction = action;
|
program.postInstallAction = action;
|
||||||
@@ -320,7 +374,7 @@ export const askQuestions = async (
|
|||||||
],
|
],
|
||||||
initial: 0,
|
initial: 0,
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
program.template = template;
|
program.template = template;
|
||||||
preferences.template = template;
|
preferences.template = template;
|
||||||
@@ -343,7 +397,7 @@ export const askQuestions = async (
|
|||||||
})),
|
})),
|
||||||
initial: 0,
|
initial: 0,
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
const projectConfig = JSON.parse(communityProjectConfig);
|
const projectConfig = JSON.parse(communityProjectConfig);
|
||||||
program.communityProjectConfig = projectConfig;
|
program.communityProjectConfig = projectConfig;
|
||||||
@@ -364,7 +418,7 @@ export const askQuestions = async (
|
|||||||
})),
|
})),
|
||||||
initial: 0,
|
initial: 0,
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
program.llamapack = llamapack;
|
program.llamapack = llamapack;
|
||||||
preferences.llamapack = llamapack;
|
preferences.llamapack = llamapack;
|
||||||
@@ -390,7 +444,7 @@ export const askQuestions = async (
|
|||||||
choices,
|
choices,
|
||||||
initial: 0,
|
initial: 0,
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
program.framework = framework;
|
program.framework = framework;
|
||||||
preferences.framework = framework;
|
preferences.framework = framework;
|
||||||
@@ -399,6 +453,7 @@ export const askQuestions = async (
|
|||||||
|
|
||||||
if (program.framework === "express" || program.framework === "fastapi") {
|
if (program.framework === "express" || program.framework === "fastapi") {
|
||||||
// if a backend-only framework is selected, ask whether we should create a frontend
|
// if a backend-only framework is selected, ask whether we should create a frontend
|
||||||
|
// (only for streaming backends)
|
||||||
if (program.frontend === undefined) {
|
if (program.frontend === undefined) {
|
||||||
if (ciInfo.isCI) {
|
if (ciInfo.isCI) {
|
||||||
program.frontend = getPrefOrDefault("frontend");
|
program.frontend = getPrefOrDefault("frontend");
|
||||||
@@ -449,7 +504,7 @@ export const askQuestions = async (
|
|||||||
],
|
],
|
||||||
initial: 0,
|
initial: 0,
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
|
|
||||||
program.observability = observability;
|
program.observability = observability;
|
||||||
@@ -457,13 +512,67 @@ export const askQuestions = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!program.modelConfig) {
|
if (!program.openAiKey) {
|
||||||
const modelConfig = await askModelConfig({
|
const { key } = await prompts(
|
||||||
openAiKey,
|
{
|
||||||
askModels: program.askModels ?? false,
|
type: "text",
|
||||||
});
|
name: "key",
|
||||||
program.modelConfig = modelConfig;
|
message: program.askModels
|
||||||
preferences.modelConfig = modelConfig;
|
? "Please provide your OpenAI API key (or leave blank to reuse OPENAI_API_KEY env variable):"
|
||||||
|
: "Please provide your OpenAI API key (leave blank to skip):",
|
||||||
|
validate: (value: string) => {
|
||||||
|
if (program.askModels && !value) {
|
||||||
|
if (process.env.OPENAI_API_KEY) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return "OpenAI API key is required";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
);
|
||||||
|
|
||||||
|
program.openAiKey = key || process.env.OPENAI_API_KEY;
|
||||||
|
preferences.openAiKey = key || process.env.OPENAI_API_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!program.model) {
|
||||||
|
if (ciInfo.isCI || !program.askModels) {
|
||||||
|
program.model = defaults.model;
|
||||||
|
} else {
|
||||||
|
const { model } = await prompts(
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
name: "model",
|
||||||
|
message: "Which LLM model would you like to use?",
|
||||||
|
choices: await getAvailableModelChoices(false, program.openAiKey),
|
||||||
|
initial: 0,
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
);
|
||||||
|
program.model = model;
|
||||||
|
preferences.model = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!program.embeddingModel) {
|
||||||
|
if (ciInfo.isCI || !program.askModels) {
|
||||||
|
program.embeddingModel = defaults.embeddingModel;
|
||||||
|
} else {
|
||||||
|
const { embeddingModel } = await prompts(
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
name: "embeddingModel",
|
||||||
|
message: "Which embedding model would you like to use?",
|
||||||
|
choices: await getAvailableModelChoices(true, program.openAiKey),
|
||||||
|
initial: 0,
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
);
|
||||||
|
program.embeddingModel = embeddingModel;
|
||||||
|
preferences.embeddingModel = embeddingModel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!program.dataSources) {
|
if (!program.dataSources) {
|
||||||
@@ -487,7 +596,7 @@ export const askQuestions = async (
|
|||||||
),
|
),
|
||||||
initial: firstQuestion ? 1 : 0,
|
initial: firstQuestion ? 1 : 0,
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (selectedSource === "no" || selectedSource === "none") {
|
if (selectedSource === "no" || selectedSource === "none") {
|
||||||
@@ -533,7 +642,7 @@ export const askQuestions = async (
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
|
|
||||||
program.dataSources.push({
|
program.dataSources.push({
|
||||||
@@ -578,7 +687,7 @@ export const askQuestions = async (
|
|||||||
];
|
];
|
||||||
program.dataSources.push({
|
program.dataSources.push({
|
||||||
type: "db",
|
type: "db",
|
||||||
config: await prompts(dbPrompts, questionHandlers),
|
config: await prompts(dbPrompts, handlers),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -605,7 +714,7 @@ export const askQuestions = async (
|
|||||||
active: "yes",
|
active: "yes",
|
||||||
inactive: "no",
|
inactive: "no",
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
program.useLlamaParse = useLlamaParse;
|
program.useLlamaParse = useLlamaParse;
|
||||||
|
|
||||||
@@ -618,7 +727,7 @@ export const askQuestions = async (
|
|||||||
message:
|
message:
|
||||||
"Please provide your LlamaIndex Cloud API key (leave blank to skip):",
|
"Please provide your LlamaIndex Cloud API key (leave blank to skip):",
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
program.llamaCloudKey = llamaCloudKey;
|
program.llamaCloudKey = llamaCloudKey;
|
||||||
}
|
}
|
||||||
@@ -637,7 +746,7 @@ export const askQuestions = async (
|
|||||||
choices: getVectorDbChoices(program.framework),
|
choices: getVectorDbChoices(program.framework),
|
||||||
initial: 0,
|
initial: 0,
|
||||||
},
|
},
|
||||||
questionHandlers,
|
handlers,
|
||||||
);
|
);
|
||||||
program.vectorDb = vectorDb;
|
program.vectorDb = vectorDb;
|
||||||
preferences.vectorDb = vectorDb;
|
preferences.vectorDb = vectorDb;
|
||||||
@@ -672,7 +781,3 @@ export const askQuestions = async (
|
|||||||
|
|
||||||
await askPostInstallAction();
|
await askPostInstallAction();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toChoice = (value: string) => {
|
|
||||||
return { title: value, value };
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
from llama_index.core.tools.tool_spec.base import BaseToolSpec
|
||||||
|
from llama_index.core.tools.function_tool import FunctionTool
|
||||||
|
|
||||||
|
|
||||||
|
class ToolFactory:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_tool(tool_name: str, **kwargs) -> list[FunctionTool]:
|
||||||
|
try:
|
||||||
|
tool_package, tool_cls_name = tool_name.split(".")
|
||||||
|
module_name = f"llama_index.tools.{tool_package}"
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
tool_class = getattr(module, tool_cls_name)
|
||||||
|
tool_spec: BaseToolSpec = tool_class(**kwargs)
|
||||||
|
return tool_spec.to_tool_list()
|
||||||
|
except (ImportError, AttributeError) as e:
|
||||||
|
raise ValueError(f"Unsupported tool: {tool_name}") from e
|
||||||
|
except TypeError as e:
|
||||||
|
raise ValueError(
|
||||||
|
f"Could not create tool: {tool_name}. With config: {kwargs}"
|
||||||
|
) from e
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_env() -> list[FunctionTool]:
|
||||||
|
tools = []
|
||||||
|
if os.path.exists("config/tools.yaml"):
|
||||||
|
with open("config/tools.yaml", "r") as f:
|
||||||
|
tool_configs = yaml.safe_load(f)
|
||||||
|
for name, config in tool_configs.items():
|
||||||
|
tools += ToolFactory.create_tool(name, **config)
|
||||||
|
return tools
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import os
|
|
||||||
import yaml
|
|
||||||
import importlib
|
|
||||||
|
|
||||||
from llama_index.core.tools.tool_spec.base import BaseToolSpec
|
|
||||||
from llama_index.core.tools.function_tool import FunctionTool
|
|
||||||
|
|
||||||
|
|
||||||
class ToolType:
|
|
||||||
LLAMAHUB = "llamahub"
|
|
||||||
LOCAL = "local"
|
|
||||||
|
|
||||||
|
|
||||||
class ToolFactory:
|
|
||||||
|
|
||||||
TOOL_SOURCE_PACKAGE_MAP = {
|
|
||||||
ToolType.LLAMAHUB: "llama_index.tools",
|
|
||||||
ToolType.LOCAL: "app.engine.tools",
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def load_tools(tool_type: str, tool_name: str, config: dict) -> list[FunctionTool]:
|
|
||||||
source_package = ToolFactory.TOOL_SOURCE_PACKAGE_MAP[tool_type]
|
|
||||||
try:
|
|
||||||
if "ToolSpec" in tool_name:
|
|
||||||
tool_package, tool_cls_name = tool_name.split(".")
|
|
||||||
module_name = f"{source_package}.{tool_package}"
|
|
||||||
module = importlib.import_module(module_name)
|
|
||||||
tool_class = getattr(module, tool_cls_name)
|
|
||||||
tool_spec: BaseToolSpec = tool_class(**config)
|
|
||||||
return tool_spec.to_tool_list()
|
|
||||||
else:
|
|
||||||
module = importlib.import_module(f"{source_package}.{tool_name}")
|
|
||||||
tools = getattr(module, "tools")
|
|
||||||
if not all(isinstance(tool, FunctionTool) for tool in tools):
|
|
||||||
raise ValueError(
|
|
||||||
f"The module {module} does not contain valid tools"
|
|
||||||
)
|
|
||||||
return tools
|
|
||||||
except ImportError as e:
|
|
||||||
raise ValueError(f"Failed to import tool {tool_name}: {e}")
|
|
||||||
except AttributeError as e:
|
|
||||||
raise ValueError(f"Failed to load tool {tool_name}: {e}")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_env() -> list[FunctionTool]:
|
|
||||||
tools = []
|
|
||||||
if os.path.exists("config/tools.yaml"):
|
|
||||||
with open("config/tools.yaml", "r") as f:
|
|
||||||
tool_configs = yaml.safe_load(f)
|
|
||||||
for tool_type, config_entries in tool_configs.items():
|
|
||||||
for tool_name, config in config_entries.items():
|
|
||||||
tools.extend(
|
|
||||||
ToolFactory.load_tools(tool_type, tool_name, config)
|
|
||||||
)
|
|
||||||
return tools
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
import os
|
|
||||||
import logging
|
|
||||||
import base64
|
|
||||||
import uuid
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from typing import List, Tuple, Dict
|
|
||||||
from llama_index.core.tools import FunctionTool
|
|
||||||
from e2b_code_interpreter import CodeInterpreter
|
|
||||||
from e2b_code_interpreter.models import Logs
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class InterpreterExtraResult(BaseModel):
|
|
||||||
type: str
|
|
||||||
filename: str
|
|
||||||
url: str
|
|
||||||
|
|
||||||
|
|
||||||
class E2BToolOutput(BaseModel):
|
|
||||||
is_error: bool
|
|
||||||
logs: Logs
|
|
||||||
results: List[InterpreterExtraResult] = []
|
|
||||||
|
|
||||||
|
|
||||||
class E2BCodeInterpreter:
|
|
||||||
|
|
||||||
output_dir = "tool-output"
|
|
||||||
|
|
||||||
def __init__(self, api_key: str, filesever_url_prefix: str):
|
|
||||||
self.api_key = api_key
|
|
||||||
self.filesever_url_prefix = filesever_url_prefix
|
|
||||||
|
|
||||||
def get_output_path(self, filename: str) -> str:
|
|
||||||
# if output directory doesn't exist, create it
|
|
||||||
if not os.path.exists(self.output_dir):
|
|
||||||
os.makedirs(self.output_dir, exist_ok=True)
|
|
||||||
return os.path.join(self.output_dir, filename)
|
|
||||||
|
|
||||||
def save_to_disk(self, base64_data: str, ext: str) -> Dict:
|
|
||||||
filename = f"{uuid.uuid4()}.{ext}" # generate a unique filename
|
|
||||||
buffer = base64.b64decode(base64_data)
|
|
||||||
output_path = self.get_output_path(filename)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(output_path, "wb") as file:
|
|
||||||
file.write(buffer)
|
|
||||||
except IOError as e:
|
|
||||||
logger.error(f"Failed to write to file {output_path}: {str(e)}")
|
|
||||||
raise e
|
|
||||||
|
|
||||||
logger.info(f"Saved file to {output_path}")
|
|
||||||
|
|
||||||
return {
|
|
||||||
"outputPath": output_path,
|
|
||||||
"filename": filename,
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_file_url(self, filename: str) -> str:
|
|
||||||
return f"{self.filesever_url_prefix}/{self.output_dir}/{filename}"
|
|
||||||
|
|
||||||
def parse_result(self, result) -> List[InterpreterExtraResult]:
|
|
||||||
"""
|
|
||||||
The result could include multiple formats (e.g. png, svg, etc.) but encoded in base64
|
|
||||||
We save each result to disk and return saved file metadata (extension, filename, url)
|
|
||||||
"""
|
|
||||||
if not result:
|
|
||||||
return []
|
|
||||||
|
|
||||||
output = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
formats = result.formats()
|
|
||||||
base64_data_arr = [result[format] for format in formats]
|
|
||||||
|
|
||||||
for ext, base64_data in zip(formats, base64_data_arr):
|
|
||||||
if ext and base64_data:
|
|
||||||
result = self.save_to_disk(base64_data, ext)
|
|
||||||
filename = result["filename"]
|
|
||||||
output.append(
|
|
||||||
InterpreterExtraResult(
|
|
||||||
type=ext, filename=filename, url=self.get_file_url(filename)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except Exception as error:
|
|
||||||
logger.error("Error when saving data to disk", error)
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
def interpret(self, code: str) -> E2BToolOutput:
|
|
||||||
with CodeInterpreter(api_key=self.api_key) as interpreter:
|
|
||||||
logger.info(
|
|
||||||
f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}"
|
|
||||||
)
|
|
||||||
exec = interpreter.notebook.exec_cell(code)
|
|
||||||
|
|
||||||
if exec.error:
|
|
||||||
output = E2BToolOutput(is_error=True, logs=[exec.error])
|
|
||||||
else:
|
|
||||||
if len(exec.results) == 0:
|
|
||||||
output = E2BToolOutput(is_error=False, logs=exec.logs, results=[])
|
|
||||||
else:
|
|
||||||
results = self.parse_result(exec.results[0])
|
|
||||||
output = E2BToolOutput(
|
|
||||||
is_error=False, logs=exec.logs, results=results
|
|
||||||
)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def code_interpret(code: str) -> Dict:
|
|
||||||
"""
|
|
||||||
Execute python code in a Jupyter notebook cell and return any result, stdout, stderr, display_data, and error.
|
|
||||||
"""
|
|
||||||
api_key = os.getenv("E2B_API_KEY")
|
|
||||||
filesever_url_prefix = os.getenv("FILESERVER_URL_PREFIX")
|
|
||||||
if not api_key:
|
|
||||||
raise ValueError(
|
|
||||||
"E2B_API_KEY key is required to run code interpreter. Get it here: https://e2b.dev/docs/getting-started/api-key"
|
|
||||||
)
|
|
||||||
if not filesever_url_prefix:
|
|
||||||
raise ValueError(
|
|
||||||
"FILESERVER_URL_PREFIX is required to display file output from sandbox"
|
|
||||||
)
|
|
||||||
|
|
||||||
interpreter = E2BCodeInterpreter(
|
|
||||||
api_key=api_key, filesever_url_prefix=filesever_url_prefix
|
|
||||||
)
|
|
||||||
output = interpreter.interpret(code)
|
|
||||||
return output.dict()
|
|
||||||
|
|
||||||
|
|
||||||
# Specify as functions tools to be loaded by the ToolFactory
|
|
||||||
tools = [FunctionTool.from_defaults(code_interpret)]
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
"""Open Meteo weather map tool spec."""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import requests
|
|
||||||
import pytz
|
|
||||||
from llama_index.core.tools import FunctionTool
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class OpenMeteoWeather:
|
|
||||||
geo_api = "https://geocoding-api.open-meteo.com/v1"
|
|
||||||
weather_api = "https://api.open-meteo.com/v1"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_geo_location(cls, location: str) -> dict:
|
|
||||||
"""Get geo location from location name."""
|
|
||||||
params = {"name": location, "count": 10, "language": "en", "format": "json"}
|
|
||||||
response = requests.get(f"{cls.geo_api}/search", params=params)
|
|
||||||
if response.status_code != 200:
|
|
||||||
raise Exception(f"Failed to fetch geo location: {response.status_code}")
|
|
||||||
else:
|
|
||||||
data = response.json()
|
|
||||||
result = data["results"][0]
|
|
||||||
geo_location = {
|
|
||||||
"id": result["id"],
|
|
||||||
"name": result["name"],
|
|
||||||
"latitude": result["latitude"],
|
|
||||||
"longitude": result["longitude"],
|
|
||||||
}
|
|
||||||
return geo_location
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_weather_information(cls, location: str) -> dict:
|
|
||||||
"""Use this function to get the weather of any given location.
|
|
||||||
Note that the weather code should follow WMO Weather interpretation codes (WW):
|
|
||||||
0: Clear sky
|
|
||||||
1, 2, 3: Mainly clear, partly cloudy, and overcast
|
|
||||||
45, 48: Fog and depositing rime fog
|
|
||||||
51, 53, 55: Drizzle: Light, moderate, and dense intensity
|
|
||||||
56, 57: Freezing Drizzle: Light and dense intensity
|
|
||||||
61, 63, 65: Rain: Slight, moderate and heavy intensity
|
|
||||||
66, 67: Freezing Rain: Light and heavy intensity
|
|
||||||
71, 73, 75: Snow fall: Slight, moderate, and heavy intensity
|
|
||||||
77: Snow grains
|
|
||||||
80, 81, 82: Rain showers: Slight, moderate, and violent
|
|
||||||
85, 86: Snow showers slight and heavy
|
|
||||||
95: Thunderstorm: Slight or moderate
|
|
||||||
96, 99: Thunderstorm with slight and heavy hail
|
|
||||||
"""
|
|
||||||
logger.info(
|
|
||||||
f"Calling open-meteo api to get weather information of location: {location}"
|
|
||||||
)
|
|
||||||
geo_location = cls._get_geo_location(location)
|
|
||||||
timezone = pytz.timezone("UTC").zone
|
|
||||||
params = {
|
|
||||||
"latitude": geo_location["latitude"],
|
|
||||||
"longitude": geo_location["longitude"],
|
|
||||||
"current": "temperature_2m,weather_code",
|
|
||||||
"hourly": "temperature_2m,weather_code",
|
|
||||||
"daily": "weather_code",
|
|
||||||
"timezone": timezone,
|
|
||||||
}
|
|
||||||
response = requests.get(f"{cls.weather_api}/forecast", params=params)
|
|
||||||
if response.status_code != 200:
|
|
||||||
raise Exception(
|
|
||||||
f"Failed to fetch weather information: {response.status_code}"
|
|
||||||
)
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
|
|
||||||
tools = [FunctionTool.from_defaults(OpenMeteoWeather.get_weather_information)]
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
from app.engine.index import get_index
|
from app.engine.index import get_index
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
|
|
||||||
def get_chat_engine():
|
def get_chat_engine():
|
||||||
@@ -9,11 +8,8 @@ def get_chat_engine():
|
|||||||
|
|
||||||
index = get_index()
|
index = get_index()
|
||||||
if index is None:
|
if index is None:
|
||||||
raise HTTPException(
|
raise Exception(
|
||||||
status_code=500,
|
"StorageContext is empty - call 'poetry run generate' to generate the storage first"
|
||||||
detail=str(
|
|
||||||
"StorageContext is empty - call 'poetry run generate' to generate the storage first"
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return index.as_chat_engine(
|
return index.as_chat_engine(
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import { BaseToolWithCall, OpenAIAgent, QueryEngineTool } from "llamaindex";
|
import {
|
||||||
|
BaseTool,
|
||||||
|
OpenAIAgent,
|
||||||
|
QueryEngineTool,
|
||||||
|
Settings,
|
||||||
|
ToolFactory,
|
||||||
|
} from "llamaindex";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { getDataSource } from "./index";
|
import { getDataSource } from "./index";
|
||||||
import { STORAGE_CACHE_DIR } from "./shared";
|
import { STORAGE_CACHE_DIR } from "./shared";
|
||||||
import { createTools } from "./tools";
|
|
||||||
|
|
||||||
export async function createChatEngine() {
|
export async function createChatEngine() {
|
||||||
const tools: BaseToolWithCall[] = [];
|
let tools: BaseTool[] = [];
|
||||||
|
|
||||||
// Add a query engine tool if we have a data source
|
// Add a query engine tool if we have a data source
|
||||||
// Delete this code if you don't have a data source
|
// Delete this code if you don't have a data source
|
||||||
@@ -23,20 +28,17 @@ export async function createChatEngine() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const configFile = path.join("config", "tools.json");
|
|
||||||
let toolConfig: any;
|
|
||||||
try {
|
try {
|
||||||
// add tools from config file if it exists
|
// add tools from config file if it exists
|
||||||
toolConfig = JSON.parse(await fs.readFile(configFile, "utf8"));
|
const config = JSON.parse(
|
||||||
} catch (e) {
|
await fs.readFile(path.join("config", "tools.json"), "utf8"),
|
||||||
console.info(`Could not read ${configFile} file. Using no tools.`);
|
);
|
||||||
}
|
tools = tools.concat(await ToolFactory.createTools(config));
|
||||||
if (toolConfig) {
|
} catch {}
|
||||||
tools.push(...(await createTools(toolConfig)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OpenAIAgent({
|
return new OpenAIAgent({
|
||||||
tools,
|
tools,
|
||||||
systemPrompt: process.env.SYSTEM_PROMPT,
|
llm: Settings.llm,
|
||||||
|
verbose: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
import { BaseToolWithCall } from "llamaindex";
|
|
||||||
import { ToolsFactory } from "llamaindex/tools/ToolsFactory";
|
|
||||||
import { InterpreterTool, InterpreterToolParams } from "./interpreter";
|
|
||||||
import { WeatherTool, WeatherToolParams } from "./weather";
|
|
||||||
|
|
||||||
type ToolCreator = (config: unknown) => BaseToolWithCall;
|
|
||||||
|
|
||||||
export async function createTools(toolConfig: {
|
|
||||||
local: Record<string, unknown>;
|
|
||||||
llamahub: any;
|
|
||||||
}): Promise<BaseToolWithCall[]> {
|
|
||||||
// add local tools from the 'tools' folder (if configured)
|
|
||||||
const tools = createLocalTools(toolConfig.local);
|
|
||||||
// add tools from LlamaIndexTS (if configured)
|
|
||||||
tools.push(...(await ToolsFactory.createTools(toolConfig.llamahub)));
|
|
||||||
return tools;
|
|
||||||
}
|
|
||||||
|
|
||||||
const toolFactory: Record<string, ToolCreator> = {
|
|
||||||
weather: (config: unknown) => {
|
|
||||||
return new WeatherTool(config as WeatherToolParams);
|
|
||||||
},
|
|
||||||
interpreter: (config: unknown) => {
|
|
||||||
return new InterpreterTool(config as InterpreterToolParams);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function createLocalTools(
|
|
||||||
localConfig: Record<string, unknown>,
|
|
||||||
): BaseToolWithCall[] {
|
|
||||||
const tools: BaseToolWithCall[] = [];
|
|
||||||
|
|
||||||
Object.keys(localConfig).forEach((key) => {
|
|
||||||
if (key in toolFactory) {
|
|
||||||
const toolConfig = localConfig[key];
|
|
||||||
const tool = toolFactory[key](toolConfig);
|
|
||||||
tools.push(tool);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return tools;
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
import { CodeInterpreter, Logs, Result } from "@e2b/code-interpreter";
|
|
||||||
import type { JSONSchemaType } from "ajv";
|
|
||||||
import fs from "fs";
|
|
||||||
import { BaseTool, ToolMetadata } from "llamaindex";
|
|
||||||
import crypto from "node:crypto";
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
export type InterpreterParameter = {
|
|
||||||
code: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InterpreterToolParams = {
|
|
||||||
metadata?: ToolMetadata<JSONSchemaType<InterpreterParameter>>;
|
|
||||||
apiKey?: string;
|
|
||||||
fileServerURLPrefix?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InterpreterToolOuput = {
|
|
||||||
isError: boolean;
|
|
||||||
logs: Logs;
|
|
||||||
extraResult: InterpreterExtraResult[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type InterpreterExtraType =
|
|
||||||
| "html"
|
|
||||||
| "markdown"
|
|
||||||
| "svg"
|
|
||||||
| "png"
|
|
||||||
| "jpeg"
|
|
||||||
| "pdf"
|
|
||||||
| "latex"
|
|
||||||
| "json"
|
|
||||||
| "javascript";
|
|
||||||
|
|
||||||
export type InterpreterExtraResult = {
|
|
||||||
type: InterpreterExtraType;
|
|
||||||
filename: string;
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_META_DATA: ToolMetadata<JSONSchemaType<InterpreterParameter>> = {
|
|
||||||
name: "interpreter",
|
|
||||||
description:
|
|
||||||
"Execute python code in a Jupyter notebook cell and return any result, stdout, stderr, display_data, and error.",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
code: {
|
|
||||||
type: "string",
|
|
||||||
description: "The python code to execute in a single cell.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["code"],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export class InterpreterTool implements BaseTool<InterpreterParameter> {
|
|
||||||
private readonly outputDir = "tool-output";
|
|
||||||
private apiKey?: string;
|
|
||||||
private fileServerURLPrefix?: string;
|
|
||||||
metadata: ToolMetadata<JSONSchemaType<InterpreterParameter>>;
|
|
||||||
codeInterpreter?: CodeInterpreter;
|
|
||||||
|
|
||||||
constructor(params?: InterpreterToolParams) {
|
|
||||||
this.metadata = params?.metadata || DEFAULT_META_DATA;
|
|
||||||
this.apiKey = params?.apiKey || process.env.E2B_API_KEY;
|
|
||||||
this.fileServerURLPrefix =
|
|
||||||
params?.fileServerURLPrefix || process.env.FILESERVER_URL_PREFIX;
|
|
||||||
|
|
||||||
if (!this.apiKey) {
|
|
||||||
throw new Error(
|
|
||||||
"E2B_API_KEY key is required to run code interpreter. Get it here: https://e2b.dev/docs/getting-started/api-key",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!this.fileServerURLPrefix) {
|
|
||||||
throw new Error(
|
|
||||||
"FILESERVER_URL_PREFIX is required to display file output from sandbox",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async initInterpreter() {
|
|
||||||
if (!this.codeInterpreter) {
|
|
||||||
this.codeInterpreter = await CodeInterpreter.create({
|
|
||||||
apiKey: this.apiKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this.codeInterpreter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async codeInterpret(code: string): Promise<InterpreterToolOuput> {
|
|
||||||
console.log(
|
|
||||||
`\n${"=".repeat(50)}\n> Running following AI-generated code:\n${code}\n${"=".repeat(50)}`,
|
|
||||||
);
|
|
||||||
const interpreter = await this.initInterpreter();
|
|
||||||
const exec = await interpreter.notebook.execCell(code);
|
|
||||||
if (exec.error) console.error("[Code Interpreter error]", exec.error);
|
|
||||||
const extraResult = await this.getExtraResult(exec.results[0]);
|
|
||||||
const result: InterpreterToolOuput = {
|
|
||||||
isError: !!exec.error,
|
|
||||||
logs: exec.logs,
|
|
||||||
extraResult,
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async call(input: InterpreterParameter): Promise<InterpreterToolOuput> {
|
|
||||||
const result = await this.codeInterpret(input.code);
|
|
||||||
await this.codeInterpreter?.close();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getExtraResult(
|
|
||||||
res?: Result,
|
|
||||||
): Promise<InterpreterExtraResult[]> {
|
|
||||||
if (!res) return [];
|
|
||||||
const output: InterpreterExtraResult[] = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const formats = res.formats(); // formats available for the result. Eg: ['png', ...]
|
|
||||||
const base64DataArr = formats.map((f) => res[f as keyof Result]); // get base64 data for each format
|
|
||||||
|
|
||||||
// save base64 data to file and return the url
|
|
||||||
for (let i = 0; i < formats.length; i++) {
|
|
||||||
const ext = formats[i];
|
|
||||||
const base64Data = base64DataArr[i];
|
|
||||||
if (ext && base64Data) {
|
|
||||||
const { filename } = this.saveToDisk(base64Data, ext);
|
|
||||||
output.push({
|
|
||||||
type: ext as InterpreterExtraType,
|
|
||||||
filename,
|
|
||||||
url: this.getFileUrl(filename),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error when saving data to disk", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consider saving to cloud storage instead but it may cost more for you
|
|
||||||
// See: https://e2b.dev/docs/sandbox/api/filesystem#write-to-file
|
|
||||||
private saveToDisk(
|
|
||||||
base64Data: string,
|
|
||||||
ext: string,
|
|
||||||
): {
|
|
||||||
outputPath: string;
|
|
||||||
filename: string;
|
|
||||||
} {
|
|
||||||
const filename = `${crypto.randomUUID()}.${ext}`; // generate a unique filename
|
|
||||||
const buffer = Buffer.from(base64Data, "base64");
|
|
||||||
const outputPath = this.getOutputPath(filename);
|
|
||||||
fs.writeFileSync(outputPath, buffer);
|
|
||||||
console.log(`Saved file to ${outputPath}`);
|
|
||||||
return {
|
|
||||||
outputPath,
|
|
||||||
filename,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private getOutputPath(filename: string): string {
|
|
||||||
// if outputDir doesn't exist, create it
|
|
||||||
if (!fs.existsSync(this.outputDir)) {
|
|
||||||
fs.mkdirSync(this.outputDir, { recursive: true });
|
|
||||||
}
|
|
||||||
return path.join(this.outputDir, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFileUrl(filename: string): string {
|
|
||||||
return `${this.fileServerURLPrefix}/${this.outputDir}/${filename}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import type { JSONSchemaType } from "ajv";
|
|
||||||
import { BaseTool, ToolMetadata } from "llamaindex";
|
|
||||||
|
|
||||||
interface GeoLocation {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
latitude: number;
|
|
||||||
longitude: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WeatherParameter = {
|
|
||||||
location: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type WeatherToolParams = {
|
|
||||||
metadata?: ToolMetadata<JSONSchemaType<WeatherParameter>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_META_DATA: ToolMetadata<JSONSchemaType<WeatherParameter>> = {
|
|
||||||
name: "get_weather_information",
|
|
||||||
description: `
|
|
||||||
Use this function to get the weather of any given location.
|
|
||||||
Note that the weather code should follow WMO Weather interpretation codes (WW):
|
|
||||||
0: Clear sky
|
|
||||||
1, 2, 3: Mainly clear, partly cloudy, and overcast
|
|
||||||
45, 48: Fog and depositing rime fog
|
|
||||||
51, 53, 55: Drizzle: Light, moderate, and dense intensity
|
|
||||||
56, 57: Freezing Drizzle: Light and dense intensity
|
|
||||||
61, 63, 65: Rain: Slight, moderate and heavy intensity
|
|
||||||
66, 67: Freezing Rain: Light and heavy intensity
|
|
||||||
71, 73, 75: Snow fall: Slight, moderate, and heavy intensity
|
|
||||||
77: Snow grains
|
|
||||||
80, 81, 82: Rain showers: Slight, moderate, and violent
|
|
||||||
85, 86: Snow showers slight and heavy
|
|
||||||
95: Thunderstorm: Slight or moderate
|
|
||||||
96, 99: Thunderstorm with slight and heavy hail
|
|
||||||
`,
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
location: {
|
|
||||||
type: "string",
|
|
||||||
description: "The location to get the weather information",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["location"],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export class WeatherTool implements BaseTool<WeatherParameter> {
|
|
||||||
metadata: ToolMetadata<JSONSchemaType<WeatherParameter>>;
|
|
||||||
|
|
||||||
private getGeoLocation = async (location: string): Promise<GeoLocation> => {
|
|
||||||
const apiUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${location}&count=10&language=en&format=json`;
|
|
||||||
const response = await fetch(apiUrl);
|
|
||||||
const data = await response.json();
|
|
||||||
const { id, name, latitude, longitude } = data.results[0];
|
|
||||||
return { id, name, latitude, longitude };
|
|
||||||
};
|
|
||||||
|
|
||||||
private getWeatherByLocation = async (location: string) => {
|
|
||||||
console.log(
|
|
||||||
"Calling open-meteo api to get weather information of location:",
|
|
||||||
location,
|
|
||||||
);
|
|
||||||
const { latitude, longitude } = await this.getGeoLocation(location);
|
|
||||||
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
||||||
const apiUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,weather_code&hourly=temperature_2m,weather_code&daily=weather_code&timezone=${timezone}`;
|
|
||||||
const response = await fetch(apiUrl);
|
|
||||||
const data = await response.json();
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(params?: WeatherToolParams) {
|
|
||||||
this.metadata = params?.metadata || DEFAULT_META_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
async call(input: WeatherParameter) {
|
|
||||||
return await this.getWeatherByLocation(input.location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,13 +9,10 @@ export async function createChatEngine() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const retriever = index.asRetriever();
|
const retriever = index.asRetriever();
|
||||||
retriever.similarityTopK = process.env.TOP_K
|
retriever.similarityTopK = 3;
|
||||||
? parseInt(process.env.TOP_K)
|
|
||||||
: 3;
|
|
||||||
|
|
||||||
return new ContextChatEngine({
|
return new ContextChatEngine({
|
||||||
chatModel: Settings.llm,
|
chatModel: Settings.llm,
|
||||||
retriever,
|
retriever,
|
||||||
systemPrompt: process.env.SYSTEM_PROMPT,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
|
||||||
from llama_parse import LlamaParse
|
from llama_parse import LlamaParse
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, validator
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class FileLoaderConfig(BaseModel):
|
class FileLoaderConfig(BaseModel):
|
||||||
data_dir: str = "data"
|
data_dir: str = "data"
|
||||||
@@ -30,28 +27,11 @@ def llama_parse_parser():
|
|||||||
def get_file_documents(config: FileLoaderConfig):
|
def get_file_documents(config: FileLoaderConfig):
|
||||||
from llama_index.core.readers import SimpleDirectoryReader
|
from llama_index.core.readers import SimpleDirectoryReader
|
||||||
|
|
||||||
try:
|
reader = SimpleDirectoryReader(
|
||||||
reader = SimpleDirectoryReader(
|
config.data_dir,
|
||||||
config.data_dir,
|
recursive=True,
|
||||||
recursive=True,
|
)
|
||||||
filename_as_id=True,
|
if config.use_llama_parse:
|
||||||
)
|
parser = llama_parse_parser()
|
||||||
if config.use_llama_parse:
|
reader.file_extractor = {".pdf": parser}
|
||||||
parser = llama_parse_parser()
|
return reader.load_data()
|
||||||
reader.file_extractor = {".pdf": parser}
|
|
||||||
return reader.load_data()
|
|
||||||
except ValueError as e:
|
|
||||||
import sys, traceback
|
|
||||||
|
|
||||||
# Catch the error if the data dir is empty
|
|
||||||
# and return as empty document list
|
|
||||||
_, _, exc_traceback = sys.exc_info()
|
|
||||||
function_name = traceback.extract_tb(exc_traceback)[-1].name
|
|
||||||
if function_name == "_add_files":
|
|
||||||
logger.warning(
|
|
||||||
f"Failed to load file documents, error message: {e} . Return as empty document list."
|
|
||||||
)
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
# Raise the error if it is not the case of empty data dir
|
|
||||||
raise e
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from llama_index.core.storage import StorageContext
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.astra_db import AstraDBVectorStore
|
||||||
|
from app.settings import init_settings
|
||||||
|
from app.engine.loaders import get_documents
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_datasource():
|
||||||
|
init_settings()
|
||||||
|
logger.info("Creating new index")
|
||||||
|
documents = get_documents()
|
||||||
|
store = AstraDBVectorStore(
|
||||||
|
token=os.environ["ASTRA_DB_APPLICATION_TOKEN"],
|
||||||
|
api_endpoint=os.environ["ASTRA_DB_ENDPOINT"],
|
||||||
|
collection_name=os.environ["ASTRA_DB_COLLECTION"],
|
||||||
|
embedding_dimension=int(os.environ["EMBEDDING_DIM"]),
|
||||||
|
)
|
||||||
|
storage_context = StorageContext.from_defaults(vector_store=store)
|
||||||
|
VectorStoreIndex.from_documents(
|
||||||
|
documents,
|
||||||
|
storage_context=storage_context,
|
||||||
|
show_progress=True, # this will show you a progress bar as the embeddings are created
|
||||||
|
)
|
||||||
|
logger.info(f"Successfully created embeddings in the AstraDB")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_datasource()
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.astra_db import AstraDBVectorStore
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger("uvicorn")
|
||||||
|
|
||||||
|
|
||||||
|
def get_index():
|
||||||
|
logger.info("Connecting to index from AstraDB...")
|
||||||
|
store = AstraDBVectorStore(
|
||||||
|
token=os.environ["ASTRA_DB_APPLICATION_TOKEN"],
|
||||||
|
api_endpoint=os.environ["ASTRA_DB_ENDPOINT"],
|
||||||
|
collection_name=os.environ["ASTRA_DB_COLLECTION"],
|
||||||
|
embedding_dimension=int(os.environ["EMBEDDING_DIM"]),
|
||||||
|
)
|
||||||
|
index = VectorStoreIndex.from_vector_store(store)
|
||||||
|
logger.info("Finished connecting to index from AstraDB.")
|
||||||
|
return index
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import os
|
|
||||||
from llama_index.vector_stores.astra_db import AstraDBVectorStore
|
|
||||||
|
|
||||||
|
|
||||||
def get_vector_store():
|
|
||||||
endpoint = os.getenv("ASTRA_DB_ENDPOINT")
|
|
||||||
token = os.getenv("ASTRA_DB_APPLICATION_TOKEN")
|
|
||||||
collection = os.getenv("ASTRA_DB_COLLECTION")
|
|
||||||
if not endpoint or not token or not collection:
|
|
||||||
raise ValueError(
|
|
||||||
"Please config ASTRA_DB_ENDPOINT, ASTRA_DB_APPLICATION_TOKEN and ASTRA_DB_COLLECTION"
|
|
||||||
" to your environment variables or config them in the .env file"
|
|
||||||
)
|
|
||||||
store = AstraDBVectorStore(
|
|
||||||
token=token,
|
|
||||||
api_endpoint=endpoint,
|
|
||||||
collection_name=collection,
|
|
||||||
embedding_dimension=int(os.getenv("EMBEDDING_DIM")),
|
|
||||||
)
|
|
||||||
return store
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import os
|
|
||||||
from llama_index.vector_stores.chroma import ChromaVectorStore
|
|
||||||
|
|
||||||
|
|
||||||
def get_vector_store():
|
|
||||||
collection_name = os.getenv("CHROMA_COLLECTION", "default")
|
|
||||||
chroma_path = os.getenv("CHROMA_PATH")
|
|
||||||
# if CHROMA_PATH is set, use a local ChromaVectorStore from the path
|
|
||||||
# otherwise, use a remote ChromaVectorStore (ChromaDB Cloud is not supported yet)
|
|
||||||
if chroma_path:
|
|
||||||
store = ChromaVectorStore.from_params(
|
|
||||||
persist_dir=chroma_path, collection_name=collection_name
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if not os.getenv("CHROMA_HOST") or not os.getenv("CHROMA_PORT"):
|
|
||||||
raise ValueError(
|
|
||||||
"Please provide either CHROMA_PATH or CHROMA_HOST and CHROMA_PORT"
|
|
||||||
)
|
|
||||||
store = ChromaVectorStore.from_params(
|
|
||||||
host=os.getenv("CHROMA_HOST"),
|
|
||||||
port=int(os.getenv("CHROMA_PORT")),
|
|
||||||
collection_name=collection_name,
|
|
||||||
)
|
|
||||||
return store
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from llama_index.core.storage import StorageContext
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.milvus import MilvusVectorStore
|
||||||
|
from app.settings import init_settings
|
||||||
|
from app.engine.loaders import get_documents
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_datasource():
|
||||||
|
init_settings()
|
||||||
|
logger.info("Creating new index")
|
||||||
|
# load the documents and create the index
|
||||||
|
documents = get_documents()
|
||||||
|
store = MilvusVectorStore(
|
||||||
|
uri=os.environ["MILVUS_ADDRESS"],
|
||||||
|
user=os.getenv("MILVUS_USERNAME"),
|
||||||
|
password=os.getenv("MILVUS_PASSWORD"),
|
||||||
|
collection_name=os.getenv("MILVUS_COLLECTION"),
|
||||||
|
dim=int(os.getenv("EMBEDDING_DIM")),
|
||||||
|
)
|
||||||
|
storage_context = StorageContext.from_defaults(vector_store=store)
|
||||||
|
VectorStoreIndex.from_documents(
|
||||||
|
documents,
|
||||||
|
storage_context=storage_context,
|
||||||
|
show_progress=True, # this will show you a progress bar as the embeddings are created
|
||||||
|
)
|
||||||
|
logger.info(f"Successfully created embeddings in the Milvus")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_datasource()
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.milvus import MilvusVectorStore
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger("uvicorn")
|
||||||
|
|
||||||
|
|
||||||
|
def get_index():
|
||||||
|
logger.info("Connecting to index from Milvus...")
|
||||||
|
store = MilvusVectorStore(
|
||||||
|
uri=os.getenv("MILVUS_ADDRESS"),
|
||||||
|
user=os.getenv("MILVUS_USERNAME"),
|
||||||
|
password=os.getenv("MILVUS_PASSWORD"),
|
||||||
|
collection_name=os.getenv("MILVUS_COLLECTION"),
|
||||||
|
dim=int(os.getenv("EMBEDDING_DIM")),
|
||||||
|
)
|
||||||
|
index = VectorStoreIndex.from_vector_store(store)
|
||||||
|
logger.info("Finished connecting to index from Milvus.")
|
||||||
|
return index
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import os
|
|
||||||
from llama_index.vector_stores.milvus import MilvusVectorStore
|
|
||||||
|
|
||||||
|
|
||||||
def get_vector_store():
|
|
||||||
address = os.getenv("MILVUS_ADDRESS")
|
|
||||||
collection = os.getenv("MILVUS_COLLECTION")
|
|
||||||
if not address or not collection:
|
|
||||||
raise ValueError(
|
|
||||||
"Please set MILVUS_ADDRESS and MILVUS_COLLECTION to your environment variables"
|
|
||||||
" or config them in the .env file"
|
|
||||||
)
|
|
||||||
store = MilvusVectorStore(
|
|
||||||
uri=address,
|
|
||||||
user=os.getenv("MILVUS_USERNAME"),
|
|
||||||
password=os.getenv("MILVUS_PASSWORD"),
|
|
||||||
collection_name=collection,
|
|
||||||
dim=int(os.getenv("EMBEDDING_DIM")),
|
|
||||||
)
|
|
||||||
return store
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from llama_index.core.storage import StorageContext
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.mongodb import MongoDBAtlasVectorSearch
|
||||||
|
from app.settings import init_settings
|
||||||
|
from app.engine.loaders import get_documents
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_datasource():
|
||||||
|
init_settings()
|
||||||
|
logger.info("Creating new index")
|
||||||
|
# load the documents and create the index
|
||||||
|
documents = get_documents()
|
||||||
|
store = MongoDBAtlasVectorSearch(
|
||||||
|
db_name=os.environ["MONGODB_DATABASE"],
|
||||||
|
collection_name=os.environ["MONGODB_VECTORS"],
|
||||||
|
index_name=os.environ["MONGODB_VECTOR_INDEX"],
|
||||||
|
)
|
||||||
|
storage_context = StorageContext.from_defaults(vector_store=store)
|
||||||
|
VectorStoreIndex.from_documents(
|
||||||
|
documents,
|
||||||
|
storage_context=storage_context,
|
||||||
|
show_progress=True, # this will show you a progress bar as the embeddings are created
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Successfully created embeddings in the MongoDB collection {os.environ['MONGODB_VECTORS']}"
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"""IMPORTANT: You can't query your index yet because you need to create a vector search index in MongoDB's UI now.
|
||||||
|
See https://github.com/run-llama/mongodb-demo/tree/main?tab=readme-ov-file#create-a-vector-search-index"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_datasource()
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.mongodb import MongoDBAtlasVectorSearch
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger("uvicorn")
|
||||||
|
|
||||||
|
|
||||||
|
def get_index():
|
||||||
|
logger.info("Connecting to index from MongoDB...")
|
||||||
|
store = MongoDBAtlasVectorSearch(
|
||||||
|
db_name=os.environ["MONGODB_DATABASE"],
|
||||||
|
collection_name=os.environ["MONGODB_VECTORS"],
|
||||||
|
index_name=os.environ["MONGODB_VECTOR_INDEX"],
|
||||||
|
)
|
||||||
|
index = VectorStoreIndex.from_vector_store(store)
|
||||||
|
logger.info("Finished connecting to index from MongoDB.")
|
||||||
|
return index
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import os
|
|
||||||
from llama_index.vector_stores.mongodb import MongoDBAtlasVectorSearch
|
|
||||||
|
|
||||||
|
|
||||||
def get_vector_store():
|
|
||||||
db_uri = os.getenv("MONGODB_URI")
|
|
||||||
db_name = os.getenv("MONGODB_DATABASE")
|
|
||||||
collection_name = os.getenv("MONGODB_VECTORS")
|
|
||||||
index_name = os.getenv("MONGODB_VECTOR_INDEX")
|
|
||||||
if not db_uri or not db_name or not collection_name or not index_name:
|
|
||||||
raise ValueError(
|
|
||||||
"Please set MONGODB_URI, MONGODB_DATABASE, MONGODB_VECTORS, and MONGODB_VECTOR_INDEX"
|
|
||||||
" to your environment variables or config them in .env file"
|
|
||||||
)
|
|
||||||
store = MongoDBAtlasVectorSearch(
|
|
||||||
db_name=db_name,
|
|
||||||
collection_name=collection_name,
|
|
||||||
index_name=index_name,
|
|
||||||
)
|
|
||||||
return store
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
STORAGE_DIR = "storage" # directory to cache the generated index
|
||||||
@@ -2,11 +2,11 @@ from dotenv import load_dotenv
|
|||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
from llama_index.core.indices import (
|
from llama_index.core.indices import (
|
||||||
VectorStoreIndex,
|
VectorStoreIndex,
|
||||||
)
|
)
|
||||||
|
from app.engine.constants import STORAGE_DIR
|
||||||
from app.engine.loaders import get_documents
|
from app.engine.loaders import get_documents
|
||||||
from app.settings import init_settings
|
from app.settings import init_settings
|
||||||
|
|
||||||
@@ -18,15 +18,14 @@ logger = logging.getLogger()
|
|||||||
def generate_datasource():
|
def generate_datasource():
|
||||||
init_settings()
|
init_settings()
|
||||||
logger.info("Creating new index")
|
logger.info("Creating new index")
|
||||||
storage_dir = os.environ.get("STORAGE_DIR", "storage")
|
|
||||||
# load the documents and create the index
|
# load the documents and create the index
|
||||||
documents = get_documents()
|
documents = get_documents()
|
||||||
index = VectorStoreIndex.from_documents(
|
index = VectorStoreIndex.from_documents(
|
||||||
documents,
|
documents,
|
||||||
)
|
)
|
||||||
# store it for later
|
# store it for later
|
||||||
index.storage_context.persist(storage_dir)
|
index.storage_context.persist(STORAGE_DIR)
|
||||||
logger.info(f"Finished creating new index. Stored in {storage_dir}")
|
logger.info(f"Finished creating new index. Stored in {STORAGE_DIR}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,30 +1,20 @@
|
|||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
import os
|
||||||
|
|
||||||
from cachetools import cached, TTLCache
|
from app.engine.constants import STORAGE_DIR
|
||||||
from llama_index.core.storage import StorageContext
|
from llama_index.core.storage import StorageContext
|
||||||
from llama_index.core.indices import load_index_from_storage
|
from llama_index.core.indices import load_index_from_storage
|
||||||
|
|
||||||
logger = logging.getLogger("uvicorn")
|
logger = logging.getLogger("uvicorn")
|
||||||
|
|
||||||
|
|
||||||
@cached(
|
|
||||||
TTLCache(maxsize=10, ttl=timedelta(minutes=5).total_seconds()),
|
|
||||||
key=lambda *args, **kwargs: "global_storage_context",
|
|
||||||
)
|
|
||||||
def get_storage_context(persist_dir: str) -> StorageContext:
|
|
||||||
return StorageContext.from_defaults(persist_dir=persist_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def get_index():
|
def get_index():
|
||||||
storage_dir = os.getenv("STORAGE_DIR", "storage")
|
|
||||||
# check if storage already exists
|
# check if storage already exists
|
||||||
if not os.path.exists(storage_dir):
|
if not os.path.exists(STORAGE_DIR):
|
||||||
return None
|
return None
|
||||||
# load the existing index
|
# load the existing index
|
||||||
logger.info(f"Loading index from {storage_dir}...")
|
logger.info(f"Loading index from {STORAGE_DIR}...")
|
||||||
storage_context = get_storage_context(storage_dir)
|
storage_context = StorageContext.from_defaults(persist_dir=STORAGE_DIR)
|
||||||
index = load_index_from_storage(storage_context)
|
index = load_index_from_storage(storage_context)
|
||||||
logger.info(f"Finished loading index from {storage_dir}")
|
logger.info(f"Finished loading index from {STORAGE_DIR}")
|
||||||
return index
|
return index
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
PGVECTOR_SCHEMA = "public"
|
||||||
|
PGVECTOR_TABLE = "llamaindex_embedding"
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.core.storage import StorageContext
|
||||||
|
|
||||||
|
from app.engine.loaders import get_documents
|
||||||
|
from app.settings import init_settings
|
||||||
|
from app.engine.utils import init_pg_vector_store_from_env
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_datasource():
|
||||||
|
init_settings()
|
||||||
|
logger.info("Creating new index")
|
||||||
|
# load the documents and create the index
|
||||||
|
documents = get_documents()
|
||||||
|
store = init_pg_vector_store_from_env()
|
||||||
|
storage_context = StorageContext.from_defaults(vector_store=store)
|
||||||
|
VectorStoreIndex.from_documents(
|
||||||
|
documents,
|
||||||
|
storage_context=storage_context,
|
||||||
|
show_progress=True, # this will show you a progress bar as the embeddings are created
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Successfully created embeddings in the PG vector store, schema={store.schema_name} table={store.table_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_datasource()
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import logging
|
||||||
|
from llama_index.core.indices.vector_store import VectorStoreIndex
|
||||||
|
from app.engine.utils import init_pg_vector_store_from_env
|
||||||
|
|
||||||
|
logger = logging.getLogger("uvicorn")
|
||||||
|
|
||||||
|
|
||||||
|
def get_index():
|
||||||
|
logger.info("Connecting to index from PGVector...")
|
||||||
|
store = init_pg_vector_store_from_env()
|
||||||
|
index = VectorStoreIndex.from_vector_store(store)
|
||||||
|
logger.info("Finished connecting to index from PGVector.")
|
||||||
|
return index
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import os
|
||||||
|
from llama_index.vector_stores.postgres import PGVectorStore
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
from app.engine.constants import PGVECTOR_SCHEMA, PGVECTOR_TABLE
|
||||||
|
|
||||||
|
|
||||||
|
def init_pg_vector_store_from_env():
|
||||||
|
original_conn_string = os.environ.get("PG_CONNECTION_STRING")
|
||||||
|
if original_conn_string is None or original_conn_string == "":
|
||||||
|
raise ValueError("PG_CONNECTION_STRING environment variable is not set.")
|
||||||
|
|
||||||
|
# The PGVectorStore requires both two connection strings, one for psycopg2 and one for asyncpg
|
||||||
|
# Update the configured scheme with the psycopg2 and asyncpg schemes
|
||||||
|
original_scheme = urlparse(original_conn_string).scheme + "://"
|
||||||
|
conn_string = original_conn_string.replace(
|
||||||
|
original_scheme, "postgresql+psycopg2://"
|
||||||
|
)
|
||||||
|
async_conn_string = original_conn_string.replace(
|
||||||
|
original_scheme, "postgresql+asyncpg://"
|
||||||
|
)
|
||||||
|
|
||||||
|
return PGVectorStore(
|
||||||
|
connection_string=conn_string,
|
||||||
|
async_connection_string=async_conn_string,
|
||||||
|
schema_name=PGVECTOR_SCHEMA,
|
||||||
|
table_name=PGVECTOR_TABLE,
|
||||||
|
)
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import os
|
|
||||||
from llama_index.vector_stores.postgres import PGVectorStore
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
PGVECTOR_SCHEMA = "public"
|
|
||||||
PGVECTOR_TABLE = "llamaindex_embedding"
|
|
||||||
|
|
||||||
vector_store: PGVectorStore = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_vector_store():
|
|
||||||
global vector_store
|
|
||||||
|
|
||||||
if vector_store is None:
|
|
||||||
original_conn_string = os.environ.get("PG_CONNECTION_STRING")
|
|
||||||
if original_conn_string is None or original_conn_string == "":
|
|
||||||
raise ValueError("PG_CONNECTION_STRING environment variable is not set.")
|
|
||||||
|
|
||||||
# The PGVectorStore requires both two connection strings, one for psycopg2 and one for asyncpg
|
|
||||||
# Update the configured scheme with the psycopg2 and asyncpg schemes
|
|
||||||
original_scheme = urlparse(original_conn_string).scheme + "://"
|
|
||||||
conn_string = original_conn_string.replace(
|
|
||||||
original_scheme, "postgresql+psycopg2://"
|
|
||||||
)
|
|
||||||
async_conn_string = original_conn_string.replace(
|
|
||||||
original_scheme, "postgresql+asyncpg://"
|
|
||||||
)
|
|
||||||
|
|
||||||
vector_store = PGVectorStore(
|
|
||||||
connection_string=conn_string,
|
|
||||||
async_connection_string=async_conn_string,
|
|
||||||
schema_name=PGVECTOR_SCHEMA,
|
|
||||||
table_name=PGVECTOR_TABLE,
|
|
||||||
embed_dim=int(os.environ.get("EMBEDDING_DIM", 1024)),
|
|
||||||
)
|
|
||||||
|
|
||||||
return vector_store
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from llama_index.core.storage import StorageContext
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.pinecone import PineconeVectorStore
|
||||||
|
from app.settings import init_settings
|
||||||
|
from app.engine.loaders import get_documents
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_datasource():
|
||||||
|
init_settings()
|
||||||
|
logger.info("Creating new index")
|
||||||
|
# load the documents and create the index
|
||||||
|
documents = get_documents()
|
||||||
|
store = PineconeVectorStore(
|
||||||
|
api_key=os.environ["PINECONE_API_KEY"],
|
||||||
|
index_name=os.environ["PINECONE_INDEX_NAME"],
|
||||||
|
environment=os.environ["PINECONE_ENVIRONMENT"],
|
||||||
|
)
|
||||||
|
storage_context = StorageContext.from_defaults(vector_store=store)
|
||||||
|
VectorStoreIndex.from_documents(
|
||||||
|
documents,
|
||||||
|
storage_context=storage_context,
|
||||||
|
show_progress=True, # this will show you a progress bar as the embeddings are created
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Successfully created embeddings and save to your Pinecone index {os.environ['PINECONE_INDEX_NAME']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_datasource()
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.pinecone import PineconeVectorStore
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger("uvicorn")
|
||||||
|
|
||||||
|
|
||||||
|
def get_index():
|
||||||
|
logger.info("Connecting to index from Pinecone...")
|
||||||
|
store = PineconeVectorStore(
|
||||||
|
api_key=os.environ["PINECONE_API_KEY"],
|
||||||
|
index_name=os.environ["PINECONE_INDEX_NAME"],
|
||||||
|
environment=os.environ["PINECONE_ENVIRONMENT"],
|
||||||
|
)
|
||||||
|
index = VectorStoreIndex.from_vector_store(store)
|
||||||
|
logger.info("Finished connecting to index from Pinecone.")
|
||||||
|
return index
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import os
|
|
||||||
from llama_index.vector_stores.pinecone import PineconeVectorStore
|
|
||||||
|
|
||||||
|
|
||||||
def get_vector_store():
|
|
||||||
api_key = os.getenv("PINECONE_API_KEY")
|
|
||||||
index_name = os.getenv("PINECONE_INDEX_NAME")
|
|
||||||
environment = os.getenv("PINECONE_ENVIRONMENT")
|
|
||||||
if not api_key or not index_name or not environment:
|
|
||||||
raise ValueError(
|
|
||||||
"Please set PINECONE_API_KEY, PINECONE_INDEX_NAME, and PINECONE_ENVIRONMENT"
|
|
||||||
" to your environment variables or config them in the .env file"
|
|
||||||
)
|
|
||||||
store = PineconeVectorStore(
|
|
||||||
api_key=api_key,
|
|
||||||
index_name=index_name,
|
|
||||||
environment=environment,
|
|
||||||
)
|
|
||||||
return store
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from app.engine.loaders import get_documents
|
||||||
|
from app.settings import init_settings
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.core.storage import StorageContext
|
||||||
|
from llama_index.vector_stores.qdrant import QdrantVectorStore
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_datasource():
|
||||||
|
init_settings()
|
||||||
|
logger.info("Creating new index with Qdrant")
|
||||||
|
# load the documents and create the index
|
||||||
|
documents = get_documents()
|
||||||
|
store = QdrantVectorStore(
|
||||||
|
collection_name=os.getenv("QDRANT_COLLECTION"),
|
||||||
|
url=os.getenv("QDRANT_URL"),
|
||||||
|
api_key=os.getenv("QDRANT_API_KEY"),
|
||||||
|
)
|
||||||
|
storage_context = StorageContext.from_defaults(vector_store=store)
|
||||||
|
VectorStoreIndex.from_documents(
|
||||||
|
documents,
|
||||||
|
storage_context=storage_context,
|
||||||
|
show_progress=True, # this will show you a progress bar as the embeddings are created
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Successfully uploaded documents to the {os.getenv('QDRANT_COLLECTION')} collection."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_datasource()
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from llama_index.core.indices import VectorStoreIndex
|
||||||
|
from llama_index.vector_stores.qdrant import QdrantVectorStore
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger("uvicorn")
|
||||||
|
|
||||||
|
|
||||||
|
def get_index():
|
||||||
|
logger.info("Connecting to Qdrant collection..")
|
||||||
|
store = QdrantVectorStore(
|
||||||
|
collection_name=os.getenv("QDRANT_COLLECTION"),
|
||||||
|
url=os.getenv("QDRANT_URL"),
|
||||||
|
api_key=os.getenv("QDRANT_API_KEY"),
|
||||||
|
)
|
||||||
|
index = VectorStoreIndex.from_vector_store(store)
|
||||||
|
logger.info("Finished connecting to Qdrant collection.")
|
||||||
|
return index
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import os
|
|
||||||
from llama_index.vector_stores.qdrant import QdrantVectorStore
|
|
||||||
|
|
||||||
|
|
||||||
def get_vector_store():
|
|
||||||
collection_name = os.getenv("QDRANT_COLLECTION")
|
|
||||||
url = os.getenv("QDRANT_URL")
|
|
||||||
api_key = os.getenv("QDRANT_API_KEY")
|
|
||||||
if not collection_name or not url:
|
|
||||||
raise ValueError(
|
|
||||||
"Please set QDRANT_COLLECTION, QDRANT_URL"
|
|
||||||
" to your environment variables or config them in the .env file"
|
|
||||||
)
|
|
||||||
store = QdrantVectorStore(
|
|
||||||
collection_name=collection_name,
|
|
||||||
url=url,
|
|
||||||
api_key=api_key,
|
|
||||||
)
|
|
||||||
return store
|
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
|
import {
|
||||||
import { AstraDBVectorStore } from "llamaindex/storage/vectorStore/AstraDBVectorStore";
|
AstraDBVectorStore,
|
||||||
|
VectorStoreIndex,
|
||||||
|
storageContextFromDefaults,
|
||||||
|
} from "llamaindex";
|
||||||
import { getDocuments } from "./loader";
|
import { getDocuments } from "./loader";
|
||||||
import { initSettings } from "./settings";
|
import { initSettings } from "./settings";
|
||||||
import { checkRequiredEnvVars } from "./shared";
|
import { checkRequiredEnvVars } from "./shared";
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import { VectorStoreIndex } from "llamaindex";
|
import { AstraDBVectorStore, VectorStoreIndex } from "llamaindex";
|
||||||
import { AstraDBVectorStore } from "llamaindex/storage/vectorStore/AstraDBVectorStore";
|
|
||||||
import { checkRequiredEnvVars } from "./shared";
|
import { checkRequiredEnvVars } from "./shared";
|
||||||
|
|
||||||
export async function getDataSource() {
|
export async function getDataSource() {
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
|
||||||
import * as dotenv from "dotenv";
|
|
||||||
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
|
|
||||||
import { ChromaVectorStore } from "llamaindex/storage/vectorStore/ChromaVectorStore";
|
|
||||||
import { getDocuments } from "./loader";
|
|
||||||
import { initSettings } from "./settings";
|
|
||||||
import { checkRequiredEnvVars } from "./shared";
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
async function loadAndIndex() {
|
|
||||||
// load objects from storage and convert them into LlamaIndex Document objects
|
|
||||||
const documents = await getDocuments();
|
|
||||||
|
|
||||||
// create vector store
|
|
||||||
const chromaUri = `http://${process.env.CHROMA_HOST}:${process.env.CHROMA_PORT}`;
|
|
||||||
|
|
||||||
const vectorStore = new ChromaVectorStore({
|
|
||||||
collectionName: process.env.CHROMA_COLLECTION,
|
|
||||||
chromaClientParams: { path: chromaUri },
|
|
||||||
});
|
|
||||||
|
|
||||||
// create index from all the Documentss and store them in Pinecone
|
|
||||||
console.log("Start creating embeddings...");
|
|
||||||
const storageContext = await storageContextFromDefaults({ vectorStore });
|
|
||||||
await VectorStoreIndex.fromDocuments(documents, { storageContext });
|
|
||||||
console.log(
|
|
||||||
"Successfully created embeddings and save to your ChromaDB index.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
checkRequiredEnvVars();
|
|
||||||
initSettings();
|
|
||||||
await loadAndIndex();
|
|
||||||
console.log("Finished generating storage.");
|
|
||||||
})();
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
|
||||||
import { VectorStoreIndex } from "llamaindex";
|
|
||||||
import { ChromaVectorStore } from "llamaindex/storage/vectorStore/ChromaVectorStore";
|
|
||||||
import { checkRequiredEnvVars } from "./shared";
|
|
||||||
|
|
||||||
export async function getDataSource() {
|
|
||||||
checkRequiredEnvVars();
|
|
||||||
const chromaUri = `http://${process.env.CHROMA_HOST}:${process.env.CHROMA_PORT}`;
|
|
||||||
|
|
||||||
const store = new ChromaVectorStore({
|
|
||||||
collectionName: process.env.CHROMA_COLLECTION,
|
|
||||||
chromaClientParams: { path: chromaUri },
|
|
||||||
});
|
|
||||||
|
|
||||||
return await VectorStoreIndex.fromVectorStore(store);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
const REQUIRED_ENV_VARS = ["CHROMA_COLLECTION", "CHROMA_HOST", "CHROMA_PORT"];
|
|
||||||
|
|
||||||
export function checkRequiredEnvVars() {
|
|
||||||
const missingEnvVars = REQUIRED_ENV_VARS.filter((envVar) => {
|
|
||||||
return !process.env[envVar];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (missingEnvVars.length > 0) {
|
|
||||||
console.log(
|
|
||||||
`The following environment variables are required but missing: ${missingEnvVars.join(
|
|
||||||
", ",
|
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
throw new Error(
|
|
||||||
`Missing environment variables: ${missingEnvVars.join(", ")}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
|
import {
|
||||||
import { MilvusVectorStore } from "llamaindex/storage/vectorStore/MilvusVectorStore";
|
MilvusVectorStore,
|
||||||
|
VectorStoreIndex,
|
||||||
|
storageContextFromDefaults,
|
||||||
|
} from "llamaindex";
|
||||||
import { getDocuments } from "./loader";
|
import { getDocuments } from "./loader";
|
||||||
import { initSettings } from "./settings";
|
import { initSettings } from "./settings";
|
||||||
import { checkRequiredEnvVars, getMilvusClient } from "./shared";
|
import { checkRequiredEnvVars, getMilvusClient } from "./shared";
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { VectorStoreIndex } from "llamaindex";
|
import { MilvusVectorStore, VectorStoreIndex } from "llamaindex";
|
||||||
import { MilvusVectorStore } from "llamaindex/storage/vectorStore/MilvusVectorStore";
|
|
||||||
import { checkRequiredEnvVars, getMilvusClient } from "./shared";
|
import { checkRequiredEnvVars, getMilvusClient } from "./shared";
|
||||||
|
|
||||||
export async function getDataSource() {
|
export async function getDataSource() {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
|
import {
|
||||||
import { MongoDBAtlasVectorSearch } from "llamaindex/storage/vectorStore/MongoDBAtlasVectorSearch";
|
MongoDBAtlasVectorSearch,
|
||||||
|
VectorStoreIndex,
|
||||||
|
storageContextFromDefaults,
|
||||||
|
} from "llamaindex";
|
||||||
import { MongoClient } from "mongodb";
|
import { MongoClient } from "mongodb";
|
||||||
import { getDocuments } from "./loader";
|
import { getDocuments } from "./loader";
|
||||||
import { initSettings } from "./settings";
|
import { initSettings } from "./settings";
|
||||||
@@ -9,7 +12,7 @@ import { checkRequiredEnvVars } from "./shared";
|
|||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const mongoUri = process.env.MONGODB_URI!;
|
const mongoUri = process.env.MONGO_URI!;
|
||||||
const databaseName = process.env.MONGODB_DATABASE!;
|
const databaseName = process.env.MONGODB_DATABASE!;
|
||||||
const vectorCollectionName = process.env.MONGODB_VECTORS!;
|
const vectorCollectionName = process.env.MONGODB_VECTORS!;
|
||||||
const indexName = process.env.MONGODB_VECTOR_INDEX;
|
const indexName = process.env.MONGODB_VECTOR_INDEX;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import { VectorStoreIndex } from "llamaindex";
|
import { MongoDBAtlasVectorSearch, VectorStoreIndex } from "llamaindex";
|
||||||
import { MongoDBAtlasVectorSearch } from "llamaindex/storage/vectorStore/MongoDBAtlasVectorSearch";
|
|
||||||
import { MongoClient } from "mongodb";
|
import { MongoClient } from "mongodb";
|
||||||
import { checkRequiredEnvVars } from "./shared";
|
import { checkRequiredEnvVars } from "./shared";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const REQUIRED_ENV_VARS = [
|
const REQUIRED_ENV_VARS = [
|
||||||
"MONGODB_URI",
|
"MONGO_URI",
|
||||||
"MONGODB_DATABASE",
|
"MONGODB_DATABASE",
|
||||||
"MONGODB_VECTORS",
|
"MONGODB_VECTORS",
|
||||||
"MONGODB_VECTOR_INDEX",
|
"MONGODB_VECTOR_INDEX",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { VectorStoreIndex } from "llamaindex";
|
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
|
||||||
import { storageContextFromDefaults } from "llamaindex/storage/StorageContext";
|
|
||||||
|
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { SimpleDocumentStore, VectorStoreIndex } from "llamaindex";
|
import {
|
||||||
import { storageContextFromDefaults } from "llamaindex/storage/StorageContext";
|
SimpleDocumentStore,
|
||||||
|
storageContextFromDefaults,
|
||||||
|
VectorStoreIndex,
|
||||||
|
} from "llamaindex";
|
||||||
import { STORAGE_CACHE_DIR } from "./shared";
|
import { STORAGE_CACHE_DIR } from "./shared";
|
||||||
|
|
||||||
export async function getDataSource() {
|
export async function getDataSource() {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
|
import {
|
||||||
import { PGVectorStore } from "llamaindex/storage/vectorStore/PGVectorStore";
|
PGVectorStore,
|
||||||
|
VectorStoreIndex,
|
||||||
|
storageContextFromDefaults,
|
||||||
|
} from "llamaindex";
|
||||||
import { getDocuments } from "./loader";
|
import { getDocuments } from "./loader";
|
||||||
import { initSettings } from "./settings";
|
import { initSettings } from "./settings";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import { VectorStoreIndex } from "llamaindex";
|
import { PGVectorStore, VectorStoreIndex } from "llamaindex";
|
||||||
import { PGVectorStore } from "llamaindex/storage/vectorStore/PGVectorStore";
|
|
||||||
import {
|
import {
|
||||||
PGVECTOR_SCHEMA,
|
PGVECTOR_SCHEMA,
|
||||||
PGVECTOR_TABLE,
|
PGVECTOR_TABLE,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export const PGVECTOR_COLLECTION = "data";
|
|||||||
export const PGVECTOR_SCHEMA = "public";
|
export const PGVECTOR_SCHEMA = "public";
|
||||||
export const PGVECTOR_TABLE = "llamaindex_embedding";
|
export const PGVECTOR_TABLE = "llamaindex_embedding";
|
||||||
|
|
||||||
const REQUIRED_ENV_VARS = ["PG_CONNECTION_STRING"];
|
const REQUIRED_ENV_VARS = ["PG_CONNECTION_STRING", "OPENAI_API_KEY"];
|
||||||
|
|
||||||
export function checkRequiredEnvVars() {
|
export function checkRequiredEnvVars() {
|
||||||
const missingEnvVars = REQUIRED_ENV_VARS.filter((envVar) => {
|
const missingEnvVars = REQUIRED_ENV_VARS.filter((envVar) => {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
|
import {
|
||||||
import { PineconeVectorStore } from "llamaindex/storage/vectorStore/PineconeVectorStore";
|
PineconeVectorStore,
|
||||||
|
VectorStoreIndex,
|
||||||
|
storageContextFromDefaults,
|
||||||
|
} from "llamaindex";
|
||||||
import { getDocuments } from "./loader";
|
import { getDocuments } from "./loader";
|
||||||
import { initSettings } from "./settings";
|
import { initSettings } from "./settings";
|
||||||
import { checkRequiredEnvVars } from "./shared";
|
import { checkRequiredEnvVars } from "./shared";
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import { VectorStoreIndex } from "llamaindex";
|
import { PineconeVectorStore, VectorStoreIndex } from "llamaindex";
|
||||||
import { PineconeVectorStore } from "llamaindex/storage/vectorStore/PineconeVectorStore";
|
|
||||||
import { checkRequiredEnvVars } from "./shared";
|
import { checkRequiredEnvVars } from "./shared";
|
||||||
|
|
||||||
export async function getDataSource() {
|
export async function getDataSource() {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
/* eslint-disable turbo/no-undeclared-env-vars */
|
/* eslint-disable turbo/no-undeclared-env-vars */
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import { VectorStoreIndex, storageContextFromDefaults } from "llamaindex";
|
import {
|
||||||
import { QdrantVectorStore } from "llamaindex/storage/vectorStore/QdrantVectorStore";
|
QdrantVectorStore,
|
||||||
|
VectorStoreIndex,
|
||||||
|
storageContextFromDefaults,
|
||||||
|
} from "llamaindex";
|
||||||
import { getDocuments } from "./loader";
|
import { getDocuments } from "./loader";
|
||||||
import { initSettings } from "./settings";
|
import { initSettings } from "./settings";
|
||||||
import { checkRequiredEnvVars, getQdrantClient } from "./shared";
|
import { checkRequiredEnvVars, getQdrantClient } from "./shared";
|
||||||
@@ -15,10 +18,7 @@ async function loadAndIndex() {
|
|||||||
const documents = await getDocuments();
|
const documents = await getDocuments();
|
||||||
|
|
||||||
// Connect to Qdrant
|
// Connect to Qdrant
|
||||||
const vectorStore = new QdrantVectorStore({
|
const vectorStore = new QdrantVectorStore(collectionName, getQdrantClient());
|
||||||
collectionName,
|
|
||||||
client: getQdrantClient(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const storageContext = await storageContextFromDefaults({ vectorStore });
|
const storageContext = await storageContextFromDefaults({ vectorStore });
|
||||||
await VectorStoreIndex.fromDocuments(documents, {
|
await VectorStoreIndex.fromDocuments(documents, {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import { VectorStoreIndex } from "llamaindex";
|
import { QdrantVectorStore, VectorStoreIndex } from "llamaindex";
|
||||||
import { QdrantVectorStore } from "llamaindex/storage/vectorStore/QdrantVectorStore";
|
|
||||||
import { checkRequiredEnvVars, getQdrantClient } from "./shared";
|
import { checkRequiredEnvVars, getQdrantClient } from "./shared";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
@@ -8,10 +7,7 @@ dotenv.config();
|
|||||||
export async function getDataSource() {
|
export async function getDataSource() {
|
||||||
checkRequiredEnvVars();
|
checkRequiredEnvVars();
|
||||||
const collectionName = process.env.QDRANT_COLLECTION;
|
const collectionName = process.env.QDRANT_COLLECTION;
|
||||||
const store = new QdrantVectorStore({
|
const store = new QdrantVectorStore(collectionName, getQdrantClient());
|
||||||
collectionName,
|
|
||||||
client: getQdrantClient(),
|
|
||||||
});
|
|
||||||
|
|
||||||
return await VectorStoreIndex.fromVectorStore(store);
|
return await VectorStoreIndex.fromVectorStore(store);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# local env files
|
# local env files
|
||||||
.env
|
.env
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
tool-output/
|
|
||||||
@@ -31,8 +31,6 @@ if (isDevelopment) {
|
|||||||
console.warn("Production CORS origin not set, defaulting to no CORS.");
|
console.warn("Production CORS origin not set, defaulting to no CORS.");
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use("/api/files/data", express.static("data"));
|
|
||||||
app.use("/api/files/tool-output", express.static("tool-output"));
|
|
||||||
app.use(express.text());
|
app.use(express.text());
|
||||||
|
|
||||||
app.get("/", (req: Request, res: Response) => {
|
app.get("/", (req: Request, res: Response) => {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
node-linker=hoisted
|
|
||||||
@@ -1,23 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "llama-index-express-streaming",
|
"name": "llama-index-express-streaming",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.mjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --ignore-unknown --cache --check .",
|
"format": "prettier --ignore-unknown --cache --check .",
|
||||||
"format:write": "prettier --ignore-unknown --write .",
|
"format:write": "prettier --ignore-unknown --write .",
|
||||||
"build": "tsup index.ts --format cjs --dts",
|
"build": "tsup index.ts --format esm --dts",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.mjs",
|
||||||
"dev": "concurrently \"tsup index.ts --format cjs --dts --watch\" \"nodemon -q dist/index.js\""
|
"dev": "concurrently \"tsup index.ts --format esm --dts --watch\" \"nodemon -q dist/index.mjs\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ai": "^3.0.21",
|
"ai": "^2.2.25",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"llamaindex": "0.3.13",
|
"llamaindex": "latest"
|
||||||
"pdf2json": "3.0.5",
|
|
||||||
"ajv": "^8.12.0",
|
|
||||||
"@e2b/code-interpreter": "^0.0.5"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.16",
|
"@types/cors": "^2.8.16",
|
||||||
@@ -25,12 +22,12 @@
|
|||||||
"@types/node": "^20.9.5",
|
"@types/node": "^20.9.5",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.54.0",
|
||||||
"eslint-config-prettier": "^8.10.0",
|
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
|
"tsup": "^8.0.1",
|
||||||
|
"typescript": "^5.3.2",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^3.2.4",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"tsx": "^4.7.2",
|
"eslint-config-prettier": "^8.10.0",
|
||||||
"tsup": "^8.0.1",
|
"ts-node": "^10.9.2"
|
||||||
"typescript": "^5.3.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export const chatRequest = async (req: Request, res: Response) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[LlamaIndex]", error);
|
console.error("[LlamaIndex]", error);
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
detail: (error as Error).message,
|
error: (error as Error).message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { Message, StreamData, streamToResponse } from "ai";
|
import { streamToResponse } from "ai";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { ChatMessage, MessageContent, Settings } from "llamaindex";
|
import { ChatMessage, MessageContent } from "llamaindex";
|
||||||
import { createChatEngine } from "./engine/chat";
|
import { createChatEngine } from "./engine/chat";
|
||||||
import { LlamaIndexStream } from "./llamaindex-stream";
|
import { LlamaIndexStream } from "./llamaindex-stream";
|
||||||
import { createCallbackManager } from "./stream-helper";
|
|
||||||
|
|
||||||
const convertMessageContent = (
|
const convertMessageContent = (
|
||||||
textMessage: string,
|
textMessage: string,
|
||||||
@@ -26,7 +25,7 @@ const convertMessageContent = (
|
|||||||
|
|
||||||
export const chat = async (req: Request, res: Response) => {
|
export const chat = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { messages, data }: { messages: Message[]; data: any } = req.body;
|
const { messages, data }: { messages: ChatMessage[]; data: any } = req.body;
|
||||||
const userMessage = messages.pop();
|
const userMessage = messages.pop();
|
||||||
if (!messages || !userMessage || userMessage.role !== "user") {
|
if (!messages || !userMessage || userMessage.role !== "user") {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@@ -43,33 +42,36 @@ export const chat = async (req: Request, res: Response) => {
|
|||||||
data?.imageUrl,
|
data?.imageUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Init Vercel AI StreamData
|
|
||||||
const vercelStreamData = new StreamData();
|
|
||||||
|
|
||||||
// Setup callbacks
|
|
||||||
const callbackManager = createCallbackManager(vercelStreamData);
|
|
||||||
|
|
||||||
// Calling LlamaIndex's ChatEngine to get a streamed response
|
// Calling LlamaIndex's ChatEngine to get a streamed response
|
||||||
const response = await Settings.withCallbackManager(callbackManager, () => {
|
const response = await chatEngine.chat({
|
||||||
return chatEngine.chat({
|
message: userMessageContent,
|
||||||
message: userMessageContent,
|
chatHistory: messages,
|
||||||
chatHistory: messages as ChatMessage[],
|
stream: true,
|
||||||
stream: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return a stream, which can be consumed by the Vercel/AI client
|
// Return a stream, which can be consumed by the Vercel/AI client
|
||||||
const stream = LlamaIndexStream(response, vercelStreamData, {
|
const { stream, data: streamData } = LlamaIndexStream(response, {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
image_url: data?.imageUrl,
|
image_url: data?.imageUrl,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return streamToResponse(stream, res, {}, vercelStreamData);
|
// Pipe LlamaIndexStream to response
|
||||||
|
const processedStream = stream.pipeThrough(streamData.stream);
|
||||||
|
return streamToResponse(processedStream, res, {
|
||||||
|
headers: {
|
||||||
|
// response MUST have the `X-Experimental-Stream-Data: 'true'` header
|
||||||
|
// so that the client uses the correct parsing logic, see
|
||||||
|
// https://sdk.vercel.ai/docs/api-reference/stream-data#on-the-server
|
||||||
|
"X-Experimental-Stream-Data": "true",
|
||||||
|
"Content-Type": "text/plain; charset=utf-8",
|
||||||
|
"Access-Control-Expose-Headers": "X-Experimental-Stream-Data",
|
||||||
|
},
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[LlamaIndex]", error);
|
console.error("[LlamaIndex]", error);
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
detail: (error as Error).message,
|
error: (error as Error).message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,93 +1,19 @@
|
|||||||
import {
|
import { OpenAI, OpenAIEmbedding, Settings } from "llamaindex";
|
||||||
Anthropic,
|
|
||||||
GEMINI_EMBEDDING_MODEL,
|
|
||||||
GEMINI_MODEL,
|
|
||||||
Gemini,
|
|
||||||
GeminiEmbedding,
|
|
||||||
OpenAI,
|
|
||||||
OpenAIEmbedding,
|
|
||||||
Settings,
|
|
||||||
} from "llamaindex";
|
|
||||||
import { HuggingFaceEmbedding } from "llamaindex/embeddings/HuggingFaceEmbedding";
|
|
||||||
import { OllamaEmbedding } from "llamaindex/embeddings/OllamaEmbedding";
|
|
||||||
import { ALL_AVAILABLE_ANTHROPIC_MODELS } from "llamaindex/llm/anthropic";
|
|
||||||
import { Ollama } from "llamaindex/llm/ollama";
|
|
||||||
|
|
||||||
const CHUNK_SIZE = 512;
|
const CHUNK_SIZE = 512;
|
||||||
const CHUNK_OVERLAP = 20;
|
const CHUNK_OVERLAP = 20;
|
||||||
|
|
||||||
export const initSettings = async () => {
|
export const initSettings = async () => {
|
||||||
// HINT: you can delete the initialization code for unused model providers
|
|
||||||
console.log(`Using '${process.env.MODEL_PROVIDER}' model provider`);
|
|
||||||
|
|
||||||
if (!process.env.MODEL || !process.env.EMBEDDING_MODEL) {
|
|
||||||
throw new Error("'MODEL' and 'EMBEDDING_MODEL' env variables must be set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (process.env.MODEL_PROVIDER) {
|
|
||||||
case "ollama":
|
|
||||||
initOllama();
|
|
||||||
break;
|
|
||||||
case "anthropic":
|
|
||||||
initAnthropic();
|
|
||||||
break;
|
|
||||||
case "gemini":
|
|
||||||
initGemini();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
initOpenAI();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Settings.chunkSize = CHUNK_SIZE;
|
|
||||||
Settings.chunkOverlap = CHUNK_OVERLAP;
|
|
||||||
};
|
|
||||||
|
|
||||||
function initOpenAI() {
|
|
||||||
Settings.llm = new OpenAI({
|
Settings.llm = new OpenAI({
|
||||||
model: process.env.MODEL ?? "gpt-3.5-turbo",
|
model: process.env.MODEL ?? "gpt-3.5-turbo",
|
||||||
maxTokens: 512,
|
maxTokens: 512,
|
||||||
});
|
});
|
||||||
|
Settings.chunkSize = CHUNK_SIZE;
|
||||||
|
Settings.chunkOverlap = CHUNK_OVERLAP;
|
||||||
Settings.embedModel = new OpenAIEmbedding({
|
Settings.embedModel = new OpenAIEmbedding({
|
||||||
model: process.env.EMBEDDING_MODEL,
|
model: process.env.EMBEDDING_MODEL,
|
||||||
dimensions: process.env.EMBEDDING_DIM
|
dimensions: process.env.EMBEDDING_DIM
|
||||||
? parseInt(process.env.EMBEDDING_DIM)
|
? parseInt(process.env.EMBEDDING_DIM)
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
function initOllama() {
|
|
||||||
const config = {
|
|
||||||
host: process.env.OLLAMA_BASE_URL ?? "http://127.0.0.1:11434",
|
|
||||||
};
|
|
||||||
|
|
||||||
Settings.llm = new Ollama({
|
|
||||||
model: process.env.MODEL ?? "",
|
|
||||||
config,
|
|
||||||
});
|
|
||||||
Settings.embedModel = new OllamaEmbedding({
|
|
||||||
model: process.env.EMBEDDING_MODEL ?? "",
|
|
||||||
config,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initAnthropic() {
|
|
||||||
const embedModelMap: Record<string, string> = {
|
|
||||||
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
|
|
||||||
"all-mpnet-base-v2": "Xenova/all-mpnet-base-v2",
|
|
||||||
};
|
|
||||||
Settings.llm = new Anthropic({
|
|
||||||
model: process.env.MODEL as keyof typeof ALL_AVAILABLE_ANTHROPIC_MODELS,
|
|
||||||
});
|
|
||||||
Settings.embedModel = new HuggingFaceEmbedding({
|
|
||||||
modelType: embedModelMap[process.env.EMBEDDING_MODEL!],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initGemini() {
|
|
||||||
Settings.llm = new Gemini({
|
|
||||||
model: process.env.MODEL as GEMINI_MODEL,
|
|
||||||
});
|
|
||||||
Settings.embedModel = new GeminiEmbedding({
|
|
||||||
model: process.env.EMBEDDING_MODEL as GEMINI_EMBEDDING_MODEL,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,65 +1,49 @@
|
|||||||
import {
|
import {
|
||||||
StreamData,
|
JSONValue,
|
||||||
createCallbacksTransformer,
|
createCallbacksTransformer,
|
||||||
createStreamDataTransformer,
|
createStreamDataTransformer,
|
||||||
|
experimental_StreamData,
|
||||||
trimStartOfStreamHelper,
|
trimStartOfStreamHelper,
|
||||||
type AIStreamCallbacksAndOptions,
|
type AIStreamCallbacksAndOptions,
|
||||||
} from "ai";
|
} from "ai";
|
||||||
import {
|
import { Response, StreamingAgentChatResponse } from "llamaindex";
|
||||||
Metadata,
|
|
||||||
NodeWithScore,
|
|
||||||
Response,
|
|
||||||
ToolCallLLMMessageOptions,
|
|
||||||
} from "llamaindex";
|
|
||||||
|
|
||||||
import { AgentStreamChatResponse } from "llamaindex/agent/base";
|
|
||||||
import { appendImageData, appendSourceData } from "./stream-helper";
|
|
||||||
|
|
||||||
type LlamaIndexResponse =
|
|
||||||
| AgentStreamChatResponse<ToolCallLLMMessageOptions>
|
|
||||||
| Response;
|
|
||||||
|
|
||||||
type ParserOptions = {
|
type ParserOptions = {
|
||||||
image_url?: string;
|
image_url?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function createParser(
|
function createParser(
|
||||||
res: AsyncIterable<LlamaIndexResponse>,
|
res: AsyncIterable<Response>,
|
||||||
data: StreamData,
|
data: experimental_StreamData,
|
||||||
opts?: ParserOptions,
|
opts?: ParserOptions,
|
||||||
) {
|
) {
|
||||||
const it = res[Symbol.asyncIterator]();
|
const it = res[Symbol.asyncIterator]();
|
||||||
const trimStartOfStream = trimStartOfStreamHelper();
|
const trimStartOfStream = trimStartOfStreamHelper();
|
||||||
|
|
||||||
let sourceNodes: NodeWithScore<Metadata>[] | undefined;
|
|
||||||
return new ReadableStream<string>({
|
return new ReadableStream<string>({
|
||||||
start() {
|
start() {
|
||||||
appendImageData(data, opts?.image_url);
|
// if image_url is provided, send it via the data stream
|
||||||
|
if (opts?.image_url) {
|
||||||
|
const message: JSONValue = {
|
||||||
|
type: "image_url",
|
||||||
|
image_url: {
|
||||||
|
url: opts.image_url,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
data.append(message);
|
||||||
|
} else {
|
||||||
|
data.append({}); // send an empty image response for the user's message
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async pull(controller): Promise<void> {
|
async pull(controller): Promise<void> {
|
||||||
const { value, done } = await it.next();
|
const { value, done } = await it.next();
|
||||||
if (done) {
|
if (done) {
|
||||||
if (sourceNodes) {
|
|
||||||
appendSourceData(data, sourceNodes);
|
|
||||||
}
|
|
||||||
controller.close();
|
controller.close();
|
||||||
|
data.append({}); // send an empty image response for the assistant's message
|
||||||
data.close();
|
data.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let delta;
|
const text = trimStartOfStream(value.response ?? "");
|
||||||
if (value instanceof Response) {
|
|
||||||
// handle Response type
|
|
||||||
if (value.sourceNodes) {
|
|
||||||
// get source nodes from the first response
|
|
||||||
sourceNodes = value.sourceNodes;
|
|
||||||
}
|
|
||||||
delta = value.response ?? "";
|
|
||||||
} else {
|
|
||||||
// handle other types
|
|
||||||
delta = value.response.delta;
|
|
||||||
}
|
|
||||||
const text = trimStartOfStream(delta ?? "");
|
|
||||||
if (text) {
|
if (text) {
|
||||||
controller.enqueue(text);
|
controller.enqueue(text);
|
||||||
}
|
}
|
||||||
@@ -68,14 +52,21 @@ function createParser(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function LlamaIndexStream(
|
export function LlamaIndexStream(
|
||||||
response: AsyncIterable<LlamaIndexResponse>,
|
response: StreamingAgentChatResponse | AsyncIterable<Response>,
|
||||||
data: StreamData,
|
|
||||||
opts?: {
|
opts?: {
|
||||||
callbacks?: AIStreamCallbacksAndOptions;
|
callbacks?: AIStreamCallbacksAndOptions;
|
||||||
parserOptions?: ParserOptions;
|
parserOptions?: ParserOptions;
|
||||||
},
|
},
|
||||||
): ReadableStream<Uint8Array> {
|
): { stream: ReadableStream; data: experimental_StreamData } {
|
||||||
return createParser(response, data, opts?.parserOptions)
|
const data = new experimental_StreamData();
|
||||||
.pipeThrough(createCallbacksTransformer(opts?.callbacks))
|
const res =
|
||||||
.pipeThrough(createStreamDataTransformer());
|
response instanceof StreamingAgentChatResponse
|
||||||
|
? response.response
|
||||||
|
: response;
|
||||||
|
return {
|
||||||
|
stream: createParser(res, data, opts?.parserOptions)
|
||||||
|
.pipeThrough(createCallbacksTransformer(opts?.callbacks))
|
||||||
|
.pipeThrough(createStreamDataTransformer(true)),
|
||||||
|
data,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user