diff --git a/.changeset/lovely-olives-carry.md b/.changeset/lovely-olives-carry.md new file mode 100644 index 0000000000000000000000000000000000000000..99741125edde227bdf27d07dafe3fdad9a8c4664 --- /dev/null +++ b/.changeset/lovely-olives-carry.md @@ -0,0 +1,5 @@ +--- +"create-llama": patch +--- + +Add Qdrant support diff --git a/helpers/env-variables.ts b/helpers/env-variables.ts index 65782179acdcde6d9b33ff598c9c6e1d9dbff16e..ecaed32be6c69d774b2a57d9035533f18c7a2bc7 100644 --- a/helpers/env-variables.ts +++ b/helpers/env-variables.ts @@ -108,6 +108,23 @@ const getVectorDBEnvs = (vectorDb: TemplateVectorDB) => { description: "The name of the collection in your Astra database", }, ]; + case "qdrant": + return [ + { + name: "QDRANT_URL", + description: + "The qualified REST URL of the Qdrant server. Eg: http://localhost:6333", + }, + { + name: "QDRANT_COLLECTION", + description: "The name of Qdrant collection to use.", + }, + { + name: "QDRANT_API_KEY", + description: + "Optional API key for authenticating requests to Qdrant.", + }, + ]; default: return []; } diff --git a/helpers/types.ts b/helpers/types.ts index 52a300a757343e4fcf73d935bf4a9f5dffba8aa0..0db557ed1ccf753a95c353ab2211d22420d9bc3f 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -10,7 +10,8 @@ export type TemplateVectorDB = | "pg" | "pinecone" | "milvus" - | "astra"; + | "astra" + | "qdrant"; export type TemplatePostInstallAction = | "none" | "VSCode" diff --git a/questions.ts b/questions.ts index 4afd0085478b102478881dfb4c7d950ed0c4c308..6efbc45d55add0ee441b9c29a822ad4d4cb2c02c 100644 --- a/questions.ts +++ b/questions.ts @@ -102,6 +102,7 @@ const getVectorDbChoices = (framework: TemplateFramework) => { { title: "Pinecone", value: "pinecone" }, { title: "Milvus", value: "milvus" }, { title: "Astra", value: "astra" }, + { title: "Qdrant", value: "qdrant" }, ]; const vectordbLang = framework === "fastapi" ? "python" : "typescript"; diff --git a/templates/components/vectordbs/python/qdrant/__init__.py b/templates/components/vectordbs/python/qdrant/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/templates/components/vectordbs/python/qdrant/generate.py b/templates/components/vectordbs/python/qdrant/generate.py new file mode 100644 index 0000000000000000000000000000000000000000..db7c055e492311a5f6e6f01bbdf0406ce470cfde --- /dev/null +++ b/templates/components/vectordbs/python/qdrant/generate.py @@ -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() diff --git a/templates/components/vectordbs/python/qdrant/index.py b/templates/components/vectordbs/python/qdrant/index.py new file mode 100644 index 0000000000000000000000000000000000000000..0a388d8a303f937aedec42aaecb73668b83f0c84 --- /dev/null +++ b/templates/components/vectordbs/python/qdrant/index.py @@ -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 diff --git a/templates/components/vectordbs/typescript/qdrant/generate.ts b/templates/components/vectordbs/typescript/qdrant/generate.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0e7fa40f9943bd5426d57324b06102dafed3102 --- /dev/null +++ b/templates/components/vectordbs/typescript/qdrant/generate.ts @@ -0,0 +1,37 @@ +/* eslint-disable turbo/no-undeclared-env-vars */ +import * as dotenv from "dotenv"; +import { + QdrantVectorStore, + VectorStoreIndex, + storageContextFromDefaults, +} from "llamaindex"; +import { getDocuments } from "./loader"; +import { initSettings } from "./settings"; +import { checkRequiredEnvVars, getQdrantClient } from "./shared"; + +dotenv.config(); + +const collectionName = process.env.QDRANT_COLLECTION; + +async function loadAndIndex() { + // load objects from storage and convert them into LlamaIndex Document objects + const documents = await getDocuments(); + + // Connect to Qdrant + const vectorStore = new QdrantVectorStore(collectionName, getQdrantClient()); + + const storageContext = await storageContextFromDefaults({ vectorStore }); + await VectorStoreIndex.fromDocuments(documents, { + storageContext: storageContext, + }); + console.log( + `Successfully upload embeddings to Qdrant collection ${collectionName}.`, + ); +} + +(async () => { + checkRequiredEnvVars(); + initSettings(); + await loadAndIndex(); + console.log("Finished generating storage."); +})(); diff --git a/templates/components/vectordbs/typescript/qdrant/index.ts b/templates/components/vectordbs/typescript/qdrant/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..229d7a3984c921c631dcd6be4a9b6cd6cf244bca --- /dev/null +++ b/templates/components/vectordbs/typescript/qdrant/index.ts @@ -0,0 +1,13 @@ +import * as dotenv from "dotenv"; +import { QdrantVectorStore, VectorStoreIndex } from "llamaindex"; +import { checkRequiredEnvVars, getQdrantClient } from "./shared"; + +dotenv.config(); + +export async function getDataSource() { + checkRequiredEnvVars(); + const collectionName = process.env.QDRANT_COLLECTION; + const store = new QdrantVectorStore(collectionName, getQdrantClient()); + + return await VectorStoreIndex.fromVectorStore(store); +} diff --git a/templates/components/vectordbs/typescript/qdrant/shared.ts b/templates/components/vectordbs/typescript/qdrant/shared.ts new file mode 100644 index 0000000000000000000000000000000000000000..e1475eeed3486ce7f40fb92cac6efc934bbbf9c8 --- /dev/null +++ b/templates/components/vectordbs/typescript/qdrant/shared.ts @@ -0,0 +1,32 @@ +import { QdrantClient } from "@qdrant/js-client-rest"; + +const REQUIRED_ENV_VARS = ["QDRANT_URL", "QDRANT_COLLECTION"]; // QDRANT_API_KEY is optional + +export function getQdrantClient() { + const url = process.env.QDRANT_URL; + if (!url) { + throw new Error("QDRANT_URL environment variable is required"); + } + const apiKey = process.env?.QDRANT_API_KEY; + return new QdrantClient({ + url, + apiKey, + }); +} + +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(", ")}`, + ); + } +}