Skip to content

Reranker Interface Contract

Branch: 015-vectorstore-reranking Date: 2025-12-23

Overview

This document defines the internal interface contract for reranker implementations.

Key Design Decision: vLLM's rerank endpoints are fully Cohere API compatible, so a single CohereReranker implementation handles both providers. The difference is only in the base_url parameter.

BaseReranker Abstract Interface

Location: src/holodeck/lib/rerankers/base.py

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any


@dataclass
class RerankResult:
    """Result from reranking a single document.

    Attributes:
        index: Original index in the input documents list
        score: Relevance score normalized to [0.0, 1.0]
        document: Original document text (optional, for reference)
    """
    index: int
    score: float
    document: str | None = None


class BaseReranker(ABC):
    """Abstract base class for reranker implementations.

    All reranker providers must implement this interface to ensure
    consistent behavior across providers.
    """

    @abstractmethod
    async def rerank(
        self,
        query: str,
        documents: list[str],
        top_n: int | None = None,
    ) -> list[RerankResult]:
        """Rerank documents by relevance to query.

        Args:
            query: The search query to rank documents against.
            documents: List of document texts to rerank.
            top_n: Maximum number of results to return. If None, returns
                all documents reranked.

        Returns:
            List of RerankResult sorted by descending relevance score.
            The list length is min(len(documents), top_n) if top_n is set.

        Raises:
            RerankerError: If reranking fails (connection error, auth error, etc.)
        """
        pass

    @abstractmethod
    async def validate(self) -> None:
        """Validate reranker configuration and connectivity.

        Called during agent initialization to fail fast on configuration errors.

        Raises:
            RerankerError: If configuration is invalid or service unreachable.
        """
        pass

    @property
    @abstractmethod
    def provider(self) -> str:
        """Return the provider name for logging and metrics."""
        pass

Error Handling Contract

Location: src/holodeck/lib/rerankers/base.py

Error Handling Behavior

The recoverable attribute determines how VectorStoreTool handles reranker errors:

Error Class recoverable Behavior
RerankerAuthError False Fail fast - raise error to caller
RerankerConnectionError True Fallback - return original vector search results
RerankerRateLimitError True Fallback - return original vector search results
RerankerError (base) False (default) Fail fast - unless explicitly set recoverable

Rationale: Authentication and configuration errors (non-recoverable) should fail fast to surface problems early. Transient errors (network, rate limits) should gracefully degrade to vector search results to maintain service availability.

class RerankerError(Exception):
    """Base exception for reranker errors.

    Attributes:
        message: Human-readable error description
        provider: Reranker provider that raised the error
        recoverable: Whether the error is transient and may succeed on retry.
                     When True, VectorStoreTool falls back to vector search results.
                     When False, VectorStoreTool raises the error to fail fast.
    """

    def __init__(
        self,
        message: str,
        provider: str,
        recoverable: bool = False,
    ) -> None:
        super().__init__(message)
        self.message = message
        self.provider = provider
        self.recoverable = recoverable


class RerankerAuthError(RerankerError):
    """Authentication or authorization error.

    Raised when API key is invalid or lacks permissions.
    Not recoverable without configuration change.

    Behavior: Fail fast (raise to caller)
    """

    def __init__(self, message: str, provider: str) -> None:
        super().__init__(message, provider, recoverable=False)


class RerankerConnectionError(RerankerError):
    """Network or connection error.

    Raised when reranker service is unreachable or request times out.
    May be recoverable on retry.

    Behavior: Fallback to vector search results
    """

    def __init__(self, message: str, provider: str) -> None:
        super().__init__(message, provider, recoverable=True)


class RerankerRateLimitError(RerankerError):
    """Rate limit exceeded.

    Raised when API rate limit is exceeded.
    Recoverable after backoff period.

    Behavior: Fallback to vector search results
    """

    def __init__(self, message: str, provider: str, retry_after: float | None = None) -> None:
        super().__init__(message, provider, recoverable=True)
        self.retry_after = retry_after

CohereReranker Implementation Contract

Location: src/holodeck/lib/rerankers/cohere.py

This single implementation handles both Cohere cloud and vLLM servers.

class CohereReranker(BaseReranker):
    """Cohere reranker implementation (works with both Cohere cloud and vLLM).

    vLLM's /v1/rerank endpoint is fully Cohere API compatible, so this single
    implementation handles both providers by varying the base_url.

    Args:
        api_key: Cohere API key (ignored for vLLM)
        model: Rerank model name (default: "rerank-v3.5")
        base_url: Optional custom base URL for vLLM servers.
                  If None, uses Cohere cloud API.
        timeout: Request timeout in seconds (default: 30.0)

    Raises:
        ImportError: If cohere package is not installed

    Examples:
        # Cohere cloud
        reranker = CohereReranker(api_key="co-xxx", model="rerank-v3.5")

        # vLLM server
        reranker = CohereReranker(
            api_key="ignored",
            model="BAAI/bge-reranker-base",
            base_url="http://localhost:8000"
        )
    """

    def __init__(
        self,
        api_key: str,
        model: str = "rerank-v3.5",
        base_url: str | None = None,
        timeout: float = 30.0,
    ) -> None: ...

    async def rerank(
        self,
        query: str,
        documents: list[str],
        top_n: int | None = None,
    ) -> list[RerankResult]:
        """Rerank using Cohere SDK (works with both Cohere and vLLM).

        Maps Cohere API response to RerankResult format.
        Scores are already normalized to [0, 1].
        """
        ...

    async def validate(self) -> None:
        """Validate connectivity by making a minimal rerank call."""
        ...

    @property
    def provider(self) -> str:
        """Return provider name based on whether base_url is set."""
        return "vllm" if self._base_url else "cohere"

Factory Function Contract

Location: src/holodeck/lib/rerankers/__init__.py

from holodeck.models.tool import (
    CohereRerankerConfig,
    RerankerConfig,
    VLLMRerankerConfig,
)


def create_reranker(config: RerankerConfig) -> BaseReranker:
    """Create a reranker instance from configuration.

    Both Cohere and vLLM configs create a CohereReranker instance,
    since vLLM is Cohere API compatible.

    Args:
        config: RerankerConfig (CohereRerankerConfig or VLLMRerankerConfig)

    Returns:
        Configured CohereReranker instance

    Raises:
        ImportError: If cohere package is not installed

    Examples:
        # Cohere cloud config
        config = CohereRerankerConfig(
            provider="cohere",
            api_key="co-xxx",
            model="rerank-v3.5"
        )
        reranker = create_reranker(config)  # base_url=None

        # vLLM config
        config = VLLMRerankerConfig(
            provider="vllm",
            url="http://localhost:8000",
            model="BAAI/bge-reranker-base"
        )
        reranker = create_reranker(config)  # base_url=config.url
    """
    from holodeck.lib.rerankers.cohere import CohereReranker

    if isinstance(config, CohereRerankerConfig):
        return CohereReranker(
            api_key=config.api_key,
            model=config.model,
            base_url=None,  # Use Cohere cloud
        )
    elif isinstance(config, VLLMRerankerConfig):
        return CohereReranker(
            api_key="vllm-placeholder",  # Ignored by vLLM
            model=config.model,
            base_url=config.url,
            timeout=config.timeout,
        )
    else:
        raise ValueError(f"Unsupported reranker config type: {type(config)}")

Integration with VectorStoreTool

Location: src/holodeck/tools/vectorstore_tool.py

The VectorStoreTool will integrate reranking into the search flow:

class VectorStoreTool:
    async def search(self, query: str) -> str:
        """Search with optional reranking.

        Flow:
        1. Calculate effective_rerank_top_n (rerank_top_n or top_k * 3)
        2. Perform vector search with effective_rerank_top_n limit
        3. If rerank enabled:
           a. Call reranker.rerank(query, documents, top_n=top_k)
           b. On failure, log warning and use original results
        4. Apply min_similarity_score filter
        5. Return top_k results
        """
        ...

Logging Contract

All reranker implementations must log at DEBUG level:

# On successful rerank
logger.debug(
    f"Reranker completed: provider={self.provider}, "
    f"input_docs={len(documents)}, output_docs={len(results)}, "
    f"latency_ms={latency_ms:.2f}"
)

# On failure (before raising)
logger.warning(
    f"Reranker failed: provider={self.provider}, "
    f"latency_ms={latency_ms:.2f}, error={error_message}"
)