mem0ai-azure-mysql 0.1.115__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. mem0/__init__.py +6 -0
  2. mem0/client/__init__.py +0 -0
  3. mem0/client/main.py +1535 -0
  4. mem0/client/project.py +860 -0
  5. mem0/client/utils.py +29 -0
  6. mem0/configs/__init__.py +0 -0
  7. mem0/configs/base.py +90 -0
  8. mem0/configs/dbs/__init__.py +4 -0
  9. mem0/configs/dbs/base.py +41 -0
  10. mem0/configs/dbs/mysql.py +25 -0
  11. mem0/configs/embeddings/__init__.py +0 -0
  12. mem0/configs/embeddings/base.py +108 -0
  13. mem0/configs/enums.py +7 -0
  14. mem0/configs/llms/__init__.py +0 -0
  15. mem0/configs/llms/base.py +152 -0
  16. mem0/configs/prompts.py +333 -0
  17. mem0/configs/vector_stores/__init__.py +0 -0
  18. mem0/configs/vector_stores/azure_ai_search.py +59 -0
  19. mem0/configs/vector_stores/baidu.py +29 -0
  20. mem0/configs/vector_stores/chroma.py +40 -0
  21. mem0/configs/vector_stores/elasticsearch.py +47 -0
  22. mem0/configs/vector_stores/faiss.py +39 -0
  23. mem0/configs/vector_stores/langchain.py +32 -0
  24. mem0/configs/vector_stores/milvus.py +43 -0
  25. mem0/configs/vector_stores/mongodb.py +25 -0
  26. mem0/configs/vector_stores/opensearch.py +41 -0
  27. mem0/configs/vector_stores/pgvector.py +37 -0
  28. mem0/configs/vector_stores/pinecone.py +56 -0
  29. mem0/configs/vector_stores/qdrant.py +49 -0
  30. mem0/configs/vector_stores/redis.py +26 -0
  31. mem0/configs/vector_stores/supabase.py +44 -0
  32. mem0/configs/vector_stores/upstash_vector.py +36 -0
  33. mem0/configs/vector_stores/vertex_ai_vector_search.py +27 -0
  34. mem0/configs/vector_stores/weaviate.py +43 -0
  35. mem0/dbs/__init__.py +4 -0
  36. mem0/dbs/base.py +68 -0
  37. mem0/dbs/configs.py +21 -0
  38. mem0/dbs/mysql.py +321 -0
  39. mem0/embeddings/__init__.py +0 -0
  40. mem0/embeddings/aws_bedrock.py +100 -0
  41. mem0/embeddings/azure_openai.py +43 -0
  42. mem0/embeddings/base.py +31 -0
  43. mem0/embeddings/configs.py +30 -0
  44. mem0/embeddings/gemini.py +39 -0
  45. mem0/embeddings/huggingface.py +41 -0
  46. mem0/embeddings/langchain.py +35 -0
  47. mem0/embeddings/lmstudio.py +29 -0
  48. mem0/embeddings/mock.py +11 -0
  49. mem0/embeddings/ollama.py +53 -0
  50. mem0/embeddings/openai.py +49 -0
  51. mem0/embeddings/together.py +31 -0
  52. mem0/embeddings/vertexai.py +54 -0
  53. mem0/graphs/__init__.py +0 -0
  54. mem0/graphs/configs.py +96 -0
  55. mem0/graphs/neptune/__init__.py +0 -0
  56. mem0/graphs/neptune/base.py +410 -0
  57. mem0/graphs/neptune/main.py +372 -0
  58. mem0/graphs/tools.py +371 -0
  59. mem0/graphs/utils.py +97 -0
  60. mem0/llms/__init__.py +0 -0
  61. mem0/llms/anthropic.py +64 -0
  62. mem0/llms/aws_bedrock.py +270 -0
  63. mem0/llms/azure_openai.py +114 -0
  64. mem0/llms/azure_openai_structured.py +76 -0
  65. mem0/llms/base.py +32 -0
  66. mem0/llms/configs.py +34 -0
  67. mem0/llms/deepseek.py +85 -0
  68. mem0/llms/gemini.py +201 -0
  69. mem0/llms/groq.py +88 -0
  70. mem0/llms/langchain.py +65 -0
  71. mem0/llms/litellm.py +87 -0
  72. mem0/llms/lmstudio.py +53 -0
  73. mem0/llms/ollama.py +94 -0
  74. mem0/llms/openai.py +124 -0
  75. mem0/llms/openai_structured.py +52 -0
  76. mem0/llms/sarvam.py +89 -0
  77. mem0/llms/together.py +88 -0
  78. mem0/llms/vllm.py +89 -0
  79. mem0/llms/xai.py +52 -0
  80. mem0/memory/__init__.py +0 -0
  81. mem0/memory/base.py +63 -0
  82. mem0/memory/graph_memory.py +632 -0
  83. mem0/memory/main.py +1843 -0
  84. mem0/memory/memgraph_memory.py +630 -0
  85. mem0/memory/setup.py +56 -0
  86. mem0/memory/storage.py +218 -0
  87. mem0/memory/telemetry.py +90 -0
  88. mem0/memory/utils.py +133 -0
  89. mem0/proxy/__init__.py +0 -0
  90. mem0/proxy/main.py +194 -0
  91. mem0/utils/factory.py +132 -0
  92. mem0/vector_stores/__init__.py +0 -0
  93. mem0/vector_stores/azure_ai_search.py +383 -0
  94. mem0/vector_stores/baidu.py +368 -0
  95. mem0/vector_stores/base.py +58 -0
  96. mem0/vector_stores/chroma.py +229 -0
  97. mem0/vector_stores/configs.py +60 -0
  98. mem0/vector_stores/elasticsearch.py +235 -0
  99. mem0/vector_stores/faiss.py +473 -0
  100. mem0/vector_stores/langchain.py +179 -0
  101. mem0/vector_stores/milvus.py +245 -0
  102. mem0/vector_stores/mongodb.py +293 -0
  103. mem0/vector_stores/opensearch.py +281 -0
  104. mem0/vector_stores/pgvector.py +294 -0
  105. mem0/vector_stores/pinecone.py +373 -0
  106. mem0/vector_stores/qdrant.py +240 -0
  107. mem0/vector_stores/redis.py +295 -0
  108. mem0/vector_stores/supabase.py +237 -0
  109. mem0/vector_stores/upstash_vector.py +293 -0
  110. mem0/vector_stores/vertex_ai_vector_search.py +629 -0
  111. mem0/vector_stores/weaviate.py +316 -0
  112. mem0ai_azure_mysql-0.1.115.data/data/README.md +169 -0
  113. mem0ai_azure_mysql-0.1.115.dist-info/METADATA +224 -0
  114. mem0ai_azure_mysql-0.1.115.dist-info/RECORD +116 -0
  115. mem0ai_azure_mysql-0.1.115.dist-info/WHEEL +4 -0
  116. mem0ai_azure_mysql-0.1.115.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,473 @@
1
+ import logging
2
+ import os
3
+ import pickle
4
+ import uuid
5
+ from pathlib import Path
6
+ from typing import Dict, List, Optional
7
+
8
+ import numpy as np
9
+ from pydantic import BaseModel
10
+
11
+ try:
12
+ logging.getLogger("faiss").setLevel(logging.WARNING)
13
+ logging.getLogger("faiss.loader").setLevel(logging.WARNING)
14
+
15
+ import faiss
16
+ except ImportError:
17
+ raise ImportError(
18
+ "Could not import faiss python package. "
19
+ "Please install it with `pip install faiss-gpu` (for CUDA supported GPU) "
20
+ "or `pip install faiss-cpu` (depending on Python version)."
21
+ )
22
+
23
+ from mem0.vector_stores.base import VectorStoreBase
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class OutputData(BaseModel):
29
+ id: Optional[str] # memory id
30
+ score: Optional[float] # distance
31
+ payload: Optional[Dict] # metadata
32
+
33
+
34
+ class FAISS(VectorStoreBase):
35
+ def __init__(
36
+ self,
37
+ collection_name: str,
38
+ path: Optional[str] = None,
39
+ distance_strategy: str = "euclidean",
40
+ normalize_L2: bool = False,
41
+ embedding_model_dims: int = 1536,
42
+ ):
43
+ """
44
+ Initialize the FAISS vector store.
45
+
46
+ Args:
47
+ collection_name (str): Name of the collection.
48
+ path (str, optional): Path for local FAISS database. Defaults to None.
49
+ distance_strategy (str, optional): Distance strategy to use. Options: 'euclidean', 'inner_product', 'cosine'.
50
+ Defaults to "euclidean".
51
+ normalize_L2 (bool, optional): Whether to normalize L2 vectors. Only applicable for euclidean distance.
52
+ Defaults to False.
53
+ """
54
+ self.collection_name = collection_name
55
+ self.path = path or f"/tmp/faiss/{collection_name}"
56
+ self.distance_strategy = distance_strategy
57
+ self.normalize_L2 = normalize_L2
58
+ self.embedding_model_dims = embedding_model_dims
59
+
60
+ # Initialize storage structures
61
+ self.index = None
62
+ self.docstore = {}
63
+ self.index_to_id = {}
64
+
65
+ # Create directory if it doesn't exist
66
+ if self.path:
67
+ os.makedirs(os.path.dirname(self.path), exist_ok=True)
68
+
69
+ # Try to load existing index if available
70
+ index_path = f"{self.path}/{collection_name}.faiss"
71
+ docstore_path = f"{self.path}/{collection_name}.pkl"
72
+ if os.path.exists(index_path) and os.path.exists(docstore_path):
73
+ self._load(index_path, docstore_path)
74
+ else:
75
+ self.create_col(collection_name)
76
+
77
+ def _load(self, index_path: str, docstore_path: str):
78
+ """
79
+ Load FAISS index and docstore from disk.
80
+
81
+ Args:
82
+ index_path (str): Path to FAISS index file.
83
+ docstore_path (str): Path to docstore pickle file.
84
+ """
85
+ try:
86
+ self.index = faiss.read_index(index_path)
87
+ with open(docstore_path, "rb") as f:
88
+ self.docstore, self.index_to_id = pickle.load(f)
89
+ logger.info(f"Loaded FAISS index from {index_path} with {self.index.ntotal} vectors")
90
+ except Exception as e:
91
+ logger.warning(f"Failed to load FAISS index: {e}")
92
+
93
+ self.docstore = {}
94
+ self.index_to_id = {}
95
+
96
+ def _save(self):
97
+ """Save FAISS index and docstore to disk."""
98
+ if not self.path or not self.index:
99
+ return
100
+
101
+ try:
102
+ os.makedirs(self.path, exist_ok=True)
103
+ index_path = f"{self.path}/{self.collection_name}.faiss"
104
+ docstore_path = f"{self.path}/{self.collection_name}.pkl"
105
+
106
+ faiss.write_index(self.index, index_path)
107
+ with open(docstore_path, "wb") as f:
108
+ pickle.dump((self.docstore, self.index_to_id), f)
109
+ except Exception as e:
110
+ logger.warning(f"Failed to save FAISS index: {e}")
111
+
112
+ def _parse_output(self, scores, ids, limit=None) -> List[OutputData]:
113
+ """
114
+ Parse the output data.
115
+
116
+ Args:
117
+ scores: Similarity scores from FAISS.
118
+ ids: Indices from FAISS.
119
+ limit: Maximum number of results to return.
120
+
121
+ Returns:
122
+ List[OutputData]: Parsed output data.
123
+ """
124
+ if limit is None:
125
+ limit = len(ids)
126
+
127
+ results = []
128
+ for i in range(min(len(ids), limit)):
129
+ if ids[i] == -1: # FAISS returns -1 for empty results
130
+ continue
131
+
132
+ index_id = int(ids[i])
133
+ vector_id = self.index_to_id.get(index_id)
134
+ if vector_id is None:
135
+ continue
136
+
137
+ payload = self.docstore.get(vector_id)
138
+ if payload is None:
139
+ continue
140
+
141
+ payload_copy = payload.copy()
142
+
143
+ score = float(scores[i])
144
+ entry = OutputData(
145
+ id=vector_id,
146
+ score=score,
147
+ payload=payload_copy,
148
+ )
149
+ results.append(entry)
150
+
151
+ return results
152
+
153
+ def create_col(self, name: str, distance: str = None):
154
+ """
155
+ Create a new collection.
156
+
157
+ Args:
158
+ name (str): Name of the collection.
159
+ distance (str, optional): Distance metric to use. Overrides the distance_strategy
160
+ passed during initialization. Defaults to None.
161
+
162
+ Returns:
163
+ self: The FAISS instance.
164
+ """
165
+ distance_strategy = distance or self.distance_strategy
166
+
167
+ # Create index based on distance strategy
168
+ if distance_strategy.lower() == "inner_product" or distance_strategy.lower() == "cosine":
169
+ self.index = faiss.IndexFlatIP(self.embedding_model_dims)
170
+ else:
171
+ self.index = faiss.IndexFlatL2(self.embedding_model_dims)
172
+
173
+ self.collection_name = name
174
+
175
+ self._save()
176
+
177
+ return self
178
+
179
+ def insert(
180
+ self,
181
+ vectors: List[list],
182
+ payloads: Optional[List[Dict]] = None,
183
+ ids: Optional[List[str]] = None,
184
+ ):
185
+ """
186
+ Insert vectors into a collection.
187
+
188
+ Args:
189
+ vectors (List[list]): List of vectors to insert.
190
+ payloads (Optional[List[Dict]], optional): List of payloads corresponding to vectors. Defaults to None.
191
+ ids (Optional[List[str]], optional): List of IDs corresponding to vectors. Defaults to None.
192
+ """
193
+ if self.index is None:
194
+ raise ValueError("Collection not initialized. Call create_col first.")
195
+
196
+ if ids is None:
197
+ ids = [str(uuid.uuid4()) for _ in range(len(vectors))]
198
+
199
+ if payloads is None:
200
+ payloads = [{} for _ in range(len(vectors))]
201
+
202
+ if len(vectors) != len(ids) or len(vectors) != len(payloads):
203
+ raise ValueError("Vectors, payloads, and IDs must have the same length")
204
+
205
+ vectors_np = np.array(vectors, dtype=np.float32)
206
+
207
+ if self.normalize_L2 and self.distance_strategy.lower() == "euclidean":
208
+ faiss.normalize_L2(vectors_np)
209
+
210
+ self.index.add(vectors_np)
211
+
212
+ starting_idx = len(self.index_to_id)
213
+ for i, (vector_id, payload) in enumerate(zip(ids, payloads)):
214
+ self.docstore[vector_id] = payload.copy()
215
+ self.index_to_id[starting_idx + i] = vector_id
216
+
217
+ self._save()
218
+
219
+ logger.info(f"Inserted {len(vectors)} vectors into collection {self.collection_name}")
220
+
221
+ def search(
222
+ self, query: str, vectors: List[list], limit: int = 5, filters: Optional[Dict] = None
223
+ ) -> List[OutputData]:
224
+ """
225
+ Search for similar vectors.
226
+
227
+ Args:
228
+ query (str): Query (not used, kept for API compatibility).
229
+ vectors (List[list]): List of vectors to search.
230
+ limit (int, optional): Number of results to return. Defaults to 5.
231
+ filters (Optional[Dict], optional): Filters to apply to the search. Defaults to None.
232
+
233
+ Returns:
234
+ List[OutputData]: Search results.
235
+ """
236
+ if self.index is None:
237
+ raise ValueError("Collection not initialized. Call create_col first.")
238
+
239
+ query_vectors = np.array(vectors, dtype=np.float32)
240
+
241
+ if len(query_vectors.shape) == 1:
242
+ query_vectors = query_vectors.reshape(1, -1)
243
+
244
+ if self.normalize_L2 and self.distance_strategy.lower() == "euclidean":
245
+ faiss.normalize_L2(query_vectors)
246
+
247
+ fetch_k = limit * 2 if filters else limit
248
+ scores, indices = self.index.search(query_vectors, fetch_k)
249
+
250
+ results = self._parse_output(scores[0], indices[0], limit)
251
+
252
+ if filters:
253
+ filtered_results = []
254
+ for result in results:
255
+ if self._apply_filters(result.payload, filters):
256
+ filtered_results.append(result)
257
+ if len(filtered_results) >= limit:
258
+ break
259
+ results = filtered_results[:limit]
260
+
261
+ return results
262
+
263
+ def _apply_filters(self, payload: Dict, filters: Dict) -> bool:
264
+ """
265
+ Apply filters to a payload.
266
+
267
+ Args:
268
+ payload (Dict): Payload to filter.
269
+ filters (Dict): Filters to apply.
270
+
271
+ Returns:
272
+ bool: True if payload passes filters, False otherwise.
273
+ """
274
+ if not filters or not payload:
275
+ return True
276
+
277
+ for key, value in filters.items():
278
+ if key not in payload:
279
+ return False
280
+
281
+ if isinstance(value, list):
282
+ if payload[key] not in value:
283
+ return False
284
+ elif payload[key] != value:
285
+ return False
286
+
287
+ return True
288
+
289
+ def delete(self, vector_id: str):
290
+ """
291
+ Delete a vector by ID.
292
+
293
+ Args:
294
+ vector_id (str): ID of the vector to delete.
295
+ """
296
+ if self.index is None:
297
+ raise ValueError("Collection not initialized. Call create_col first.")
298
+
299
+ index_to_delete = None
300
+ for idx, vid in self.index_to_id.items():
301
+ if vid == vector_id:
302
+ index_to_delete = idx
303
+ break
304
+
305
+ if index_to_delete is not None:
306
+ self.docstore.pop(vector_id, None)
307
+ self.index_to_id.pop(index_to_delete, None)
308
+
309
+ self._save()
310
+
311
+ logger.info(f"Deleted vector {vector_id} from collection {self.collection_name}")
312
+ else:
313
+ logger.warning(f"Vector {vector_id} not found in collection {self.collection_name}")
314
+
315
+ def update(
316
+ self,
317
+ vector_id: str,
318
+ vector: Optional[List[float]] = None,
319
+ payload: Optional[Dict] = None,
320
+ ):
321
+ """
322
+ Update a vector and its payload.
323
+
324
+ Args:
325
+ vector_id (str): ID of the vector to update.
326
+ vector (Optional[List[float]], optional): Updated vector. Defaults to None.
327
+ payload (Optional[Dict], optional): Updated payload. Defaults to None.
328
+ """
329
+ if self.index is None:
330
+ raise ValueError("Collection not initialized. Call create_col first.")
331
+
332
+ if vector_id not in self.docstore:
333
+ raise ValueError(f"Vector {vector_id} not found")
334
+
335
+ current_payload = self.docstore[vector_id].copy()
336
+
337
+ if payload is not None:
338
+ self.docstore[vector_id] = payload.copy()
339
+ current_payload = self.docstore[vector_id].copy()
340
+
341
+ if vector is not None:
342
+ self.delete(vector_id)
343
+ self.insert([vector], [current_payload], [vector_id])
344
+ else:
345
+ self._save()
346
+
347
+ logger.info(f"Updated vector {vector_id} in collection {self.collection_name}")
348
+
349
+ def get(self, vector_id: str) -> OutputData:
350
+ """
351
+ Retrieve a vector by ID.
352
+
353
+ Args:
354
+ vector_id (str): ID of the vector to retrieve.
355
+
356
+ Returns:
357
+ OutputData: Retrieved vector.
358
+ """
359
+ if self.index is None:
360
+ raise ValueError("Collection not initialized. Call create_col first.")
361
+
362
+ if vector_id not in self.docstore:
363
+ return None
364
+
365
+ payload = self.docstore[vector_id].copy()
366
+
367
+ return OutputData(
368
+ id=vector_id,
369
+ score=None,
370
+ payload=payload,
371
+ )
372
+
373
+ def list_cols(self) -> List[str]:
374
+ """
375
+ List all collections.
376
+
377
+ Returns:
378
+ List[str]: List of collection names.
379
+ """
380
+ if not self.path:
381
+ return [self.collection_name] if self.index else []
382
+
383
+ try:
384
+ collections = []
385
+ path = Path(self.path).parent
386
+ for file in path.glob("*.faiss"):
387
+ collections.append(file.stem)
388
+ return collections
389
+ except Exception as e:
390
+ logger.warning(f"Failed to list collections: {e}")
391
+ return [self.collection_name] if self.index else []
392
+
393
+ def delete_col(self):
394
+ """
395
+ Delete a collection.
396
+ """
397
+ if self.path:
398
+ try:
399
+ index_path = f"{self.path}/{self.collection_name}.faiss"
400
+ docstore_path = f"{self.path}/{self.collection_name}.pkl"
401
+
402
+ if os.path.exists(index_path):
403
+ os.remove(index_path)
404
+ if os.path.exists(docstore_path):
405
+ os.remove(docstore_path)
406
+
407
+ logger.info(f"Deleted collection {self.collection_name}")
408
+ except Exception as e:
409
+ logger.warning(f"Failed to delete collection: {e}")
410
+
411
+ self.index = None
412
+ self.docstore = {}
413
+ self.index_to_id = {}
414
+
415
+ def col_info(self) -> Dict:
416
+ """
417
+ Get information about a collection.
418
+
419
+ Returns:
420
+ Dict: Collection information.
421
+ """
422
+ if self.index is None:
423
+ return {"name": self.collection_name, "count": 0}
424
+
425
+ return {
426
+ "name": self.collection_name,
427
+ "count": self.index.ntotal,
428
+ "dimension": self.index.d,
429
+ "distance": self.distance_strategy,
430
+ }
431
+
432
+ def list(self, filters: Optional[Dict] = None, limit: int = 100) -> List[OutputData]:
433
+ """
434
+ List all vectors in a collection.
435
+
436
+ Args:
437
+ filters (Optional[Dict], optional): Filters to apply to the list. Defaults to None.
438
+ limit (int, optional): Number of vectors to return. Defaults to 100.
439
+
440
+ Returns:
441
+ List[OutputData]: List of vectors.
442
+ """
443
+ if self.index is None:
444
+ return []
445
+
446
+ results = []
447
+ count = 0
448
+
449
+ for vector_id, payload in self.docstore.items():
450
+ if filters and not self._apply_filters(payload, filters):
451
+ continue
452
+
453
+ payload_copy = payload.copy()
454
+
455
+ results.append(
456
+ OutputData(
457
+ id=vector_id,
458
+ score=None,
459
+ payload=payload_copy,
460
+ )
461
+ )
462
+
463
+ count += 1
464
+ if count >= limit:
465
+ break
466
+
467
+ return [results]
468
+
469
+ def reset(self):
470
+ """Reset the index by deleting and recreating it."""
471
+ logger.warning(f"Resetting index {self.collection_name}...")
472
+ self.delete_col()
473
+ self.create_col(self.collection_name)
@@ -0,0 +1,179 @@
1
+ import logging
2
+ from typing import Dict, List, Optional
3
+
4
+ from pydantic import BaseModel
5
+
6
+ try:
7
+ from langchain_community.vectorstores import VectorStore
8
+ except ImportError:
9
+ raise ImportError(
10
+ "The 'langchain_community' library is required. Please install it using 'pip install langchain_community'."
11
+ )
12
+
13
+ from mem0.vector_stores.base import VectorStoreBase
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class OutputData(BaseModel):
19
+ id: Optional[str] # memory id
20
+ score: Optional[float] # distance
21
+ payload: Optional[Dict] # metadata
22
+
23
+
24
+ class Langchain(VectorStoreBase):
25
+ def __init__(self, client: VectorStore, collection_name: str = "mem0"):
26
+ self.client = client
27
+ self.collection_name = collection_name
28
+
29
+ def _parse_output(self, data: Dict) -> List[OutputData]:
30
+ """
31
+ Parse the output data.
32
+
33
+ Args:
34
+ data (Dict): Output data or list of Document objects.
35
+
36
+ Returns:
37
+ List[OutputData]: Parsed output data.
38
+ """
39
+ # Check if input is a list of Document objects
40
+ if isinstance(data, list) and all(hasattr(doc, "metadata") for doc in data if hasattr(doc, "__dict__")):
41
+ result = []
42
+ for doc in data:
43
+ entry = OutputData(
44
+ id=getattr(doc, "id", None),
45
+ score=None, # Document objects typically don't include scores
46
+ payload=getattr(doc, "metadata", {}),
47
+ )
48
+ result.append(entry)
49
+ return result
50
+
51
+ # Original format handling
52
+ keys = ["ids", "distances", "metadatas"]
53
+ values = []
54
+
55
+ for key in keys:
56
+ value = data.get(key, [])
57
+ if isinstance(value, list) and value and isinstance(value[0], list):
58
+ value = value[0]
59
+ values.append(value)
60
+
61
+ ids, distances, metadatas = values
62
+ max_length = max(len(v) for v in values if isinstance(v, list) and v is not None)
63
+
64
+ result = []
65
+ for i in range(max_length):
66
+ entry = OutputData(
67
+ id=ids[i] if isinstance(ids, list) and ids and i < len(ids) else None,
68
+ score=(distances[i] if isinstance(distances, list) and distances and i < len(distances) else None),
69
+ payload=(metadatas[i] if isinstance(metadatas, list) and metadatas and i < len(metadatas) else None),
70
+ )
71
+ result.append(entry)
72
+
73
+ return result
74
+
75
+ def create_col(self, name, vector_size=None, distance=None):
76
+ self.collection_name = name
77
+ return self.client
78
+
79
+ def insert(
80
+ self, vectors: List[List[float]], payloads: Optional[List[Dict]] = None, ids: Optional[List[str]] = None
81
+ ):
82
+ """
83
+ Insert vectors into the LangChain vectorstore.
84
+ """
85
+ # Check if client has add_embeddings method
86
+ if hasattr(self.client, "add_embeddings"):
87
+ # Some LangChain vectorstores have a direct add_embeddings method
88
+ self.client.add_embeddings(embeddings=vectors, metadatas=payloads, ids=ids)
89
+ else:
90
+ # Fallback to add_texts method
91
+ texts = [payload.get("data", "") for payload in payloads] if payloads else [""] * len(vectors)
92
+ self.client.add_texts(texts=texts, metadatas=payloads, ids=ids)
93
+
94
+ def search(self, query: str, vectors: List[List[float]], limit: int = 5, filters: Optional[Dict] = None):
95
+ """
96
+ Search for similar vectors in LangChain.
97
+ """
98
+ # For each vector, perform a similarity search
99
+ if filters:
100
+ results = self.client.similarity_search_by_vector(embedding=vectors, k=limit, filter=filters)
101
+ else:
102
+ results = self.client.similarity_search_by_vector(embedding=vectors, k=limit)
103
+
104
+ final_results = self._parse_output(results)
105
+ return final_results
106
+
107
+ def delete(self, vector_id):
108
+ """
109
+ Delete a vector by ID.
110
+ """
111
+ self.client.delete(ids=[vector_id])
112
+
113
+ def update(self, vector_id, vector=None, payload=None):
114
+ """
115
+ Update a vector and its payload.
116
+ """
117
+ self.delete(vector_id)
118
+ self.insert(vector, payload, [vector_id])
119
+
120
+ def get(self, vector_id):
121
+ """
122
+ Retrieve a vector by ID.
123
+ """
124
+ docs = self.client.get_by_ids([vector_id])
125
+ if docs and len(docs) > 0:
126
+ doc = docs[0]
127
+ return self._parse_output([doc])[0]
128
+ return None
129
+
130
+ def list_cols(self):
131
+ """
132
+ List all collections.
133
+ """
134
+ # LangChain doesn't have collections
135
+ return [self.collection_name]
136
+
137
+ def delete_col(self):
138
+ """
139
+ Delete a collection.
140
+ """
141
+ logger.warning("Deleting collection")
142
+ if hasattr(self.client, "delete_collection"):
143
+ self.client.delete_collection()
144
+ elif hasattr(self.client, "reset_collection"):
145
+ self.client.reset_collection()
146
+ else:
147
+ self.client.delete(ids=None)
148
+
149
+ def col_info(self):
150
+ """
151
+ Get information about a collection.
152
+ """
153
+ return {"name": self.collection_name}
154
+
155
+ def list(self, filters=None, limit=None):
156
+ """
157
+ List all vectors in a collection.
158
+ """
159
+ try:
160
+ if hasattr(self.client, "_collection") and hasattr(self.client._collection, "get"):
161
+ # Convert mem0 filters to Chroma where clause if needed
162
+ where_clause = None
163
+ if filters and "user_id" in filters:
164
+ where_clause = {"user_id": filters["user_id"]}
165
+
166
+ result = self.client._collection.get(where=where_clause, limit=limit)
167
+
168
+ # Convert the result to the expected format
169
+ if result and isinstance(result, dict):
170
+ return [self._parse_output(result)]
171
+ return []
172
+ except Exception as e:
173
+ logger.error(f"Error listing vectors from Chroma: {e}")
174
+ return []
175
+
176
+ def reset(self):
177
+ """Reset the index by deleting and recreating it."""
178
+ logger.warning(f"Resetting collection: {self.collection_name}")
179
+ self.delete_col()