Files
Hy-Memory/tests/test_faiss_get_embeddings.py
d6890793ec fix(sdk): repair FAISS embedding lookup, dedup pairing, and episode summaries (#84)
* fix(faiss): reconstruct embeddings for dedup and episode summaries

Normal upsert paths store only the payload and the FAISS index position.
get_embeddings() looked for an "embedding" key that only update_payload
ever wrote, so it returned an empty mapping for every upserted node. Search
dedup and episode summaries therefore saw no vectors on the FAISS backend.

Reconstruct the vector from the live index position, guarded by the same
_idx_to_id check search() uses, and keep the stored fallback for nodes
updated through update_payload. Add a regression test.

Co-Authored-By: Claude Code <noreply@anthropic.com>

* fix(search): pair dedup markers with their source search result

The public items are filtered by the requested layers, but the private
dedup view was built with zip(result.memories, public_items). Once one
result is filtered out every later _memory_kind shifts by one, so a
canonical_state can enter fuzzy dedup and be shadowed, or an ordinary fact
can silently skip dedup.

Build both views in the same loop, from the same source element, and add a
regression test for the filtered case.

Co-Authored-By: Claude Code <noreply@anthropic.com>

* fix(episode): use the writer's vector store for background summaries

_schedule_episode_summary accessed self.vector_store, which MemoryWriter
never defines. The AttributeError was swallowed by the task's except block,
so every closed episode silently skipped its L3 summary.

Resolve the store through the existing async _get_vector_store() accessor
and cover the scheduled task with a regression test.

Co-Authored-By: Claude Code <noreply@anthropic.com>

---------

Co-authored-by: qiye.sjh <shijiahao_0802@youzan.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-10-21 12:19:28 +08:00

58 lines
1.8 KiB
Python

# -*- coding: utf-8 -*-
"""FAISS 后端的 get_embeddings 必须能取回普通 upsert 写入的向量。
``_data`` 只保存 payload 与内部下标,向量本体在 FAISS index 里。去重和
episode 摘要依赖 get_embeddings,取不到向量会让这些能力静默失效。
"""
import asyncio
import pytest
pytest.importorskip("faiss")
from hy_memory.config import MemoryConfig # noqa: E402
from hy_memory.data.vector_store_faiss import FaissVectorStore # noqa: E402
from hy_memory.models.memory import MemoryLayer, MemoryNode # noqa: E402
def _config(tmp_path):
return MemoryConfig.from_dict({
"vector_store": {
"provider": "faiss",
"collection_name": "get_embeddings_test",
"persist_directory": str(tmp_path / "faiss"),
"embedding_dims": 4,
},
})
def test_get_embeddings_reconstructs_upserted_and_updated_vectors(tmp_path):
async def _run():
store = FaissVectorStore(_config(tmp_path))
await store.initialize()
try:
node = MemoryNode(
user_id="u1",
agent_id="a1",
session_id="s1",
source="default",
layer=MemoryLayer.L1_RAW,
content="hello",
embedding=[1.0, 0.0, 0.0, 0.0],
)
await store.upsert(node)
got = await store.get_embeddings([node.node_id])
assert got[node.node_id] == pytest.approx([1.0, 0.0, 0.0, 0.0])
assert await store.update_embedding(node.node_id, [0.0, 1.0, 0.0, 0.0])
got = await store.get_embeddings([node.node_id])
assert got[node.node_id] == pytest.approx([0.0, 1.0, 0.0, 0.0])
assert await store.get_embeddings(["missing"]) == {}
finally:
await store.close()
asyncio.run(_run())