rag-demo / src /vector_store.py
tca411's picture
Upload 12 files
477651d verified
Raw History Blame Contribute Delete
3.9 kB
"""
Pinecone vector store wrapper.
Reconstructed to match the interface app.py expects:
- create_vector_store_from_env()
- .add_documents(documents, chunk_size, chunk_overlap)
- .get_index_stats()
- .similarity_search(...) / .similarity_search_with_score(...)
- .delete_index()
"""
import os
import time
from typing import List, Tuple
from langchain.schema import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_pinecone import PineconeVectorStore
from pinecone import Pinecone, ServerlessSpec
# text-embedding-3-small / ada-002 both emit 1536 dims
EMBEDDING_DIMENSION = 1536
class VectorStore:
"""Thin wrapper around a Pinecone serverless index + LangChain embeddings."""
def __init__(
self,
api_key: str,
index_name: str,
embedding_model: str = "text-embedding-3-small",
cloud: str = "aws",
region: str = "us-east-1",
):
self.index_name = index_name
self.pc = Pinecone(api_key=api_key)
self.embeddings = OpenAIEmbeddings(model=embedding_model)
existing = [idx["name"] for idx in self.pc.list_indexes()]
if index_name not in existing:
self.pc.create_index(
name=index_name,
dimension=EMBEDDING_DIMENSION,
metric="cosine",
spec=ServerlessSpec(cloud=cloud, region=region),
)
# wait for the index to be ready
while not self.pc.describe_index(index_name).status["ready"]:
time.sleep(1)
self.index = self.pc.Index(index_name)
self._store = PineconeVectorStore(
index=self.index, embedding=self.embeddings
)
def add_documents(
self,
documents: List[Document],
chunk_size: int = 1000,
chunk_overlap: int = 200,
) -> int:
"""Split documents into chunks and upsert them. Returns chunk count."""
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=["\n\n", "\n", ". ", " ", ""],
)
chunks = splitter.split_documents(documents)
if chunks:
self._store.add_documents(chunks)
return len(chunks)
def similarity_search(self, query: str, k: int = 5) -> List[Document]:
return self._store.similarity_search(query, k=k)
def similarity_search_with_score(
self, query: str, k: int = 5
) -> List[Tuple[Document, float]]:
return self._store.similarity_search_with_score(query, k=k)
def as_retriever(self, k: int = 5):
return self._store.as_retriever(search_kwargs={"k": k})
def get_index_stats(self) -> dict:
stats = self.index.describe_index_stats()
return {
"total_vector_count": stats.get("total_vector_count", 0),
"dimension": stats.get("dimension", EMBEDDING_DIMENSION),
}
def delete_index(self) -> None:
"""Destroy the entire index. Irreversible."""
self.pc.delete_index(self.index_name)
def create_vector_store_from_env() -> VectorStore:
"""Build a VectorStore from environment variables."""
api_key = os.getenv("PINECONE_API_KEY")
index_name = os.getenv("PINECONE_INDEX_NAME", "rag-demo")
region = os.getenv("PINECONE_ENVIRONMENT", "us-east-1")
embedding_model = os.getenv(
"OPENAI_EMBEDDING_MODEL", "text-embedding-3-small"
)
if not api_key:
raise ValueError("PINECONE_API_KEY is not set")
# PINECONE_ENVIRONMENT may arrive as "us-east-1-aws"; keep just the region
region = region.replace("-aws", "").replace("-gcp", "")
return VectorStore(
api_key=api_key,
index_name=index_name,
embedding_model=embedding_model,
region=region,
)