Mem0 1.0.0 (#3545)

This commit is contained in:
Parshva Daftari
2025-10-16 15:50:20 +05:30
committed by GitHub
parent 8f5151c344
commit 394203d1b5
77 changed files with 3445 additions and 990 deletions
+74 -9
View File
@@ -254,14 +254,79 @@ class ChromaDB(VectorStoreBase):
Returns:
dict[str, any]: Properly formatted where clause for ChromaDB.
"""
# If only one filter is supplied, return it as is
# (no need to wrap in $and based on chroma docs)
if where is None:
return {}
if len(where.keys()) <= 1:
return where
where_filters = []
for k, v in where.items():
if isinstance(v, str):
where_filters.append({k: v})
return {"$and": where_filters}
def convert_condition(key: str, value: any) -> dict:
"""Convert universal filter format to ChromaDB format."""
if value == "*":
# Wildcard - match any value (ChromaDB doesn't have direct wildcard, so we skip this filter)
return None
elif isinstance(value, dict):
# Handle comparison operators
chroma_condition = {}
for op, val in value.items():
if op == "eq":
chroma_condition[key] = {"$eq": val}
elif op == "ne":
chroma_condition[key] = {"$ne": val}
elif op == "gt":
chroma_condition[key] = {"$gt": val}
elif op == "gte":
chroma_condition[key] = {"$gte": val}
elif op == "lt":
chroma_condition[key] = {"$lt": val}
elif op == "lte":
chroma_condition[key] = {"$lte": val}
elif op == "in":
chroma_condition[key] = {"$in": val}
elif op == "nin":
chroma_condition[key] = {"$nin": val}
elif op in ["contains", "icontains"]:
# ChromaDB doesn't support contains, fallback to equality
chroma_condition[key] = {"$eq": val}
else:
# Unknown operator, treat as equality
chroma_condition[key] = {"$eq": val}
return chroma_condition
else:
# Simple equality
return {key: {"$eq": value}}
processed_filters = []
for key, value in where.items():
if key == "$or":
# Handle OR conditions
or_conditions = []
for condition in value:
or_condition = {}
for sub_key, sub_value in condition.items():
converted = convert_condition(sub_key, sub_value)
if converted:
or_condition.update(converted)
if or_condition:
or_conditions.append(or_condition)
if len(or_conditions) > 1:
processed_filters.append({"$or": or_conditions})
elif len(or_conditions) == 1:
processed_filters.append(or_conditions[0])
elif key == "$not":
# Handle NOT conditions - ChromaDB doesn't have direct NOT, so we'll skip for now
continue
else:
# Regular condition
converted = convert_condition(key, value)
if converted:
processed_filters.append(converted)
# Return appropriate format based on number of conditions
if len(processed_filters) == 0:
return {}
elif len(processed_filters) == 1:
return processed_filters[0]
else:
return {"$and": processed_filters}
+34 -23
View File
@@ -113,15 +113,26 @@ class OpenSearchDB(VectorStoreBase):
if payloads is None:
payloads = [{} for _ in range(len(vectors))]
results = []
for i, (vec, id_) in enumerate(zip(vectors, ids)):
body = {
"vector_field": vec,
"payload": payloads[i],
"id": id_,
}
self.client.index(index=self.collection_name, body=body)
results = []
try:
self.client.index(index=self.collection_name, body=body)
# Force refresh to make documents immediately searchable for tests
self.client.indices.refresh(index=self.collection_name)
results.append(OutputData(
id=id_,
score=1.0, # No score for inserts
payload=payloads[i]
))
except Exception as e:
logger.error(f"Error inserting vector {id_}: {e}")
raise
return results
@@ -157,15 +168,19 @@ class OpenSearchDB(VectorStoreBase):
else:
query_body["query"] = knn_query
# Execute search
response = self.client.search(index=self.collection_name, body=query_body)
try:
# Execute search
response = self.client.search(index=self.collection_name, body=query_body)
hits = response["hits"]["hits"]
results = [
OutputData(id=hit["_source"].get("id"), score=hit["_score"], payload=hit["_source"].get("payload", {}))
for hit in hits
]
return results
hits = response["hits"]["hits"]
results = [
OutputData(id=hit["_source"].get("id"), score=hit["_score"], payload=hit["_source"].get("payload", {}))
for hit in hits[:limit] # Ensure we don't exceed limit
]
return results
except Exception as e:
logger.error(f"Error during search: {e}")
return []
def delete(self, vector_id: str) -> None:
"""Delete a vector by custom ID."""
@@ -213,12 +228,6 @@ class OpenSearchDB(VectorStoreBase):
def get(self, vector_id: str) -> Optional[OutputData]:
"""Retrieve a vector by ID."""
try:
# First check if index exists
if not self.client.indices.exists(index=self.collection_name):
logger.info(f"Index {self.collection_name} does not exist, creating it...")
self.create_col(self.collection_name, self.embedding_model_dims)
return None
search_query = {"query": {"term": {"id": vector_id}}}
response = self.client.search(index=self.collection_name, body=search_query)
@@ -265,14 +274,16 @@ class OpenSearchDB(VectorStoreBase):
response = self.client.search(index=self.collection_name, body=query)
hits = response["hits"]["hits"]
return [
[
OutputData(id=hit["_source"].get("id"), score=1.0, payload=hit["_source"].get("payload", {}))
for hit in hits
]
# Return a flat list, not a nested array
results = [
OutputData(id=hit["_source"].get("id"), score=1.0, payload=hit["_source"].get("payload", {}))
for hit in hits
]
except Exception:
return [results] # VectorStore expects tuple/list format
except Exception as e:
logger.error(f"Error listing vectors: {e}")
return []
def reset(self):
"""Reset the index by deleting and recreating it."""
@@ -65,10 +65,16 @@ class GoogleMatchingEngine(VectorStoreBase):
"project": self.project_id,
"location": self.region,
}
# Support both credentials_path and service_account_json
if hasattr(config, "credentials_path") and config.credentials_path:
logger.debug("Using credentials from: %s", config.credentials_path)
logger.debug("Using credentials from file: %s", config.credentials_path)
credentials = service_account.Credentials.from_service_account_file(config.credentials_path)
init_args["credentials"] = credentials
elif hasattr(config, "service_account_json") and config.service_account_json:
logger.debug("Using credentials from provided JSON dict")
credentials = service_account.Credentials.from_service_account_info(config.service_account_json)
init_args["credentials"] = credentials
try:
aiplatform.init(**init_args)