Spaces:
Running
Running
Commit
·
055a3a7
1
Parent(s):
3aa91e9
fix(rag): apply lazy imports and use centralized config
Browse files- Remove unused TYPE_CHECKING block (imports are in __init__)
- Use settings.openai_model instead of hard-coded "gpt-4o-mini"
- Fix line length in clear_collection() method
- Change Document type hint to Any for lazy import compatibility
- src/services/llamaindex_rag.py +43 -22
src/services/llamaindex_rag.py
CHANGED
|
@@ -1,14 +1,11 @@
|
|
| 1 |
-
"""LlamaIndex RAG service for evidence retrieval and indexing.
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
from typing import Any
|
| 4 |
|
| 5 |
-
import chromadb
|
| 6 |
import structlog
|
| 7 |
-
from llama_index.core import Document, Settings, StorageContext, VectorStoreIndex
|
| 8 |
-
from llama_index.core.retrievers import VectorIndexRetriever
|
| 9 |
-
from llama_index.embeddings.openai import OpenAIEmbedding
|
| 10 |
-
from llama_index.llms.openai import OpenAI
|
| 11 |
-
from llama_index.vector_stores.chroma import ChromaVectorStore
|
| 12 |
|
| 13 |
from src.utils.config import settings
|
| 14 |
from src.utils.models import Evidence
|
|
@@ -35,22 +32,44 @@ class LlamaIndexRAGService:
|
|
| 35 |
embedding_model: OpenAI embedding model to use
|
| 36 |
similarity_top_k: Number of top results to retrieve
|
| 37 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
self.collection_name = collection_name
|
| 39 |
self.persist_dir = persist_dir or settings.chroma_db_path
|
| 40 |
self.similarity_top_k = similarity_top_k
|
| 41 |
|
| 42 |
-
# Configure LlamaIndex settings
|
| 43 |
-
|
| 44 |
-
model=
|
| 45 |
api_key=settings.openai_api_key,
|
| 46 |
)
|
| 47 |
-
|
| 48 |
model=embedding_model,
|
| 49 |
api_key=settings.openai_api_key,
|
| 50 |
)
|
| 51 |
|
| 52 |
# Initialize ChromaDB client
|
| 53 |
-
self.chroma_client =
|
| 54 |
|
| 55 |
# Get or create collection
|
| 56 |
try:
|
|
@@ -61,18 +80,18 @@ class LlamaIndexRAGService:
|
|
| 61 |
logger.info("created_new_collection", name=self.collection_name)
|
| 62 |
|
| 63 |
# Initialize vector store and index
|
| 64 |
-
self.vector_store =
|
| 65 |
-
self.storage_context =
|
| 66 |
|
| 67 |
# Try to load existing index, or create empty one
|
| 68 |
try:
|
| 69 |
-
self.index =
|
| 70 |
vector_store=self.vector_store,
|
| 71 |
storage_context=self.storage_context,
|
| 72 |
)
|
| 73 |
logger.info("loaded_existing_index")
|
| 74 |
except Exception:
|
| 75 |
-
self.index =
|
| 76 |
logger.info("created_new_index")
|
| 77 |
|
| 78 |
def ingest_evidence(self, evidence_list: list[Evidence]) -> None:
|
|
@@ -97,7 +116,7 @@ class LlamaIndexRAGService:
|
|
| 97 |
"authors": ", ".join(evidence.citation.authors),
|
| 98 |
}
|
| 99 |
|
| 100 |
-
doc =
|
| 101 |
text=evidence.content,
|
| 102 |
metadata=metadata,
|
| 103 |
doc_id=evidence.citation.url, # Use URL as unique ID
|
|
@@ -113,7 +132,7 @@ class LlamaIndexRAGService:
|
|
| 113 |
logger.error("failed_to_ingest_evidence", error=str(e))
|
| 114 |
raise
|
| 115 |
|
| 116 |
-
def ingest_documents(self, documents: list[
|
| 117 |
"""
|
| 118 |
Ingest raw LlamaIndex Documents.
|
| 119 |
|
|
@@ -146,7 +165,7 @@ class LlamaIndexRAGService:
|
|
| 146 |
k = top_k or self.similarity_top_k
|
| 147 |
|
| 148 |
# Create retriever
|
| 149 |
-
retriever =
|
| 150 |
index=self.index,
|
| 151 |
similarity_top_k=k,
|
| 152 |
)
|
|
@@ -205,9 +224,11 @@ class LlamaIndexRAGService:
|
|
| 205 |
try:
|
| 206 |
self.chroma_client.delete_collection(self.collection_name)
|
| 207 |
self.collection = self.chroma_client.create_collection(self.collection_name)
|
| 208 |
-
self.vector_store =
|
| 209 |
-
self.storage_context =
|
| 210 |
-
|
|
|
|
|
|
|
| 211 |
logger.info("cleared_collection", name=self.collection_name)
|
| 212 |
except Exception as e:
|
| 213 |
logger.error("failed_to_clear_collection", error=str(e))
|
|
|
|
| 1 |
+
"""LlamaIndex RAG service for evidence retrieval and indexing.
|
| 2 |
+
|
| 3 |
+
Requires optional dependencies: uv sync --extra modal
|
| 4 |
+
"""
|
| 5 |
|
| 6 |
from typing import Any
|
| 7 |
|
|
|
|
| 8 |
import structlog
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
from src.utils.config import settings
|
| 11 |
from src.utils.models import Evidence
|
|
|
|
| 32 |
embedding_model: OpenAI embedding model to use
|
| 33 |
similarity_top_k: Number of top results to retrieve
|
| 34 |
"""
|
| 35 |
+
# Lazy import - only when instantiated
|
| 36 |
+
try:
|
| 37 |
+
import chromadb
|
| 38 |
+
from llama_index.core import Document, Settings, StorageContext, VectorStoreIndex
|
| 39 |
+
from llama_index.core.retrievers import VectorIndexRetriever
|
| 40 |
+
from llama_index.embeddings.openai import OpenAIEmbedding
|
| 41 |
+
from llama_index.llms.openai import OpenAI
|
| 42 |
+
from llama_index.vector_stores.chroma import ChromaVectorStore
|
| 43 |
+
except ImportError as e:
|
| 44 |
+
raise ImportError(
|
| 45 |
+
"LlamaIndex dependencies not installed. Run: uv sync --extra modal"
|
| 46 |
+
) from e
|
| 47 |
+
|
| 48 |
+
# Store references for use in other methods
|
| 49 |
+
self._chromadb = chromadb
|
| 50 |
+
self._Document = Document
|
| 51 |
+
self._Settings = Settings
|
| 52 |
+
self._StorageContext = StorageContext
|
| 53 |
+
self._VectorStoreIndex = VectorStoreIndex
|
| 54 |
+
self._VectorIndexRetriever = VectorIndexRetriever
|
| 55 |
+
self._ChromaVectorStore = ChromaVectorStore
|
| 56 |
+
|
| 57 |
self.collection_name = collection_name
|
| 58 |
self.persist_dir = persist_dir or settings.chroma_db_path
|
| 59 |
self.similarity_top_k = similarity_top_k
|
| 60 |
|
| 61 |
+
# Configure LlamaIndex settings (use centralized config)
|
| 62 |
+
self._Settings.llm = OpenAI(
|
| 63 |
+
model=settings.openai_model,
|
| 64 |
api_key=settings.openai_api_key,
|
| 65 |
)
|
| 66 |
+
self._Settings.embed_model = OpenAIEmbedding(
|
| 67 |
model=embedding_model,
|
| 68 |
api_key=settings.openai_api_key,
|
| 69 |
)
|
| 70 |
|
| 71 |
# Initialize ChromaDB client
|
| 72 |
+
self.chroma_client = self._chromadb.PersistentClient(path=self.persist_dir)
|
| 73 |
|
| 74 |
# Get or create collection
|
| 75 |
try:
|
|
|
|
| 80 |
logger.info("created_new_collection", name=self.collection_name)
|
| 81 |
|
| 82 |
# Initialize vector store and index
|
| 83 |
+
self.vector_store = self._ChromaVectorStore(chroma_collection=self.collection)
|
| 84 |
+
self.storage_context = self._StorageContext.from_defaults(vector_store=self.vector_store)
|
| 85 |
|
| 86 |
# Try to load existing index, or create empty one
|
| 87 |
try:
|
| 88 |
+
self.index = self._VectorStoreIndex.from_vector_store(
|
| 89 |
vector_store=self.vector_store,
|
| 90 |
storage_context=self.storage_context,
|
| 91 |
)
|
| 92 |
logger.info("loaded_existing_index")
|
| 93 |
except Exception:
|
| 94 |
+
self.index = self._VectorStoreIndex([], storage_context=self.storage_context)
|
| 95 |
logger.info("created_new_index")
|
| 96 |
|
| 97 |
def ingest_evidence(self, evidence_list: list[Evidence]) -> None:
|
|
|
|
| 116 |
"authors": ", ".join(evidence.citation.authors),
|
| 117 |
}
|
| 118 |
|
| 119 |
+
doc = self._Document(
|
| 120 |
text=evidence.content,
|
| 121 |
metadata=metadata,
|
| 122 |
doc_id=evidence.citation.url, # Use URL as unique ID
|
|
|
|
| 132 |
logger.error("failed_to_ingest_evidence", error=str(e))
|
| 133 |
raise
|
| 134 |
|
| 135 |
+
def ingest_documents(self, documents: list[Any]) -> None:
|
| 136 |
"""
|
| 137 |
Ingest raw LlamaIndex Documents.
|
| 138 |
|
|
|
|
| 165 |
k = top_k or self.similarity_top_k
|
| 166 |
|
| 167 |
# Create retriever
|
| 168 |
+
retriever = self._VectorIndexRetriever(
|
| 169 |
index=self.index,
|
| 170 |
similarity_top_k=k,
|
| 171 |
)
|
|
|
|
| 224 |
try:
|
| 225 |
self.chroma_client.delete_collection(self.collection_name)
|
| 226 |
self.collection = self.chroma_client.create_collection(self.collection_name)
|
| 227 |
+
self.vector_store = self._ChromaVectorStore(chroma_collection=self.collection)
|
| 228 |
+
self.storage_context = self._StorageContext.from_defaults(
|
| 229 |
+
vector_store=self.vector_store
|
| 230 |
+
)
|
| 231 |
+
self.index = self._VectorStoreIndex([], storage_context=self.storage_context)
|
| 232 |
logger.info("cleared_collection", name=self.collection_name)
|
| 233 |
except Exception as e:
|
| 234 |
logger.error("failed_to_clear_collection", error=str(e))
|