"""Registered RAG retrieval pipeline versions. Task 7 froze ``rag-naive-v1`` as the observed baseline and, from genuine SigNoz-backed diagnosis (see ``docs/observability/diagnostic-workflow.md``), picked deduplication of near-duplicate candidates -- over-fetch a larger raw pool, remove near-duplicates, then truncate back to the caller's requested count -- as the one architectural variable for ``rag-dedup-v2``. This module is the single source of truth for what a "pipeline version" means behaviourally. It does not itself touch Chroma, embeddings, or telemetry -- ``ChromaDBClient.query_observed`` (``app/rag/chromadb_client.py``) reads a :class:`PipelineVersion` from here and gates its over-fetch/dedup logic on ``dedup_enabled``/``overfetch_factor``. Design contract (do not weaken without updating Task 8's report): - ``rag-naive-v1`` is immutable: ``dedup_enabled=False``, ``overfetch_factor=1``, forever. A test in ``tests/rag/test_pipeline_version.py`` pins this exact tuple so an accidental edit here fails loudly. - The *default* (no pipeline version requested -- ``None``) must always resolve to exactly ``rag-naive-v1``'s behavior. Ordinary product traffic (Chat, LEARN_NODE, flashcards, quiz, StudyBuddy) never passes an explicit pipeline version, so it is always on this default path and is therefore provably unaffected by anything registered here. - An explicitly requested but unregistered version string is a hard error (``UnknownPipelineVersionError``), per the brief's "reject evaluation startup when the requested version is unknown" -- silently falling back to v1 behavior for a typo'd version name would make a v1/v2 comparison silently meaningless. """ from __future__ import annotations from dataclasses import dataclass # Over-fetch factor for rag-dedup-v2: request `requested_top_k * factor` raw # candidates from Chroma before dedup+truncate, so removing near-duplicates # can still backfill up to the caller's original requested count instead of # only ever shrinking the naive top-5 in place (the exact trap the plan # preamble's acceptance gate calls out: "the improvement is not caused solely # by returning fewer required candidates"). 3x is a deliberately modest # choice -- big enough that Task 7's finding of low document_diversity # (1.58/5 average) has real room to be corrected by pulling in alternate # documents' chunks, small enough that it doesn't turn `chroma.vector_search` # into a new dominant cost on top of the already-dominant `embedding.query` # stage (a 3x-larger candidate pool over a ~10k-chunk shared collection is # still a small single Chroma call, not a new order of magnitude). DEDUP_V2_OVERFETCH_FACTOR = 3 DEFAULT_PIPELINE_VERSION = "rag-naive-v1" CURRENT_PRODUCT_PIPELINE_VERSION = "rag-structured-hybrid-v3" @dataclass(frozen=True) class PipelineVersion: """One registered, behaviorally-complete retrieval pipeline configuration.""" name: str dedup_enabled: bool overfetch_factor: int class UnknownPipelineVersionError(ValueError): """Raised when an explicitly requested pipeline version is not registered.""" _REGISTRY: dict[str, PipelineVersion] = { "rag-naive-v1": PipelineVersion( name="rag-naive-v1", dedup_enabled=False, overfetch_factor=1 ), "rag-dedup-v2": PipelineVersion( name="rag-dedup-v2", dedup_enabled=True, overfetch_factor=DEDUP_V2_OVERFETCH_FACTOR, ), } def registered_versions() -> tuple[str, ...]: """All registered pipeline version names, for error messages and tests.""" return tuple(sorted(_REGISTRY)) def get_pipeline_version(name: str | None) -> PipelineVersion: """Resolve a pipeline version name to its behavior. ``name=None`` (the default for every ordinary product call site) always resolves to ``rag-naive-v1`` -- unspecified pipeline version is never ambiguous and never requires the caller to know version names exist. Raises :class:`UnknownPipelineVersionError` for any non-``None`` name that isn't registered, rather than silently falling back to v1 -- an evaluation run that mistypes a version string must fail loudly, not quietly measure the wrong pipeline. """ if name is None: return _REGISTRY[DEFAULT_PIPELINE_VERSION] try: return _REGISTRY[name] except KeyError as exc: raise UnknownPipelineVersionError( f"Unknown pipeline version {name!r}. Registered versions: " f"{', '.join(registered_versions())}" ) from exc