| from pathlib import Path |
|
|
| from dotenv import load_dotenv |
| from langchain_chroma import Chroma |
| from langchain_huggingface import HuggingFaceEmbeddings |
|
|
| from core.config import ( |
| DATA_DIR, |
| DB_DIR, |
| DEFAULT_CHUNK_OVERLAP, |
| DEFAULT_CHUNK_SIZE, |
| DEFAULT_SEMANTIC_PERCENTILE, |
| REGISTRY_PATH, |
| ) |
| from rag.chunking import split_documents |
| from rag.loaders import load_documents |
| from rag.metadata import enrich_chunk_metadata, enrich_document_metadata |
| from rag.registry import update_chunk_registry |
|
|
|
|
| def build_vectorstore(chunks, embeddings, persist_dir): |
| """ |
| Build a Chroma vector store from the provided document chunks and embeddings. |
| """ |
|
|
| return Chroma.from_documents( |
| documents=chunks, |
| embedding=embeddings, |
| persist_directory=persist_dir, |
| ) |
|
|
|
|
| def prepare_chunks(documents, embeddings): |
| """ |
| Run the ingestion preparation pipeline before writing to Chroma. |
| """ |
|
|
| documents = enrich_document_metadata(documents) |
| chunks = split_documents( |
| documents, |
| embeddings=embeddings, |
| chunk_size=DEFAULT_CHUNK_SIZE, |
| chunk_overlap=DEFAULT_CHUNK_OVERLAP, |
| percentile=DEFAULT_SEMANTIC_PERCENTILE, |
| ) |
| return enrich_chunk_metadata(chunks) |
|
|
|
|
| def add_documents_to_vectorstore(data_dir, embeddings, persist_dir): |
| """ |
| Add new documents into the existing Chroma vector store. |
| """ |
|
|
| documents = load_documents(data_dir) |
| return add_loaded_documents_to_vectorstore(documents, embeddings, persist_dir) |
|
|
|
|
| def add_loaded_documents_to_vectorstore(documents, embeddings, persist_dir): |
| """ |
| Add already-loaded documents into the existing Chroma vector store. |
| """ |
|
|
| enriched_chunks = prepare_chunks(documents, embeddings) |
|
|
| if not enriched_chunks: |
| raise ValueError("No supported documents were found to ingest.") |
|
|
| persist_path = Path(persist_dir) |
| |
| if persist_path.exists() and any(persist_path.iterdir()): |
| vector_db = Chroma( |
| embedding_function=embeddings, |
| persist_directory=str(persist_path), |
| ) |
| vector_db.add_documents(enriched_chunks) |
| update_chunk_registry(enriched_chunks) |
| return vector_db |
|
|
| vector_db = build_vectorstore(enriched_chunks, embeddings, str(persist_path)) |
| update_chunk_registry(enriched_chunks) |
| return vector_db |
|
|
|
|
| def reingest_directory(data_dir, embeddings, persist_dir, registry_path=REGISTRY_PATH): |
| """ |
| Rebuild Chroma and the chunk registry from a directory of uploaded files. |
| """ |
|
|
| documents = load_documents(data_dir) |
| enriched_chunks = prepare_chunks(documents, embeddings) |
|
|
| persist_path = Path(persist_dir) |
| persist_path.mkdir(parents=True, exist_ok=True) |
|
|
| vector_db = None |
| if any(persist_path.iterdir()): |
| vector_db = Chroma( |
| embedding_function=embeddings, |
| persist_directory=str(persist_path), |
| ) |
| existing = vector_db._collection.get() |
| existing_ids = existing.get("ids", []) |
| if existing_ids: |
| vector_db._collection.delete(ids=existing_ids) |
|
|
| registry_file = Path(registry_path) |
| registry_file.write_text( |
| '{"by_document": {}, "by_chunk_id": {}}', |
| encoding="utf-8", |
| ) |
|
|
| if not enriched_chunks: |
| return { |
| "document_count": len(documents), |
| "chunk_count": 0, |
| } |
|
|
| if vector_db is not None: |
| vector_db.add_documents(enriched_chunks) |
| else: |
| build_vectorstore(enriched_chunks, embeddings, str(persist_path)) |
| update_chunk_registry(enriched_chunks, registry_path=registry_path) |
| return { |
| "document_count": len(documents), |
| "chunk_count": len(enriched_chunks), |
| } |
|
|
|
|
| def main(): |
|
|
| load_dotenv() |
|
|
| embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2") |
|
|
| print("Starting ingestion process...") |
| documents = load_documents(DATA_DIR) |
| enriched_chunks = prepare_chunks(documents, embeddings) |
| build_vectorstore(enriched_chunks, embeddings, str(DB_DIR)) |
| update_chunk_registry(enriched_chunks) |
|
|
| print(f"Loaded {len(documents)} documents") |
| print(f"Created {len(enriched_chunks)} chunks") |
| print(f"Saved vector DB to {DB_DIR}") |
| print("Ingestion completed.") |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit( |
| "Run this from the project root with `python ingest_docs.py`, not `python rag/ingest.py`." |
| ) |
|
|