Pre-submission checklist
Bug Description
GeneralTextMemory.search() reverses Qdrant's relevance ordering when the collection uses Euclidean distance. Qdrant returns the nearest matches first, but the memory layer sorts every score descending. With Euclidean scores, that puts the farthest match first within the selected top-k.
For distances [0.0, 0.7071067811865476, 2.0], the expected order is near, middle, far. The current code returns far, middle, near. With top_k=2, it returns middle, near.
How to Reproduce
Run this from a checkout with the core dependencies and qdrant-client installed (PYTHONPATH=src python reproduce.py). It uses embedded Qdrant and fixed embeddings; no model, API key, or external service is needed.
from tempfile import TemporaryDirectory
from unittest.mock import MagicMock, patch
from memos.configs.memory import GeneralTextMemoryConfig
from memos.memories.textual.general import GeneralTextMemory
from memos.memories.textual.item import TextualMemoryItem
vectors = {"query": [1.0, 0.0], "near": [1.0, 0.0], "middle": [0.5, 0.5], "far": [-1.0, 0.0]}
embedder = MagicMock()
embedder.embed.side_effect = lambda texts: [vectors[text] for text in texts]
with TemporaryDirectory() as directory:
config = GeneralTextMemoryConfig(
extractor_llm={"backend": "ollama", "config": {"model_name_or_path": "test"}},
embedder={"backend": "ollama", "config": {"model_name_or_path": "test"}},
vector_db={"backend": "qdrant", "config": {
"collection_name": "repro", "vector_dimension": 2,
"distance_metric": "euclidean", "path": directory,
}},
)
with patch("memos.memories.textual.general.LLMFactory.from_config"), patch(
"memos.memories.textual.general.EmbedderFactory.from_config", return_value=embedder
):
memory = GeneralTextMemory(config)
try:
memory.add([TextualMemoryItem(memory=text) for text in ["middle", "far", "near"]])
print("Backend distances:", [item.score for item in memory.vector_db.search(vectors["query"], 3)])
print("Memory top 3:", [item.memory for item in memory.search("query", 3)])
print("Memory top 2:", [item.memory for item in memory.search("query", 2)])
finally:
memory.vector_db.client.close()
Actual output on 176d4f676a93e0e34ca9fd50091eff5ad3236506:
Backend distances: [0.0, 0.7071067811865476, 2.0]
Memory top 3: ['far', 'middle', 'near']
Memory top 2: ['middle', 'near']
Expected:
Memory top 3: ['near', 'middle', 'far']
Memory top 2: ['near', 'middle']
Environment
- MemOS: main and dev-v2.0.35 at
176d4f676a93e0e34ca9fd50091eff5ad3236506 (package version 2.0.33)
- Python 3.12.14
- macOS 15.5, ARM64
- qdrant-client 1.19.1, embedded mode
Additional Context
The extra descending sort is in src/memos/memories/textual/general.py. Qdrant already orders the results according to the collection metric. Qdrant's search documentation confirms that larger Euclidean scores are more distant.
I have a small fix that preserves the backend order and a regression using real local Qdrant for Euclidean, cosine, and dot-product collections with both top_k=2 and top_k=3.
Willingness to Implement
Pre-submission checklist
Bug Description
GeneralTextMemory.search()reverses Qdrant's relevance ordering when the collection uses Euclidean distance. Qdrant returns the nearest matches first, but the memory layer sorts every score descending. With Euclidean scores, that puts the farthest match first within the selected top-k.For distances
[0.0, 0.7071067811865476, 2.0], the expected order isnear, middle, far. The current code returnsfar, middle, near. Withtop_k=2, it returnsmiddle, near.How to Reproduce
Run this from a checkout with the core dependencies and
qdrant-clientinstalled (PYTHONPATH=src python reproduce.py). It uses embedded Qdrant and fixed embeddings; no model, API key, or external service is needed.Actual output on
176d4f676a93e0e34ca9fd50091eff5ad3236506:Expected:
Environment
176d4f676a93e0e34ca9fd50091eff5ad3236506(package version 2.0.33)Additional Context
The extra descending sort is in
src/memos/memories/textual/general.py. Qdrant already orders the results according to the collection metric. Qdrant's search documentation confirms that larger Euclidean scores are more distant.I have a small fix that preserves the backend order and a regression using real local Qdrant for Euclidean, cosine, and dot-product collections with both
top_k=2andtop_k=3.Willingness to Implement