Skip to content

cookbook.stores

cookbook.stores

Thin wrappers over Qdrant, LanceDB, and Chroma with one identical API.

The point: every recipe uses the same .add() / .search() / .hybrid_search() signatures, so swapping vector stores is a one-line change. The wrappers are intentionally minimal — they expose only what the recipes need, no plumbing for sharding, replication, or schema migrations.

Hit dataclass

One retrieval result.

Source code in cookbook/stores.py
@dataclass(frozen=True)
class Hit:
    """One retrieval result."""

    doc_id: str
    text: str
    score: float
    metadata: dict

VectorBackend

Bases: ABC

Shared API across Qdrant, LanceDB, and Chroma backends.

Source code in cookbook/stores.py
class VectorBackend(ABC):
    """Shared API across Qdrant, LanceDB, and Chroma backends."""

    @abstractmethod
    def add(
        self,
        texts: Sequence[str],
        vectors: Sequence[Sequence[float]],
        metadatas: Sequence[dict] | None = None,
        ids: Sequence[str] | None = None,
    ) -> None: ...

    @abstractmethod
    def search(self, query_vector: Sequence[float], top_k: int = 5) -> list[Hit]: ...

    def hybrid_search(
        self,
        query_vector: Sequence[float],
        query_text: str,
        top_k: int = 5,
    ) -> list[Hit]:
        """Default fallback: dense only. Backends that support sparse override."""
        return self.search(query_vector, top_k=top_k)

Default fallback: dense only. Backends that support sparse override.

Source code in cookbook/stores.py
def hybrid_search(
    self,
    query_vector: Sequence[float],
    query_text: str,
    top_k: int = 5,
) -> list[Hit]:
    """Default fallback: dense only. Backends that support sparse override."""
    return self.search(query_vector, top_k=top_k)