feat: add LanceDB evaluation backend scaffold

This commit is contained in:
LLLin000 2026-07-05 14:19:37 +08:00
parent 9d5a008df1
commit e23f35114c
5 changed files with 108 additions and 0 deletions

View file

@ -26,4 +26,10 @@ class VectorBackend(Protocol):
def delete_paper(self, paper_id: str) -> int: ...
def capabilities(self) -> dict:
"""Return a dict describing what this backend supports.
Keys: backend, supports_hybrid, supports_multimodal.
"""
...
def health(self) -> dict: ...

View file

@ -100,3 +100,10 @@ class ChromaBackend:
"error": str(exc),
"corrupted": "hnsw" in err_lower or "corrupt" in err_lower,
}
def capabilities(self) -> dict:
return {
"backend": "chroma",
"supports_hybrid": False,
"supports_multimodal": False,
}

View file

@ -0,0 +1,22 @@
from __future__ import annotations
import lancedb
class LanceBackend:
"""LanceDB-based vector backend (evaluation seam, not the default).
This backend advertises hybrid and multimodal capabilities that the
Chroma backend does not support, making it suitable for evaluation
without switching the production default.
"""
def __init__(self, dataset_path: str) -> None:
self.db = lancedb.connect(str(dataset_path))
def capabilities(self) -> dict:
return {
"backend": "lancedb",
"supports_hybrid": True,
"supports_multimodal": True,
}

View file

@ -9,6 +9,35 @@ from paperforge.embedding.backends import get_vector_backend
logger = logging.getLogger(__name__)
def _module_available(name: str) -> bool:
"""Check whether *name* can be imported without side effects."""
import importlib
try:
importlib.import_module(name)
return True
except ImportError:
return False
def get_available_backends() -> dict[str, dict]:
"""Return a dict of known backends with installation and selection status."""
return {
"chroma": {
"installed": True,
"selected": True,
"supports_hybrid": False,
"supports_multimodal": False,
},
"lancedb": {
"installed": _module_available("lancedb"),
"selected": False,
"supports_hybrid": True,
"supports_multimodal": True,
},
}
def get_embed_status(vault: Path) -> dict:
"""Get vector DB status. API-only mode.

View file

@ -0,0 +1,44 @@
from __future__ import annotations
import pytest
class TestLanceBackendCapabilities:
"""LanceDB backend capability reporting (optional — skips when lancedb missing)."""
def test_lance_backend_advertises_file_based_capabilities(self):
pytest.importorskip("lancedb")
from paperforge.embedding.backends.lance_backend import LanceBackend
backend = LanceBackend("/tmp/lance")
caps = backend.capabilities()
assert caps["backend"] == "lancedb"
assert caps["supports_hybrid"] is True
assert caps["supports_multimodal"] is True
class TestAvailableBackends:
"""Status-level backend enumeration (always available, no lancedb dependency)."""
def test_get_available_backends_lists_chroma_and_lance(self):
from paperforge.embedding.status import get_available_backends
backends = get_available_backends()
assert "chroma" in backends
assert backends["chroma"]["installed"] is True
assert backends["chroma"]["selected"] is True
assert "lancedb" in backends
assert backends["lancedb"]["installed"] is False
assert backends["lancedb"]["selected"] is False
assert backends["lancedb"]["supports_hybrid"] is True
assert backends["lancedb"]["supports_multimodal"] is True
def test_get_available_backends_returns_dict_with_expected_keys(self):
from paperforge.embedding.status import get_available_backends
backends = get_available_backends()
for name, info in backends.items():
for key in ("installed", "selected", "supports_hybrid", "supports_multimodal"):
assert key in info, f"{name} missing {key}"