Ctrl K
ComunidadGuías…

test-int/semantic/test_milvus_lite.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Real Milvus Lite contract verification."""

from __future__ import annotations

import subprocess
import sys

import pytest

pytest.importorskip("pymilvus", reason="install basic-memory[milvus] to test Milvus")

from basic_memory.repository.milvus_config import MilvusSettings
from basic_memory.repository.milvus_index import MilvusVectorIndex
from basic_memory.repository.search_scope import ProjectScope
from basic_memory.repository.semantic_vector_index import (
    VectorDeletion,
    VectorIndexScope,
    VectorKey,
    VectorRecord,
)

pytestmark = [
    pytest.mark.semantic,
    pytest.mark.skipif(sys.platform == "win32", reason="Milvus Lite does not support Windows"),
]

PROJECT = 7
PROJECTS = ProjectScope.single(PROJECT)


_RESTART_SCRIPT = """
import asyncio
import sys

from basic_memory.repository.milvus_config import MilvusSettings
from basic_memory.repository.milvus_index import MilvusVectorIndex
from basic_memory.repository.search_scope import ProjectScope
from basic_memory.repository.semantic_vector_index import (
    VectorIndexScope,
    VectorKey,
    VectorRecord,
)


async def main() -> None:
    phase, database_path = sys.argv[1:]
    scope = VectorIndexScope(
        namespace="restart-database",
        embedding_identity="Stub:model",
        dimensions=3,
    )
    key = VectorKey(entity_id=1, chunk_key="summary:0")
    index = MilvusVectorIndex(scope, MilvusSettings(uri=database_path))

    if phase == "write":
        await index.upsert(
            7,
            [
                VectorRecord(
                    key=key,
                    source_hash="auth-v1",
                    values=(1.0, 0.0, 0.0),
                )
            ],
        )
        return

    await index.delete_orphans(7, [key])
    matches = await index.search((1.0, 0.0, 0.0), limit=1, projects=ProjectScope.single(7))
    assert [match.key for match in matches] == [key]


asyncio.run(main())
"""


def _run_restart_phase(phase: str, database_path: str) -> None:
    result = subprocess.run(
        [sys.executable, "-c", _RESTART_SCRIPT, phase, database_path],
        check=False,
        capture_output=True,
        text=True,
    )
    assert result.returncode == 0, result.stderr


@pytest.mark.asyncio
async def test_milvus_lite_vector_lifecycle(tmp_path) -> None:
    scope = VectorIndexScope(
        namespace="integration-database",
        embedding_identity="Stub:model",
        dimensions=3,
    )
    index = MilvusVectorIndex(
        scope,
        MilvusSettings(uri=str(tmp_path / "vectors.db")),
    )
    auth_key = VectorKey(entity_id=1, chunk_key="summary:0")
    database_key = VectorKey(entity_id=2, chunk_key="summary:0")
    await index.upsert(
        PROJECT,
        [
            VectorRecord(
                key=auth_key,
                source_hash="auth-v1",
                values=(1.0, 0.0, 0.0),
            ),
            VectorRecord(
                key=database_key,
                source_hash="database-v1",
                values=(0.0, 1.0, 0.0),
            ),
        ],
    )

    matches = await index.search((1.0, 0.0, 0.0), limit=2, projects=PROJECTS)
    assert matches[0].key == auth_key
    assert matches[0].similarity == pytest.approx(1.0)

    await index.delete(PROJECT, [VectorDeletion(key=auth_key, source_hash="stale-generation")])
    assert (await index.search((1.0, 0.0, 0.0), limit=2, projects=PROJECTS))[0].key == auth_key

    await index.delete(PROJECT, [VectorDeletion(key=auth_key, source_hash="auth-v1")])
    assert [
        match.key for match in await index.search((1.0, 0.0, 0.0), limit=2, projects=PROJECTS)
    ] == [database_key]

    await index.delete_orphans(PROJECT, [])
    assert await index.search((1.0, 0.0, 0.0), limit=2, projects=PROJECTS) == []


def test_milvus_lite_reloads_collection_after_process_restart(tmp_path) -> None:
    database_path = str(tmp_path / "restart-vectors.db")

    _run_restart_phase("write", database_path)
    _run_restart_phase("read", database_path)