Fix: Add missing filter methods to AsyncMemory (#3624)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1895,6 +1895,102 @@ class AsyncMemory(MemoryBase):
|
|||||||
|
|
||||||
return {"results": original_memories}
|
return {"results": original_memories}
|
||||||
|
|
||||||
|
def _process_metadata_filters(self, metadata_filters: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Process enhanced metadata filters and convert them to vector store compatible format.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
metadata_filters: Enhanced metadata filters with operators
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict of processed filters compatible with vector store
|
||||||
|
"""
|
||||||
|
processed_filters = {}
|
||||||
|
|
||||||
|
def process_condition(key: str, condition: Any) -> Dict[str, Any]:
|
||||||
|
if not isinstance(condition, dict):
|
||||||
|
# Simple equality: {"key": "value"}
|
||||||
|
if condition == "*":
|
||||||
|
# Wildcard: match everything for this field (implementation depends on vector store)
|
||||||
|
return {key: "*"}
|
||||||
|
return {key: condition}
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for operator, value in condition.items():
|
||||||
|
# Map platform operators to universal format that can be translated by each vector store
|
||||||
|
operator_map = {
|
||||||
|
"eq": "eq", "ne": "ne", "gt": "gt", "gte": "gte",
|
||||||
|
"lt": "lt", "lte": "lte", "in": "in", "nin": "nin",
|
||||||
|
"contains": "contains", "icontains": "icontains"
|
||||||
|
}
|
||||||
|
|
||||||
|
if operator in operator_map:
|
||||||
|
result[key] = {operator_map[operator]: value}
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported metadata filter operator: {operator}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
for key, value in metadata_filters.items():
|
||||||
|
if key == "AND":
|
||||||
|
# Logical AND: combine multiple conditions
|
||||||
|
if not isinstance(value, list):
|
||||||
|
raise ValueError("AND operator requires a list of conditions")
|
||||||
|
for condition in value:
|
||||||
|
for sub_key, sub_value in condition.items():
|
||||||
|
processed_filters.update(process_condition(sub_key, sub_value))
|
||||||
|
elif key == "OR":
|
||||||
|
# Logical OR: Pass through to vector store for implementation-specific handling
|
||||||
|
if not isinstance(value, list) or not value:
|
||||||
|
raise ValueError("OR operator requires a non-empty list of conditions")
|
||||||
|
# Store OR conditions in a way that vector stores can interpret
|
||||||
|
processed_filters["$or"] = []
|
||||||
|
for condition in value:
|
||||||
|
or_condition = {}
|
||||||
|
for sub_key, sub_value in condition.items():
|
||||||
|
or_condition.update(process_condition(sub_key, sub_value))
|
||||||
|
processed_filters["$or"].append(or_condition)
|
||||||
|
elif key == "NOT":
|
||||||
|
# Logical NOT: Pass through to vector store for implementation-specific handling
|
||||||
|
if not isinstance(value, list) or not value:
|
||||||
|
raise ValueError("NOT operator requires a non-empty list of conditions")
|
||||||
|
processed_filters["$not"] = []
|
||||||
|
for condition in value:
|
||||||
|
not_condition = {}
|
||||||
|
for sub_key, sub_value in condition.items():
|
||||||
|
not_condition.update(process_condition(sub_key, sub_value))
|
||||||
|
processed_filters["$not"].append(not_condition)
|
||||||
|
else:
|
||||||
|
processed_filters.update(process_condition(key, value))
|
||||||
|
|
||||||
|
return processed_filters
|
||||||
|
|
||||||
|
def _has_advanced_operators(self, filters: Dict[str, Any]) -> bool:
|
||||||
|
"""
|
||||||
|
Check if filters contain advanced operators that need special processing.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filters: Dictionary of filters to check
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if advanced operators are detected
|
||||||
|
"""
|
||||||
|
if not isinstance(filters, dict):
|
||||||
|
return False
|
||||||
|
|
||||||
|
for key, value in filters.items():
|
||||||
|
# Check for platform-style logical operators
|
||||||
|
if key in ["AND", "OR", "NOT"]:
|
||||||
|
return True
|
||||||
|
# Check for comparison operators (without $ prefix for universal compatibility)
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for op in value.keys():
|
||||||
|
if op in ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "contains", "icontains"]:
|
||||||
|
return True
|
||||||
|
# Check for wildcard values
|
||||||
|
if value == "*":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
async def _search_vector_store(self, query, filters, limit, threshold: Optional[float] = None):
|
async def _search_vector_store(self, query, filters, limit, threshold: Optional[float] = None):
|
||||||
embeddings = await asyncio.to_thread(self.embedding_model.embed, query, "search")
|
embeddings = await asyncio.to_thread(self.embedding_model.embed, query, "search")
|
||||||
memories = await asyncio.to_thread(
|
memories = await asyncio.to_thread(
|
||||||
|
|||||||
Reference in New Issue
Block a user