@@ -89,24 +89,39 @@ def count(self) -> int:
8989# ============================================================================
9090
9191class PineconeCollection :
92- """Pinecone collection wrapper for production."""
92+ """Pinecone collection wrapper for production with real embeddings ."""
9393
94- EMBEDDING_DIMENSION = 384 # Matches all-MiniLM-L6-v2
94+ EMBEDDING_DIMENSION = 1024 # multilingual-e5-large dimension
95+ EMBEDDING_MODEL = "multilingual-e5-large" # Pinecone's hosted model
9596
9697 def __init__ (self , collection_name : str ):
97- """Initialize Pinecone index."""
98+ """Initialize Pinecone index with integrated inference ."""
9899 from pinecone import Pinecone , ServerlessSpec
99100
100101 self .index_name = collection_name .lower ().replace ("_" , "-" )
101102
102103 # Initialize Pinecone client
103104 self .pc = Pinecone (api_key = PINECONE_API_KEY )
104105
105- # Get or create index
106+ # Get or create index with correct dimension
106107 existing_indexes = [idx .name for idx in self .pc .list_indexes ()]
107108
109+ if self .index_name in existing_indexes :
110+ # Check if existing index has correct dimension
111+ try :
112+ existing_index = self .pc .Index (self .index_name )
113+ stats = existing_index .describe_index_stats ()
114+ # If dimension is wrong (384 vs 1024), delete and recreate
115+ index_info = self .pc .describe_index (self .index_name )
116+ if hasattr (index_info , 'dimension' ) and index_info .dimension != self .EMBEDDING_DIMENSION :
117+ logger .warning (f"Index dimension mismatch ({ index_info .dimension } vs { self .EMBEDDING_DIMENSION } ), recreating..." )
118+ self .pc .delete_index (self .index_name )
119+ existing_indexes = [] # Force recreation
120+ except Exception as e :
121+ logger .warning (f"Could not check index dimension: { e } " )
122+
108123 if self .index_name not in existing_indexes :
109- logger .info (f"Creating Pinecone index: { self .index_name } " )
124+ logger .info (f"Creating Pinecone index with { self . EMBEDDING_MODEL } embeddings : { self .index_name } " )
110125 self .pc .create_index (
111126 name = self .index_name ,
112127 dimension = self .EMBEDDING_DIMENSION ,
@@ -116,42 +131,60 @@ def __init__(self, collection_name: str):
116131 region = os .getenv ("PINECONE_ENVIRONMENT" , "us-east-1" )
117132 )
118133 )
134+ # Wait for index to be ready
135+ import time
136+ time .sleep (5 )
119137
120138 self .index = self .pc .Index (self .index_name )
121- logger .info (f"✅ Pinecone index '{ self .index_name } ' initialized (production mode)" )
139+
140+ # Use Pinecone's inference API for embeddings
141+ self .use_inference_api = True
142+ logger .info (f"✅ Pinecone index '{ self .index_name } ' initialized with { self .EMBEDDING_MODEL } embeddings" )
143+
144+ def _get_embeddings (self , texts : List [str ]) -> List [List [float ]]:
145+ """Get embeddings using Pinecone's inference API."""
146+ try :
147+ # Use Pinecone's inference API
148+ embeddings_response = self .pc .inference .embed (
149+ model = self .EMBEDDING_MODEL ,
150+ inputs = texts ,
151+ parameters = {"input_type" : "passage" }
152+ )
153+ return [e .values for e in embeddings_response .data ]
154+ except Exception as e :
155+ logger .warning (f"Pinecone inference failed: { e } , using fallback" )
156+ return self ._fallback_embeddings (texts )
122157
123- def _text_to_vector (self , text : str ) -> List [float ]:
124- """
125- Simple text embedding using hash-based approach.
126- Lightweight alternative to loading ML models.
127- """
158+ def _fallback_embeddings (self , texts : List [str ]) -> List [List [float ]]:
159+ """Fallback: simple keyword-based pseudo-embeddings."""
128160 import hashlib
129161 import math
130162
131- text = text .lower ().strip ()
132- vector = []
133-
134- for i in range (self .EMBEDDING_DIMENSION ):
135- hash_input = f"{ text } _{ i } " .encode ('utf-8' )
136- hash_value = int (hashlib .md5 (hash_input ).hexdigest (), 16 )
137- normalized = (hash_value % 10000 ) / 5000 - 1.0
138- vector .append (normalized )
139-
140- # Normalize to unit length
141- magnitude = math .sqrt (sum (x * x for x in vector ))
142- if magnitude > 0 :
143- vector = [x / magnitude for x in vector ]
144-
145- return vector
163+ embeddings = []
164+ for text in texts :
165+ text = text .lower ().strip ()
166+ vector = []
167+ for i in range (self .EMBEDDING_DIMENSION ):
168+ hash_input = f"{ text } _{ i } " .encode ('utf-8' )
169+ hash_value = int (hashlib .md5 (hash_input ).hexdigest (), 16 )
170+ normalized = (hash_value % 10000 ) / 5000 - 1.0
171+ vector .append (normalized )
172+ magnitude = math .sqrt (sum (x * x for x in vector ))
173+ if magnitude > 0 :
174+ vector = [x / magnitude for x in vector ]
175+ embeddings .append (vector )
176+ return embeddings
146177
147178 def add (self , ids : List [str ], documents : List [str ], metadatas : List [Dict ] = None ):
148- """Add documents to Pinecone."""
179+ """Add documents to Pinecone with real embeddings ."""
149180 if metadatas is None :
150181 metadatas = [{} for _ in ids ]
151182
183+ # Get embeddings for all documents
184+ embeddings = self ._get_embeddings (documents )
185+
152186 vectors = []
153- for doc_id , doc , meta in zip (ids , documents , metadatas ):
154- embedding = self ._text_to_vector (doc )
187+ for doc_id , doc , meta , embedding in zip (ids , documents , metadatas , embeddings ):
155188 meta_with_doc = {** meta , "_document" : doc [:1000 ]}
156189 vectors .append ({
157190 "id" : doc_id ,
@@ -165,18 +198,19 @@ def add(self, ids: List[str], documents: List[str], metadatas: List[Dict] = None
165198 batch = vectors [i :i + batch_size ]
166199 self .index .upsert (vectors = batch )
167200
168- logger .info (f"✅ Added { len (ids )} documents to Pinecone" )
201+ logger .info (f"✅ Added { len (ids )} documents to Pinecone with semantic embeddings " )
169202
170203 def query (self , query_texts : List [str ], n_results : int = 3 ) -> Dict [str , List ]:
171- """Query Pinecone for similar documents."""
204+ """Query Pinecone for similar documents using real embeddings ."""
172205 all_ids = []
173206 all_documents = []
174207 all_metadatas = []
175208 all_distances = []
176209
177- for query_text in query_texts :
178- query_embedding = self ._text_to_vector (query_text )
179-
210+ # Get query embeddings
211+ query_embeddings = self ._get_embeddings (query_texts )
212+
213+ for query_embedding in query_embeddings :
180214 results = self .index .query (
181215 vector = query_embedding ,
182216 top_k = n_results ,
0 commit comments