From 25a2787ed07fb7417783b70f1816b3483d1cf215 Mon Sep 17 00:00:00 2001 From: are-ces <195810094+are-ces@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:40:48 +0200 Subject: [PATCH 1/5] LCORE-1426: unify RAG config under single rag section Restructure RAG configuration from separate top-level sections (byok_rag, rag, okp, reranker) into a unified rag section: - Add RagStore, ByokConfiguration, RetrievalStrategyConfiguration, RetrievalConfiguration, RagConfiguration models - Move reranker from top-level to rag.retrieval.inline.reranker - Rename rag_type field to backend (drop inline::/remote:: prefix) - Replace hardcoded chunk-limit constants with configurable max_chunks fields (rag.byok.max_chunks, rag.okp.max_chunks, rag.retrieval.inline.max_chunks, rag.retrieval.tool.max_chunks) - Add duplicate rag_id validation on ByokConfiguration - Add BACKEND_TO_PROVIDER_TYPE mapping for enrichment - Update synthesize_configuration to read from rag.byok.stores - Update all unit and integration tests Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/endpoints/rags.py | 6 +- src/client.py | 8 +- src/configuration.py | 21 +- src/constants.py | 21 +- src/llama_stack_configuration.py | 56 +- .../api/responses/successful/configuration.py | 14 +- src/models/config.py | 298 ++++-- src/utils/reranker.py | 4 +- src/utils/responses.py | 28 +- src/utils/vector_search.py | 29 +- .../endpoints/test_query_byok_integration.py | 71 +- .../test_responses_byok_integration.py | 65 +- .../test_streaming_query_byok_integration.py | 67 +- tests/unit/app/endpoints/test_rags.py | 42 +- tests/unit/models/config/test_byok_rag.py | 201 ++-- .../models/config/test_dump_configuration.py | 408 +++++--- .../models/config/test_rag_configuration.py | 198 +++- tests/unit/telemetry/conftest.py | 53 +- tests/unit/test_configuration.py | 956 ++++++++++-------- tests/unit/test_llama_stack_configuration.py | 74 +- tests/unit/test_llama_stack_synthesize.py | 20 +- tests/unit/utils/test_config_dumper.py | 5 +- tests/unit/utils/test_responses.py | 90 +- tests/unit/utils/test_vector_search.py | 79 +- 24 files changed, 1736 insertions(+), 1078 deletions(-) diff --git a/src/app/endpoints/rags.py b/src/app/endpoints/rags.py index 8cf5c7679..7af061460 100644 --- a/src/app/endpoints/rags.py +++ b/src/app/endpoints/rags.py @@ -24,7 +24,7 @@ RAGInfoResponse, RAGListResponse, ) -from models.config import Action, ByokRag +from models.config import Action, RagStore from utils.endpoints import check_configuration_loaded logger = get_logger(__name__) @@ -112,7 +112,7 @@ async def rags_endpoint_handler( raise HTTPException(**response.model_dump()) from e -def _resolve_rag_id_to_vector_db_id(rag_id: str, byok_rags: list[ByokRag]) -> str: +def _resolve_rag_id_to_vector_db_id(rag_id: str, byok_rags: list[RagStore]) -> str: """Resolve a user-facing rag_id to the llama-stack vector_db_id. Checks if the given ID matches a rag_id in the BYOK config and returns @@ -178,7 +178,7 @@ async def get_rag_endpoint_handler( # Resolve user-facing rag_id to llama-stack vector_db_id vector_db_id = _resolve_rag_id_to_vector_db_id( - rag_id, configuration.configuration.byok_rag + rag_id, configuration.configuration.rag.byok.stores ) try: diff --git a/src/client.py b/src/client.py index 05e6fe250..585535198 100644 --- a/src/client.py +++ b/src/client.py @@ -142,10 +142,14 @@ def _enrich_library_config(self, input_config_path: str) -> str: config = configuration.configuration # Enrichment: BYOK RAG - enrich_byok_rag(ls_config, [b.model_dump() for b in config.byok_rag]) + enrich_byok_rag(ls_config, [s.model_dump() for s in config.rag.byok.stores]) # Enrichment: Solr - enabled when "okp" appears in either inline or tool list - enrich_solr(ls_config, config.rag.model_dump(), config.okp.model_dump()) + rag_config_for_solr = { + "inline": config.rag.retrieval.inline.sources, + "tool": config.rag.retrieval.tool.sources, + } + enrich_solr(ls_config, rag_config_for_solr, config.rag.okp.model_dump()) # Enrichment: Azure Entra ID deferred auth entra_id_config = ( diff --git a/src/configuration.py b/src/configuration.py index e95e89083..2459189cc 100644 --- a/src/configuration.py +++ b/src/configuration.py @@ -537,14 +537,14 @@ def okp(self) -> "OkpConfiguration": """Return OKP configuration.""" if self._configuration is None: raise LogicError("logic error: configuration is not loaded") - return self._configuration.okp + return self._configuration.rag.okp @property - def reranker(self) -> "RerankerConfiguration": + def reranker(self) -> Optional["RerankerConfiguration"]: """Return reranker configuration.""" if self._configuration is None: raise LogicError("logic error: configuration is not loaded") - return self._configuration.reranker + return self._configuration.rag.retrieval.inline.reranker @property def skills(self) -> Optional[SkillsConfiguration]: @@ -567,12 +567,15 @@ def rag_id_mapping(self) -> dict[str, str]: if self._configuration is None: raise LogicError("logic error: configuration is not loaded") byok_mapping = { - brag.vector_db_id: brag.rag_id for brag in self._configuration.byok_rag + store.vector_db_id: store.rag_id + for store in self._configuration.rag.byok.stores } - rag = self._configuration.rag + retrieval = self._configuration.rag.retrieval okp_id = constants.OKP_RAG_ID - okp_enabled = okp_id in (rag.inline or []) or okp_id in (rag.tool or []) + okp_enabled = okp_id in (retrieval.inline.sources or []) or okp_id in ( + retrieval.tool.sources or [] + ) okp_mapping = ( {constants.SOLR_DEFAULT_VECTOR_STORE_ID: okp_id} if okp_enabled else {} ) @@ -592,8 +595,8 @@ def score_multiplier_mapping(self) -> dict[str, float]: if self._configuration is None: raise LogicError("logic error: configuration is not loaded") return { - brag.vector_db_id: brag.score_multiplier - for brag in self._configuration.byok_rag + store.vector_db_id: store.score_multiplier + for store in self._configuration.rag.byok.stores } @property @@ -608,7 +611,7 @@ def inline_solr_enabled(self) -> bool: """ if self._configuration is None: raise LogicError("logic error: configuration is not loaded") - return constants.OKP_RAG_ID in self._configuration.rag.inline + return constants.OKP_RAG_ID in self._configuration.rag.retrieval.inline.sources def resolve_index_name( self, vector_store_id: str, rag_id_mapping: Optional[dict[str, str]] = None diff --git a/src/constants.py b/src/constants.py index a94bb350a..f2847584f 100644 --- a/src/constants.py +++ b/src/constants.py @@ -184,8 +184,13 @@ CACHE_TYPE_NOOP: Final[str] = "noop" # BYOK RAG -# Default RAG type for bring-your-own-knowledge RAG configurations, that type -# needs to be supported by Llama Stack +# Backends that have enrichment support in llama_stack_configuration.py +SUPPORTED_RAG_BACKENDS: Final[frozenset[str]] = frozenset({"faiss", "pgvector"}) + +# Default RAG backend for bring-your-own-knowledge RAG configurations +DEFAULT_RAG_BACKEND: Final[str] = "faiss" + +# Default RAG type (fully-qualified Llama Stack provider type) DEFAULT_RAG_TYPE: Final[str] = "inline::faiss" # Default sentence transformer model for embedding generation, that type needs @@ -203,16 +208,14 @@ USER_QUOTA_LIMITER: Final[str] = "user_limiter" CLUSTER_QUOTA_LIMITER: Final[str] = "cluster_limiter" -# Hard cap on total RAG chunks delivered to the LLM across all sources -INLINE_RAG_MAX_CHUNKS: Final[int] = 10 +# Default chunk limits (used as Pydantic field defaults in RagConfiguration) +DEFAULT_INLINE_RAG_MAX_CHUNKS: Final[int] = 10 +DEFAULT_TOOL_RAG_MAX_CHUNKS: Final[int] = 10 +DEFAULT_BYOK_RAG_MAX_CHUNKS: Final[int] = 10 +DEFAULT_OKP_RAG_MAX_CHUNKS: Final[int] = 5 # RAG as a tool constants DEFAULT_RAG_TOOL: Final[str] = "file_search" -TOOL_RAG_MAX_CHUNKS: Final[int] = 10 # retrieved from RAG as a tool - -# Inline RAG constants -BYOK_RAG_MAX_CHUNKS: Final[int] = 10 # retrieved from BYOK RAG -OKP_RAG_MAX_CHUNKS: Final[int] = 5 # retrieved from OKP RAG # Score multiplier applied to BYOK chunks after cross-encoder reranking (Solr chunks unchanged) BYOK_RAG_RERANK_BOOST: Final[float] = 1.2 diff --git a/src/llama_stack_configuration.py b/src/llama_stack_configuration.py index 1df975e48..14a0f6964 100644 --- a/src/llama_stack_configuration.py +++ b/src/llama_stack_configuration.py @@ -34,10 +34,6 @@ logger = get_logger(__name__) -# Maps a UnifiedInferenceProvider.type (canonical, backend-agnostic vocabulary) -# to the Llama Stack provider_type emitted by apply_high_level_inference. The -# completeness of this map against UnifiedInferenceProvider.type is asserted by -# a unit test so a new Literal value cannot be added without a mapping. PROVIDER_TYPE_MAP: dict[str, str] = { "openai": "remote::openai", "ollama": "remote::ollama", @@ -50,14 +46,10 @@ "vllm_rhel_ai": "remote::vllm", } -# Maps Llama Stack provider_type -> config field name for the auth token. -# Providers not listed default to "api_key". API_KEY_FIELD_MAP: dict[str, str] = { "remote::vllm": "api_token", } -# Package-relative path to the built-in default baseline run.yaml shipped with -# LCORE, used when unified mode selects baseline "default" without a profile. DEFAULT_BASELINE_RESOURCE: Path = Path(__file__).parent / "data" / "default_run.yaml" VECTOR_IO_TEMPLATES: dict[str, dict[str, Any]] = { @@ -81,6 +73,26 @@ }, } +BACKEND_TO_PROVIDER_TYPE: dict[str, str] = { + "faiss": "inline::faiss", + "pgvector": "remote::pgvector", +} + + +def _resolve_rag_type(brag: dict[str, Any]) -> str: + """Resolve the full Llama Stack provider type from a BYOK RAG dict. + + Parameters: + brag (dict[str, Any]): A single BYOK RAG entry dict, expected to + contain a ``backend`` key (e.g. ``"faiss"``, ``"pgvector"``). + + Returns: + str: The fully-qualified Llama Stack provider type + (e.g. ``"inline::faiss"``, ``"remote::pgvector"``). + """ + backend = brag.get("backend", constants.DEFAULT_RAG_BACKEND) + return BACKEND_TO_PROVIDER_TYPE.get(backend, f"inline::{backend}") + class YamlDumper(yaml.Dumper): # pylint: disable=too-many-ancestors """Custom YAML dumper with proper indentation levels.""" @@ -206,7 +218,7 @@ def construct_storage_backends_section( for brag in byok_rag: if not brag.get("rag_id"): raise ValueError(f"BYOK RAG entry is missing required 'rag_id': {brag}") - rag_type = brag.get("rag_type", constants.DEFAULT_RAG_TYPE) + rag_type = _resolve_rag_type(brag) template = VECTOR_IO_TEMPLATES.get(rag_type, {}) if not template.get("needs_storage_backend", True): continue @@ -443,7 +455,7 @@ def construct_vector_io_providers_section( continue existing_ids.add(provider_id) added += 1 - rag_type = brag.get("rag_type", constants.DEFAULT_RAG_TYPE) + rag_type = _resolve_rag_type(brag) config = _build_vector_io_config(rag_type, backend_name, brag) output.append( { @@ -852,8 +864,16 @@ def synthesize_configuration( # 4. Existing enrichment — same calls as legacy generate_configuration so # unified output matches legacy output for equivalent inputs (R7). enrich_azure_entra_id_inference(ls_config, lcs_config.get("azure_entra_id")) - enrich_byok_rag(ls_config, lcs_config.get("byok_rag", [])) - enrich_solr(ls_config, lcs_config.get("rag", {}), lcs_config.get("okp", {})) + rag_section = lcs_config.get("rag", {}) + byok_stores = rag_section.get("byok", {}).get("stores", []) + enrich_byok_rag(ls_config, byok_stores) + retrieval = rag_section.get("retrieval", {}) + rag_config_for_solr = { + "inline": retrieval.get("inline", {}).get("sources", []), + "tool": retrieval.get("tool", {}).get("sources", []), + } + okp_config = rag_section.get("okp", {}) + enrich_solr(ls_config, rag_config_for_solr, okp_config) # 5. High-level inference providers (Decision S5 — a root-level section). inference = lcs_config.get("inference") or {} @@ -1017,10 +1037,18 @@ def generate_configuration( enrich_azure_entra_id_inference(ls_config, config.get("azure_entra_id")) # Enrichment: BYOK RAG - enrich_byok_rag(ls_config, config.get("byok_rag", [])) + rag_section = config.get("rag", {}) + byok_stores = rag_section.get("byok", {}).get("stores", []) + enrich_byok_rag(ls_config, byok_stores) # Enrichment: Solr - enabled when "okp" appears in either inline or tool list - enrich_solr(ls_config, config.get("rag", {}), config.get("okp", {})) + retrieval = rag_section.get("retrieval", {}) + rag_config_for_solr = { + "inline": retrieval.get("inline", {}).get("sources", []), + "tool": retrieval.get("tool", {}).get("sources", []), + } + okp_config = rag_section.get("okp", {}) + enrich_solr(ls_config, rag_config_for_solr, okp_config) dedupe_providers_vector_io(ls_config) diff --git a/src/models/api/responses/successful/configuration.py b/src/models/api/responses/successful/configuration.py index d41e8ff20..f62795bce 100644 --- a/src/models/api/responses/successful/configuration.py +++ b/src/models/api/responses/successful/configuration.py @@ -79,7 +79,19 @@ class ConfigurationResponse(AbstractSuccessfulResponse): "sqlite": None, "postgres": None, }, - "byok_rag": [], + "rag": { + "byok": {"max_chunks": 10, "stores": []}, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": {"sources": [], "max_chunks": 10}, + "tool": {"sources": [], "max_chunks": 10}, + }, + }, "quota_handlers": { "sqlite": None, "postgres": None, diff --git a/src/models/config.py b/src/models/config.py index 28ff23264..f93e1a9b2 100644 --- a/src/models/config.py +++ b/src/models/config.py @@ -2022,8 +2022,8 @@ def config( return None -class ByokRag(ConfigurationBase): - """BYOK (Bring Your Own Knowledge) RAG configuration.""" +class RagStore(ConfigurationBase): + """BYOK (Bring Your Own Knowledge) RAG store configuration.""" rag_id: str = Field( ..., @@ -2032,13 +2032,24 @@ class ByokRag(ConfigurationBase): description="Unique RAG ID", ) - rag_type: str = Field( - constants.DEFAULT_RAG_TYPE, + backend: str = Field( + constants.DEFAULT_RAG_BACKEND, min_length=1, - title="RAG type", + title="RAG backend", description="Type of RAG database (e.g. 'inline::faiss', 'remote::pgvector').", ) + @field_validator("backend") + @classmethod + def validate_backend(cls, value: str) -> str: + """Reject unsupported backend values at config load time.""" + if value not in constants.SUPPORTED_RAG_BACKENDS: + raise ValueError( + f"Unsupported RAG backend '{value}'. " + f"Supported backends: {sorted(constants.SUPPORTED_RAG_BACKENDS)}" + ) + return value + embedding_model: str = Field( constants.DEFAULT_EMBEDDING_MODEL, min_length=1, @@ -2078,44 +2089,44 @@ class ByokRag(ConfigurationBase): default=None, title="PostgreSQL host", description="PostgreSQL host for remote::pgvector. " - "Defaults to ${env.POSTGRES_HOST} when rag_type is remote::pgvector.", + "Defaults to ${env.POSTGRES_HOST} when backend is pgvector.", ) port: Optional[str] = Field( default=None, title="PostgreSQL port", description="PostgreSQL port for remote::pgvector. " - "Defaults to ${env.POSTGRES_PORT} when rag_type is remote::pgvector.", + "Defaults to ${env.POSTGRES_PORT} when backend is pgvector.", ) db: Optional[str] = Field( default=None, title="PostgreSQL database", description="PostgreSQL database name for remote::pgvector. " - "Defaults to ${env.POSTGRES_DATABASE} when rag_type is remote::pgvector.", + "Defaults to ${env.POSTGRES_DATABASE} when backend is pgvector.", ) user: Optional[str] = Field( default=None, title="PostgreSQL user", description="PostgreSQL user for remote::pgvector. " - "Defaults to ${env.POSTGRES_USER} when rag_type is remote::pgvector.", + "Defaults to ${env.POSTGRES_USER} when backend is pgvector.", ) password: Optional[SecretStr] = Field( default=None, title="PostgreSQL password", description="PostgreSQL password for remote::pgvector. " - "Defaults to ${env.POSTGRES_PASSWORD} when rag_type is remote::pgvector.", + "Defaults to ${env.POSTGRES_PASSWORD} when backend is pgvector.", ) @model_validator(mode="after") - def validate_rag_type_fields(self) -> Self: - """Validate and populate fields based on rag_type.""" - if self.rag_type == "inline::faiss": + def validate_backend_fields(self) -> Self: + """Validate and populate fields based on backend.""" + if self.backend == "faiss": if not self.db_path: - raise ValueError("db_path is required when rag_type is 'inline::faiss'") - elif self.rag_type == "remote::pgvector": + raise ValueError("db_path is required when backend is 'faiss'") + elif self.backend == "pgvector": pgvector_defaults: dict[str, str | SecretStr] = { "host": "${env.POSTGRES_HOST}", "port": "${env.POSTGRES_PORT}", @@ -2250,43 +2261,113 @@ class QuotaHandlersConfiguration(ConfigurationBase): ) -class RagConfiguration(ConfigurationBase): - """RAG strategy configuration. +class RerankerConfiguration(ConfigurationBase): + """Reranker configuration for RAG chunk reranking.""" + + enabled: bool = Field( + default=False, + title="Reranker enabled", + description="When True, reranking applied to RAG chunks. " + "When False, reranking is disabled and original scoring used.", + ) + model: str = Field( + default="cross-encoder/ms-marco-MiniLM-L6-v2", + title="Reranker model", + description="Cross-encoder model name for reranking RAG chunks. " + "Defaults to 'cross-encoder/ms-marco-MiniLM-L6-v2' from sentence-transformers.", + ) + + _explicitly_configured: bool = PrivateAttr(default=False) - Controls which RAG sources are used for inline and tool-based retrieval. + @model_validator(mode="after") + def mark_as_explicitly_configured(self) -> Self: + """Mark this configuration as explicitly set when instantiated from user input.""" + if self.model_fields_set: + self._explicitly_configured = True - Each strategy lists RAG IDs to include. The special ID ``"okp"`` defined in constants, - activates the OKP provider; all other IDs refer to entries in ``byok_rag``. + return self - Backward compatibility: - - ``inline`` defaults to ``[]`` (no inline RAG). - - ``tool`` defaults to ``[]`` (no tool RAG). - If no RAG strategy is defined (inline and tool are empty), - the RAG tool will register all stores available to llama-stack. - """ +class RetrievalStrategyConfiguration(ConfigurationBase): + """Configuration for a single retrieval strategy (inline or tool).""" - inline: list[str] = Field( + sources: list[str] = Field( default_factory=list, - title="Inline RAG IDs", - description="RAG IDs whose sources are injected as context before the LLM call. " - f"Use '{constants.OKP_RAG_ID}' to enable OKP inline RAG. Empty by default (no inline RAG).", + title="RAG source IDs", + description="RAG IDs to use for this retrieval strategy. " + f"Use '{constants.OKP_RAG_ID}' to include the OKP vector store.", + ) + + max_chunks: PositiveInt = Field( + default=constants.DEFAULT_INLINE_RAG_MAX_CHUNKS, + title="Max chunks", + description="Maximum number of chunks returned by this retrieval strategy.", ) - tool: list[str] = Field( + reranker: Optional[RerankerConfiguration] = Field( + default=None, + title="Reranker configuration", + description="Neural reranking of RAG chunks using cross-encoder. " + "Only applicable to inline retrieval.", + ) + + +class RetrievalConfiguration(ConfigurationBase): + """Configuration for inline and tool retrieval strategies.""" + + inline: RetrievalStrategyConfiguration = Field( + default_factory=lambda: RetrievalStrategyConfiguration( + max_chunks=constants.DEFAULT_INLINE_RAG_MAX_CHUNKS, + reranker=RerankerConfiguration(), + ), + title="Inline retrieval", + description="Inline RAG: context injected before the LLM request.", + ) + + tool: RetrievalStrategyConfiguration = Field( + default_factory=lambda: RetrievalStrategyConfiguration( + max_chunks=constants.DEFAULT_TOOL_RAG_MAX_CHUNKS, + ), + title="Tool retrieval", + description="Tool RAG: LLM can call file_search on demand.", + ) + + +class ByokConfiguration(ConfigurationBase): + """BYOK (Bring Your Own Knowledge) configuration.""" + + max_chunks: PositiveInt = Field( + default=constants.DEFAULT_BYOK_RAG_MAX_CHUNKS, + title="Max BYOK chunks", + description="Maximum total number of chunks returned across all BYOK stores.", + ) + + stores: list[RagStore] = Field( default_factory=list, - title="Tool RAG IDs", - description="RAG IDs made available to the LLM as a file_search tool. " - f"Use '{constants.OKP_RAG_ID}' to include the OKP vector store. " - "When omitted, all registered BYOK vector stores are used (backward compatibility).", + title="BYOK RAG stores", + description="List of BYOK RAG store configurations.", ) + @model_validator(mode="after") + def validate_unique_rag_ids(self) -> Self: + """Reject duplicate rag_id values across stores.""" + seen: set[str] = set() + for store in self.stores: + if store.rag_id in seen: + raise ValueError( + f"Duplicate rag_id '{store.rag_id}' in rag.byok.stores. " + "Each store must have a unique rag_id." + ) + seen.add(store.rag_id) + return self + class OkpConfiguration(ConfigurationBase): """OKP (Offline Knowledge Portal) provider configuration. Controls provider-specific behaviour for the OKP vector store. - Only relevant when ``"okp"`` is listed in ``rag.inline`` or ``rag.tool``. + Only relevant when ``"okp"`` is listed in ``rag.retrieval.inline.sources`` + or ``rag.retrieval.tool.sources``. """ rhokp_url: Optional[AnyHttpUrl] = Field( @@ -2311,31 +2392,80 @@ class OkpConfiguration(ConfigurationBase): "Use Solr boolean syntax, e.g. 'product:ansible AND product:*openshift*'.", ) + max_chunks: PositiveInt = Field( + default=constants.DEFAULT_OKP_RAG_MAX_CHUNKS, + title="Max OKP chunks", + description="Maximum number of chunks fetched from OKP.", + ) -class RerankerConfiguration(ConfigurationBase): - """Reranker configuration for RAG chunk reranking.""" - enabled: bool = Field( - default=False, - title="Reranker enabled", - description="When True, reranking applied to RAG chunks. " - "When False, reranking is disabled and original scoring used.", +class RagConfiguration(ConfigurationBase): + """Unified RAG configuration. + + Groups all RAG-related settings: BYOK stores, OKP provider, and + retrieval strategies (inline and tool). + """ + + byok: ByokConfiguration = Field( + default_factory=ByokConfiguration, + title="BYOK configuration", + description="Bring Your Own Knowledge store configurations and settings.", ) - model: str = Field( - default="cross-encoder/ms-marco-MiniLM-L6-v2", - title="Reranker model", - description="Cross-encoder model name for reranking RAG chunks. " - "Defaults to 'cross-encoder/ms-marco-MiniLM-L6-v2' from sentence-transformers.", + + okp: OkpConfiguration = Field( + default_factory=OkpConfiguration, + title="OKP configuration", + description=f"OKP provider settings. Only used when '{constants.OKP_RAG_ID}' " + "is listed in retrieval.inline.sources or retrieval.tool.sources.", ) - # Private attribute to track if this was explicitly configured - _explicitly_configured: bool = PrivateAttr(default=False) + retrieval: RetrievalConfiguration = Field( + default_factory=RetrievalConfiguration, + title="Retrieval configuration", + description="Inline and tool retrieval strategy settings.", + ) @model_validator(mode="after") - def mark_as_explicitly_configured(self) -> Self: - """Mark this configuration as explicitly set when instantiated from user input.""" - if self.model_fields_set: - self._explicitly_configured = True + def validate_retrieval_sources(self) -> Self: + """Reject retrieval source IDs not declared in byok.stores or OKP.""" + # pylint: disable=no-member + known_ids = {store.rag_id for store in self.byok.stores} + known_ids.add(constants.OKP_RAG_ID) + + for strategy_name in ("inline", "tool"): + strategy = getattr(self.retrieval, strategy_name) + unknown = set(strategy.sources) - known_ids + if unknown: + raise ValueError( + f"retrieval.{strategy_name}.sources contains unknown RAG IDs: " + f"{sorted(unknown)}. " + f"Declared IDs: {sorted(known_ids)}" + ) + + return self + + @model_validator(mode="after") + def validate_reranker_auto_enable(self) -> Self: + """Automatically enable reranker when both BYOK and OKP RAG are configured.""" + # pylint: disable=no-member + has_byok = len(self.byok.stores) > 0 + has_okp = constants.OKP_RAG_ID in self.retrieval.inline.sources + reranker = self.retrieval.inline.reranker + + if ( + has_byok + and has_okp + and reranker is not None + and not reranker._explicitly_configured # pylint: disable=protected-access + and not reranker.enabled + ): + logger.info( + "Automatically enabling reranker: Both BYOK RAG (%d stores) and " + "OKP are configured. Reranking improves result quality when " + "multiple knowledge sources are available.", + len(self.byok.stores), + ) + reranker.enabled = True return self @@ -2701,13 +2831,6 @@ class Configuration(ConfigurationBase): description="Settings for human-in-the-loop approval of MCP tool invocations", ) - byok_rag: list[ByokRag] = Field( - default_factory=list, - title="BYOK RAG configuration", - description="BYOK RAG configuration. This configuration can be used to " - "reconfigure Llama Stack through its run.yaml configuration file", - ) - a2a_state: A2AStateConfiguration = Field( default_factory=A2AStateConfiguration, title="A2A state configuration", @@ -2746,20 +2869,8 @@ class Configuration(ConfigurationBase): rag: RagConfiguration = Field( default_factory=RagConfiguration, title="RAG configuration", - description="Configuration for all RAG strategies (inline and tool-based).", - ) - - okp: OkpConfiguration = Field( - default_factory=OkpConfiguration, - title="OKP configuration", - description=f"OKP provider settings. Only used when '{constants.OKP_RAG_ID}' is listed " - "in rag.inline or rag.tool.", - ) - - reranker: RerankerConfiguration = Field( - default_factory=RerankerConfiguration, - title="Reranker configuration", - description="Configuration for neural reranking of RAG chunks using cross-encoder.", + description="Unified RAG configuration: BYOK stores, OKP provider, " + "and retrieval strategies (inline and tool-based).", ) skills: Optional[SkillsConfiguration] = Field( @@ -2877,43 +2988,6 @@ def validate_rlsapi_v1_quota_configuration(self) -> Self: return self - @model_validator(mode="after") - def validate_reranker_auto_enable(self) -> Self: - """Automatically enable reranker when both BYOK and OKP RAG are configured. - - When users have both BYOK entries in byok_rag and OKP - configured in the RAG strategies, automatically - enable the reranker if it's not explicitly disabled. This improves result - quality when multiple knowledge sources are available. - - Returns: - Self: The validated configuration instance with reranker potentially enabled. - """ - # Check if BYOK RAG entries are configured - has_byok = len(self.byok_rag) > 0 - - # Check if OKP is configured in either inline or tool RAG strategies - # pylint: disable=no-member - has_okp = constants.OKP_RAG_ID in self.rag.inline - - # If both BYOK and OKP are present and reranker is using default settings, - # ensure it's enabled for optimal results - if ( - has_byok - and has_okp - and not self.reranker._explicitly_configured # pylint: disable=protected-access - and not self.reranker.enabled - ): - logger.info( - "Automatically enabling reranker: Both BYOK RAG (%d entries) or " - "other inline RAG and OKP are configured. Reranking improves result " - "quality when multiple knowledge sources are available.", - len(self.byok_rag), - ) - self.reranker.enabled = True - - return self - @model_validator(mode="after") def check_unified_vs_legacy(self) -> Self: """Reconcile unified synthesis inputs, legacy mode, and library-mode needs. diff --git a/src/utils/reranker.py b/src/utils/reranker.py index 0d8608b14..d72ad0ec6 100644 --- a/src/utils/reranker.py +++ b/src/utils/reranker.py @@ -31,7 +31,9 @@ async def _get_cross_encoder(model_name: str) -> Any: Loaded CrossEncoder model instance, or None if loading fails. """ # Check if reranking is enabled before attempting to load the model - if not configuration.reranker.enabled: # pylint: disable=no-member + if ( + not configuration.reranker or not configuration.reranker.enabled + ): # pylint: disable=no-member logger.debug("Reranker is disabled, not loading cross-encoder model") return None diff --git a/src/utils/responses.py b/src/utils/responses.py index 5e5916e0b..8ce084d90 100644 --- a/src/utils/responses.py +++ b/src/utils/responses.py @@ -107,7 +107,7 @@ ToolResultSummary, TurnSummary, ) -from models.config import ByokRag +from models.config import RagStore from models.database.conversations import UserConversation from utils.mcp_headers import ( McpHeaders, @@ -262,16 +262,20 @@ async def prepare_tools( # pylint: disable=too-many-arguments,too-many-position # If rag.inline is configured, but not rag.tool, tool RAG is disabled. # 3. All registered vector DBs: fallback when neither rag.tool nor rag.inline are configured. # IDs fetched from llama-stack are already internal and need no translation. - byok_rags = configuration.configuration.byok_rag + byok_stores = configuration.configuration.rag.byok.stores - is_tool_rag_enabled = len(configuration.configuration.rag.tool) > 0 - is_inline_rag_enabled = len(configuration.configuration.rag.inline) > 0 + is_tool_rag_enabled = ( + len(configuration.configuration.rag.retrieval.tool.sources) > 0 + ) + is_inline_rag_enabled = ( + len(configuration.configuration.rag.retrieval.inline.sources) > 0 + ) if vector_store_ids is not None: - effective_ids = resolve_vector_store_ids(vector_store_ids, byok_rags) + effective_ids = resolve_vector_store_ids(vector_store_ids, byok_stores) elif is_tool_rag_enabled: effective_ids = resolve_vector_store_ids( - configuration.configuration.rag.tool, byok_rags + configuration.configuration.rag.retrieval.tool.sources, byok_stores ) elif not is_inline_rag_enabled: effective_ids = await get_vector_store_ids(client, None) @@ -645,7 +649,7 @@ def filter_tools_by_allowed_entries( def resolve_vector_store_ids( - vector_store_ids: list[str], byok_rags: list[ByokRag] + vector_store_ids: list[str], byok_rags: list[RagStore] ) -> list[str]: """Translate customer-facing rag_ids to llama-stack vector_db_ids. @@ -673,7 +677,7 @@ def resolve_vector_store_ids( def translate_tools_vector_store_ids( - tools: list[InputTool], byok_rags: list[ByokRag] + tools: list[InputTool], byok_rags: list[RagStore] ) -> list[InputTool]: """Translate user-facing vector_store_ids to llama-stack IDs in each file_search tool. @@ -713,7 +717,7 @@ def get_rag_tools(vector_store_ids: list[str]) -> Optional[list[InputToolFileSea InputToolFileSearch( type="file_search", vector_store_ids=vector_store_ids, - max_num_results=constants.TOOL_RAG_MAX_CHUNKS, + max_num_results=configuration.rag.retrieval.tool.max_chunks, ) ] @@ -1746,8 +1750,8 @@ async def _resolve_client_tools( # Per-request override of vector stores (user-facing rag_ids) vector_store_ids = extract_vector_store_ids_from_tools(tools) or None # Translate user-facing rag_ids to llama-stack vector_store_ids in each file_search tool - byok_rags = configuration.configuration.byok_rag - prepared_tools = translate_tools_vector_store_ids(tools, byok_rags) + byok_stores = configuration.configuration.rag.byok.stores + prepared_tools = translate_tools_vector_store_ids(tools, byok_stores) prepared_tools = apply_mcp_headers_to_explicit_tools( prepared_tools, token, mcp_headers, request_headers ) @@ -1841,7 +1845,7 @@ async def resolve_tool_choice( ) else: # Pass tools explicitly configured for this request - byok_rags = configuration.configuration.byok_rag + byok_rags = configuration.configuration.rag.byok.stores prepared_tools = translate_tools_vector_store_ids(tools, byok_rags) prepared_tools = apply_mcp_headers_to_explicit_tools( prepared_tools, token, mcp_headers, request_headers diff --git a/src/utils/vector_search.py b/src/utils/vector_search.py index 2e0fd3dea..d30fd48c5 100644 --- a/src/utils/vector_search.py +++ b/src/utils/vector_search.py @@ -250,7 +250,7 @@ async def _query_store_for_byok_rag( vector_store_id: str, query: str, weight: float, - max_chunks: int = constants.BYOK_RAG_MAX_CHUNKS, + max_chunks: int = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS, ) -> list[dict[str, Any]]: """Query a single vector store for BYOK RAG. @@ -443,7 +443,6 @@ async def _fetch_byok_rag( # pylint: disable=too-many-locals client: AsyncLlamaStackClient, query: str, vector_store_ids: Optional[list[str]] = None, - max_chunks: Optional[int] = None, ) -> tuple[list[RAGChunk], list[ReferencedDocument]]: """Fetch chunks and documents from BYOK RAG sources. @@ -453,15 +452,13 @@ async def _fetch_byok_rag( # pylint: disable=too-many-locals vector_store_ids: Optional list of vector store IDs to query. If provided, only these stores will be queried. If None, all stores (excluding Solr) will be queried. - max_chunks: Maximum number of chunks to return. If None, uses - constants.BYOK_RAG_MAX_CHUNKS. Returns: Tuple containing: - rag_chunks: RAG chunks from BYOK RAG - referenced_documents: Documents referenced in BYOK RAG results """ - limit = max_chunks if max_chunks is not None else constants.BYOK_RAG_MAX_CHUNKS + limit = configuration.rag.byok.max_chunks rag_chunks: list[RAGChunk] = [] referenced_documents: list[ReferencedDocument] = [] @@ -470,17 +467,17 @@ async def _fetch_byok_rag( # pylint: disable=too-many-locals # Per-request IDs are intersected with the config to prevent triggering inline RAG # for stores not explicitly configured for inline use. if vector_store_ids is None: - rag_ids_to_query = configuration.configuration.rag.inline + rag_ids_to_query = configuration.configuration.rag.retrieval.inline.sources else: rag_ids_to_query = [ v for v in vector_store_ids - if v in set(configuration.configuration.rag.inline) + if v in set(configuration.configuration.rag.retrieval.inline.sources) ] # Translate user-facing rag_ids to llama-stack ids vector_store_ids_to_query: list[str] = resolve_vector_store_ids( - rag_ids_to_query, configuration.configuration.byok_rag + rag_ids_to_query, configuration.configuration.rag.byok.stores ) # Request-level override: filter out Solr store, use the rest @@ -550,7 +547,7 @@ async def _fetch_byok_rag( # pylint: disable=too-many-locals return rag_chunks, referenced_documents -async def _fetch_solr_rag( # pylint: disable=too-many-locals +async def _fetch_okp_rag( # pylint: disable=too-many-locals client: AsyncLlamaStackClient, query: str, solr: Optional[SolrVectorSearchRequest] = None, @@ -561,8 +558,6 @@ async def _fetch_solr_rag( # pylint: disable=too-many-locals client: The AsyncLlamaStackClient to use for the request query: The user's query solr: Structured Solr inline RAG request from the API (optional). - max_chunks: Maximum number of chunks to return. If None, uses - constants.OKP_RAG_MAX_CHUNKS. Returns: Tuple containing: @@ -571,7 +566,7 @@ async def _fetch_solr_rag( # pylint: disable=too-many-locals """ rag_chunks: list[RAGChunk] = [] referenced_documents: list[ReferencedDocument] = [] - limit = constants.OKP_RAG_MAX_CHUNKS + limit = configuration.rag.okp.max_chunks if not _is_solr_enabled(): logger.info("OKP vector IO is disabled, skipping OKP search") @@ -655,13 +650,11 @@ async def build_rag_context( # pylint: disable=too-many-locals,too-many-branche if moderation_decision == "blocked": return RAGContext() - top_k = constants.INLINE_RAG_MAX_CHUNKS + top_k = configuration.rag.retrieval.inline.max_chunks # Fetch from each source using per-source limits for the reranking pool - byok_chunks_task = _fetch_byok_rag( - client, query, vector_store_ids, max_chunks=constants.BYOK_RAG_MAX_CHUNKS - ) - solr_chunks_task = _fetch_solr_rag(client, query, solr) + byok_chunks_task = _fetch_byok_rag(client, query, vector_store_ids) + solr_chunks_task = _fetch_okp_rag(client, query, solr) (byok_chunks, byok_documents), (solr_chunks, solr_documents) = await asyncio.gather( byok_chunks_task, solr_chunks_task @@ -671,7 +664,7 @@ async def build_rag_context( # pylint: disable=too-many-locals,too-many-branche merged = byok_chunks + solr_chunks # Rerank full pool with cross-encoder if enabled; then take top_k - if configuration.reranker.enabled: + if configuration.reranker and configuration.reranker.enabled: logger.info( "Reranker enabled: processing %d chunks with model '%s'", len(merged), diff --git a/tests/integration/endpoints/test_query_byok_integration.py b/tests/integration/endpoints/test_query_byok_integration.py index a4e0205aa..c7ebed535 100644 --- a/tests/integration/endpoints/test_query_byok_integration.py +++ b/tests/integration/endpoints/test_query_byok_integration.py @@ -231,7 +231,7 @@ def byok_config_fixture(test_config: AppConfig, mocker: MockerFixture) -> AppCon byok_entry.score_multiplier = 1.0 byok_entry.model_dump.return_value = { "rag_id": "test-knowledge", - "rag_type": "inline::faiss", + "backend": "faiss", "embedding_model": "sentence-transformers/all-mpnet-base-v2", "embedding_dimension": 768, "vector_db_id": "vs-byok-knowledge", @@ -239,9 +239,9 @@ def byok_config_fixture(test_config: AppConfig, mocker: MockerFixture) -> AppCon "score_multiplier": 1.0, } - # Patch the loaded configuration's byok_rag and rag.inline - test_config.configuration.byok_rag = [byok_entry] - test_config.configuration.rag.inline = ["test-knowledge"] + # Patch the loaded configuration's rag.byok.stores and rag.retrieval.inline.sources + test_config.configuration.rag.byok.stores = [byok_entry] + test_config.configuration.rag.retrieval.inline.sources = ["test-knowledge"] return test_config @@ -252,8 +252,8 @@ def byok_tool_config_fixture( ) -> AppConfig: """Load test config with BYOK RAG configured for tool-based (file_search) usage. - Sets rag.inline to empty and rag.tool to include the BYOK store, - so only tool-based RAG is active. + Sets rag.retrieval.inline.sources to empty and rag.retrieval.tool.sources + to include the BYOK store, so only tool-based RAG is active. """ byok_entry = mocker.MagicMock() byok_entry.rag_id = "test-knowledge" @@ -261,7 +261,7 @@ def byok_tool_config_fixture( byok_entry.score_multiplier = 1.0 byok_entry.model_dump.return_value = { "rag_id": "test-knowledge", - "rag_type": "inline::faiss", + "backend": "faiss", "embedding_model": "sentence-transformers/all-mpnet-base-v2", "embedding_dimension": 768, "vector_db_id": "vs-byok-knowledge", @@ -269,9 +269,9 @@ def byok_tool_config_fixture( "score_multiplier": 1.0, } - test_config.configuration.byok_rag = [byok_entry] - test_config.configuration.rag.inline = [] - test_config.configuration.rag.tool = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [byok_entry] + test_config.configuration.rag.retrieval.inline.sources = [] + test_config.configuration.rag.retrieval.tool.sources = ["test-knowledge"] return test_config @@ -426,8 +426,8 @@ async def test_query_byok_inline_rag_with_request_vector_store_ids( entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a"] mock_holder_class = mocker.patch("app.endpoints.query.AsyncLlamaStackClientHolder") mock_client = _build_base_mock_client(mocker) @@ -499,8 +499,8 @@ async def test_query_byok_request_vector_store_ids_filters_configured_stores( entry_b.score_multiplier = 1.0 # Both sources are in config - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a", "source-b"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a", "source-b"] mock_holder_class = mocker.patch("app.endpoints.query.AsyncLlamaStackClientHolder") mock_client = _build_base_mock_client(mocker) @@ -760,9 +760,9 @@ async def test_query_byok_combined_inline_and_tool_rag( # pylint: disable=too-m byok_entry.rag_id = "test-knowledge" byok_entry.vector_db_id = "vs-byok-knowledge" byok_entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [byok_entry] - test_config.configuration.rag.inline = ["test-knowledge"] - test_config.configuration.rag.tool = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [byok_entry] + test_config.configuration.rag.retrieval.inline.sources = ["test-knowledge"] + test_config.configuration.rag.retrieval.tool.sources = ["test-knowledge"] # Mock Llama Stack client mock_holder_class = mocker.patch("app.endpoints.query.AsyncLlamaStackClientHolder") @@ -871,8 +871,8 @@ async def test_query_byok_inline_rag_only_configured_rag_id_is_queried( entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a"] mock_holder_class = mocker.patch("app.endpoints.query.AsyncLlamaStackClientHolder") mock_client = _build_base_mock_client(mocker) @@ -957,8 +957,8 @@ async def test_query_byok_score_multiplier_shifts_chunk_priority( # pylint: dis entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 5.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a", "source-b"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a", "source-b"] mock_holder_class = mocker.patch("app.endpoints.query.AsyncLlamaStackClientHolder") mock_client = _build_base_mock_client(mocker) @@ -1056,17 +1056,17 @@ async def test_query_rag_content_limit_caps_retrieved_results( # pylint: disabl entry.vector_db_id = "vs-big-source" entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry] - test_config.configuration.rag.inline = ["big-source"] + test_config.configuration.rag.byok.stores = [entry] + test_config.configuration.rag.retrieval.inline.sources = ["big-source"] # Disable reranker for this test since it's testing chunk capping, not reranking - test_config.configuration.reranker.enabled = False + test_config.configuration.rag.retrieval.inline.reranker.enabled = False mock_holder_class = mocker.patch("app.endpoints.query.AsyncLlamaStackClientHolder") mock_client = _build_base_mock_client(mocker) # Generate more chunks than INLINE_RAG_MAX_CHUNKS - num_chunks = constants.INLINE_RAG_MAX_CHUNKS + 1 + num_chunks = constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + 1 chunks_data = [ (f"Chunk content {i}", f"chunk-{i}", round(0.50 + i * 0.03, 2)) for i in range(num_chunks) @@ -1105,7 +1105,7 @@ async def test_query_rag_content_limit_caps_retrieved_results( # pylint: disabl ) assert response.rag_chunks is not None - assert len(response.rag_chunks) == constants.INLINE_RAG_MAX_CHUNKS + assert len(response.rag_chunks) == constants.DEFAULT_INLINE_RAG_MAX_CHUNKS # Check that the score is computed properly for chunk in response.rag_chunks: @@ -1153,14 +1153,14 @@ async def test_query_rag_content_limit_caps_across_multiple_sources( # pylint: entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a", "source-b"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a", "source-b"] mock_holder_class = mocker.patch("app.endpoints.query.AsyncLlamaStackClientHolder") mock_client = _build_base_mock_client(mocker) # Overlapping score bands so top-k must pick from both sources - n = constants.INLINE_RAG_MAX_CHUNKS + n = constants.DEFAULT_INLINE_RAG_MAX_CHUNKS resp_a = _make_vector_io_response( mocker, [ @@ -1212,7 +1212,7 @@ async def _side_effect(**kwargs: Any) -> Any: ) assert response.rag_chunks is not None - assert len(response.rag_chunks) == constants.INLINE_RAG_MAX_CHUNKS + assert len(response.rag_chunks) == constants.DEFAULT_INLINE_RAG_MAX_CHUNKS # Check that the score is computed properly for chunk in response.rag_chunks: @@ -1252,21 +1252,22 @@ async def test_query_rag_content_limit_caps_inline_rag( # pylint: disable=too-m - Returned chunks are the top-scoring ones """ _ = mock_query_agent - mocker.patch("utils.vector_search.constants.INLINE_RAG_MAX_CHUNKS", 3) + mocker.patch("utils.vector_search.constants.DEFAULT_INLINE_RAG_MAX_CHUNKS", 3) entry = mocker.MagicMock() entry.rag_id = "big-source" entry.vector_db_id = "vs-big-source" entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry] - test_config.configuration.rag.inline = ["big-source"] - test_config.configuration.reranker.enabled = False + test_config.configuration.rag.byok.stores = [entry] + test_config.configuration.rag.retrieval.inline.sources = ["big-source"] + test_config.configuration.rag.retrieval.inline.max_chunks = 3 + test_config.configuration.rag.retrieval.inline.reranker.enabled = False mock_holder_class = mocker.patch("app.endpoints.query.AsyncLlamaStackClientHolder") mock_client = _build_base_mock_client(mocker) - num_chunks = constants.BYOK_RAG_MAX_CHUNKS + num_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS chunks_data = [ (f"Chunk content {i}", f"chunk-{i}", round(0.50 + i * 0.03, 2)) for i in range(num_chunks) diff --git a/tests/integration/endpoints/test_responses_byok_integration.py b/tests/integration/endpoints/test_responses_byok_integration.py index d316af6a6..b84840f4e 100644 --- a/tests/integration/endpoints/test_responses_byok_integration.py +++ b/tests/integration/endpoints/test_responses_byok_integration.py @@ -116,8 +116,8 @@ async def test_responses_byok_inline_rag_injects_context( # pylint: disable=too entry.rag_id = "test-knowledge" entry.vector_db_id = "vs-byok-knowledge" entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry] - test_config.configuration.rag.inline = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [entry] + test_config.configuration.rag.retrieval.inline.sources = ["test-knowledge"] mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) @@ -169,8 +169,8 @@ async def test_responses_byok_inline_rag_error_is_handled_gracefully( # pylint: entry.rag_id = "test-knowledge" entry.vector_db_id = "vs-byok-knowledge" entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry] - test_config.configuration.rag.inline = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [entry] + test_config.configuration.rag.retrieval.inline.sources = ["test-knowledge"] mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) @@ -219,7 +219,7 @@ async def test_responses_byok_tool_rag_returns_tool_calls( # pylint: disable=to byok_entry.score_multiplier = 1.0 byok_entry.model_dump.return_value = { "rag_id": "test-knowledge", - "rag_type": "inline::faiss", + "backend": "faiss", "embedding_model": "sentence-transformers/all-mpnet-base-v2", "embedding_dimension": 768, "vector_db_id": "vs-byok-knowledge", @@ -227,9 +227,9 @@ async def test_responses_byok_tool_rag_returns_tool_calls( # pylint: disable=to "score_multiplier": 1.0, } - test_config.configuration.byok_rag = [byok_entry] - test_config.configuration.rag.inline = [] - test_config.configuration.rag.tool = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [byok_entry] + test_config.configuration.rag.retrieval.inline.sources = [] + test_config.configuration.rag.retrieval.tool.sources = ["test-knowledge"] mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) @@ -291,16 +291,16 @@ async def test_responses_byok_combined_inline_and_tool_rag( # pylint: disable=t byok_entry.score_multiplier = 1.0 byok_entry.model_dump.return_value = { "rag_id": "test-knowledge", - "rag_type": "inline::faiss", + "backend": "faiss", "embedding_model": "sentence-transformers/all-mpnet-base-v2", "embedding_dimension": 768, "vector_db_id": "vs-byok-knowledge", "db_path": "/tmp/test-db", "score_multiplier": 1.0, } - test_config.configuration.byok_rag = [byok_entry] - test_config.configuration.rag.inline = ["test-knowledge"] - test_config.configuration.rag.tool = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [byok_entry] + test_config.configuration.rag.retrieval.inline.sources = ["test-knowledge"] + test_config.configuration.rag.retrieval.tool.sources = ["test-knowledge"] mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) @@ -381,8 +381,8 @@ async def test_responses_byok_inline_rag_only_configured_rag_id_is_queried( # p entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a"] mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) @@ -443,8 +443,8 @@ async def test_responses_byok_score_multiplier_shifts_chunk_priority( # pylint: entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 5.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a", "source-b"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a", "source-b"] mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) @@ -526,15 +526,15 @@ async def test_responses_rag_content_limit_caps_retrieved_results( # pylint: di entry.vector_db_id = "vs-big-source" entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry] - test_config.configuration.rag.inline = ["big-source"] - test_config.configuration.reranker.enabled = False + test_config.configuration.rag.byok.stores = [entry] + test_config.configuration.rag.retrieval.inline.sources = ["big-source"] + test_config.configuration.rag.retrieval.inline.reranker.enabled = False mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) # Generate more chunks than INLINE_RAG_MAX_CHUNKS - num_chunks = constants.INLINE_RAG_MAX_CHUNKS + 1 + num_chunks = constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + 1 chunks_data = [ (f"Chunk content {i}", f"chunk-{i}", round(0.50 + i * 0.03, 2)) for i in range(num_chunks) @@ -560,7 +560,9 @@ async def test_responses_rag_content_limit_caps_retrieved_results( # pylint: di create_call = mock_client.responses.create.call_args_list[0] input_text = create_call.kwargs.get("input", "") - expected_header = f"file_search found {constants.INLINE_RAG_MAX_CHUNKS} chunks:" + expected_header = ( + f"file_search found {constants.DEFAULT_INLINE_RAG_MAX_CHUNKS} chunks:" + ) assert expected_header in input_text # The highest-scored chunk should be present @@ -595,14 +597,14 @@ async def test_responses_rag_content_limit_caps_across_multiple_sources( # pyli entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a", "source-b"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a", "source-b"] mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) # Overlapping score bands so top-k must pick from both sources - n = constants.INLINE_RAG_MAX_CHUNKS + n = constants.DEFAULT_INLINE_RAG_MAX_CHUNKS resp_a = _make_vector_io_response( mocker, [ @@ -642,7 +644,9 @@ async def _side_effect(**kwargs: Any) -> Any: create_call = mock_client.responses.create.call_args_list[0] input_text = create_call.kwargs.get("input", "") - expected_header = f"file_search found {constants.INLINE_RAG_MAX_CHUNKS} chunks:" + expected_header = ( + f"file_search found {constants.DEFAULT_INLINE_RAG_MAX_CHUNKS} chunks:" + ) assert expected_header in input_text # Both sources should survive the cap (high-scoring chunks from each) @@ -669,21 +673,20 @@ async def test_responses_rag_content_limit_caps_inline_rag( # pylint: disable=t - Context chunk count equals the lowered INLINE_RAG_MAX_CHUNKS - Only the highest-scored chunks appear in the context """ - mocker.patch("utils.vector_search.constants.INLINE_RAG_MAX_CHUNKS", 3) - entry = mocker.MagicMock() entry.rag_id = "big-source" entry.vector_db_id = "vs-big-source" entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry] - test_config.configuration.rag.inline = ["big-source"] - test_config.configuration.reranker.enabled = False + test_config.configuration.rag.byok.stores = [entry] + test_config.configuration.rag.retrieval.inline.sources = ["big-source"] + test_config.configuration.rag.retrieval.inline.max_chunks = 3 + test_config.configuration.rag.retrieval.inline.reranker.enabled = False mock_client = _build_responses_mock_client(mocker) _patch_all_client_holders(mocker, mock_client) - num_chunks = constants.BYOK_RAG_MAX_CHUNKS + num_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS chunks_data = [ (f"Chunk content {i}", f"chunk-{i}", round(0.50 + i * 0.03, 2)) for i in range(num_chunks) diff --git a/tests/integration/endpoints/test_streaming_query_byok_integration.py b/tests/integration/endpoints/test_streaming_query_byok_integration.py index 1a371fdb8..9befbb730 100644 --- a/tests/integration/endpoints/test_streaming_query_byok_integration.py +++ b/tests/integration/endpoints/test_streaming_query_byok_integration.py @@ -182,7 +182,7 @@ def byok_config_fixture(test_config: AppConfig, mocker: MockerFixture) -> AppCon byok_entry.score_multiplier = 1.0 byok_entry.model_dump.return_value = { "rag_id": "test-knowledge", - "rag_type": "inline::faiss", + "backend": "faiss", "embedding_model": "sentence-transformers/all-mpnet-base-v2", "embedding_dimension": 768, "vector_db_id": "vs-byok-knowledge", @@ -190,8 +190,8 @@ def byok_config_fixture(test_config: AppConfig, mocker: MockerFixture) -> AppCon "score_multiplier": 1.0, } - test_config.configuration.byok_rag = [byok_entry] - test_config.configuration.rag.inline = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [byok_entry] + test_config.configuration.rag.retrieval.inline.sources = ["test-knowledge"] return test_config @@ -207,7 +207,7 @@ def byok_tool_config_fixture( byok_entry.score_multiplier = 1.0 byok_entry.model_dump.return_value = { "rag_id": "test-knowledge", - "rag_type": "inline::faiss", + "backend": "faiss", "embedding_model": "sentence-transformers/all-mpnet-base-v2", "embedding_dimension": 768, "vector_db_id": "vs-byok-knowledge", @@ -215,9 +215,9 @@ def byok_tool_config_fixture( "score_multiplier": 1.0, } - test_config.configuration.byok_rag = [byok_entry] - test_config.configuration.rag.inline = [] - test_config.configuration.rag.tool = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [byok_entry] + test_config.configuration.rag.retrieval.inline.sources = [] + test_config.configuration.rag.retrieval.tool.sources = ["test-knowledge"] return test_config @@ -294,8 +294,8 @@ async def test_streaming_query_byok_inline_rag_with_request_vector_store_ids( entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a"] mock_holder_class = mocker.patch( "app.endpoints.streaming_query.AsyncLlamaStackClientHolder" @@ -359,8 +359,8 @@ async def test_streaming_query_byok_request_vector_store_ids_filters_configured_ entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a", "source-b"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a", "source-b"] mock_holder_class = mocker.patch( "app.endpoints.streaming_query.AsyncLlamaStackClientHolder" @@ -635,9 +635,9 @@ async def test_streaming_query_byok_combined_inline_and_tool_rag( byok_entry.rag_id = "test-knowledge" byok_entry.vector_db_id = "vs-byok-knowledge" byok_entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [byok_entry] - test_config.configuration.rag.inline = ["test-knowledge"] - test_config.configuration.rag.tool = ["test-knowledge"] + test_config.configuration.rag.byok.stores = [byok_entry] + test_config.configuration.rag.retrieval.inline.sources = ["test-knowledge"] + test_config.configuration.rag.retrieval.tool.sources = ["test-knowledge"] # Mock Llama Stack client mock_holder_class = mocker.patch( @@ -715,8 +715,8 @@ async def test_streaming_query_byok_only_configured_rag_id_is_queried( entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a"] mock_holder_class = mocker.patch( "app.endpoints.streaming_query.AsyncLlamaStackClientHolder" @@ -793,8 +793,8 @@ async def test_streaming_query_byok_score_multiplier_shifts_priority( # pylint: entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 5.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a", "source-b"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a", "source-b"] mock_holder_class = mocker.patch( "app.endpoints.streaming_query.AsyncLlamaStackClientHolder" @@ -874,8 +874,8 @@ async def test_streaming_query_rag_content_limit_caps_context( # pylint: disabl entry.vector_db_id = "vs-big-source" entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry] - test_config.configuration.rag.inline = ["big-source"] + test_config.configuration.rag.byok.stores = [entry] + test_config.configuration.rag.retrieval.inline.sources = ["big-source"] mock_holder_class = mocker.patch( "app.endpoints.streaming_query.AsyncLlamaStackClientHolder" @@ -883,7 +883,7 @@ async def test_streaming_query_rag_content_limit_caps_context( # pylint: disabl mock_client = _build_base_streaming_mock_client(mocker) # Generate more chunks than INLINE_RAG_MAX_CHUNKS - num_chunks = constants.INLINE_RAG_MAX_CHUNKS + 5 + num_chunks = constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + 5 chunks_data = [ (f"Chunk content {i}", f"chunk-{i}", round(0.50 + i * 0.03, 2)) for i in range(num_chunks) @@ -912,7 +912,9 @@ async def test_streaming_query_rag_content_limit_caps_context( # pylint: disabl # Verify the context header reports the capped count await _collect_sse_events(response) prompt = mock_streaming_query_agent.run_stream_events.call_args.args[0] - expected_header = f"file_search found {constants.INLINE_RAG_MAX_CHUNKS} chunks:" + expected_header = ( + f"file_search found {constants.DEFAULT_INLINE_RAG_MAX_CHUNKS} chunks:" + ) assert expected_header in prompt # The lowest-scoring chunk should NOT be in the context @@ -949,8 +951,8 @@ async def test_streaming_query_rag_content_limit_caps_across_multiple_sources( entry_b.vector_db_id = "vs-source-b" entry_b.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry_a, entry_b] - test_config.configuration.rag.inline = ["source-a", "source-b"] + test_config.configuration.rag.byok.stores = [entry_a, entry_b] + test_config.configuration.rag.retrieval.inline.sources = ["source-a", "source-b"] mock_holder_class = mocker.patch( "app.endpoints.streaming_query.AsyncLlamaStackClientHolder" @@ -958,7 +960,7 @@ async def test_streaming_query_rag_content_limit_caps_across_multiple_sources( mock_client = _build_base_streaming_mock_client(mocker) # Overlapping score bands so top-k must pick from both sources - n = constants.INLINE_RAG_MAX_CHUNKS + n = constants.DEFAULT_INLINE_RAG_MAX_CHUNKS resp_a = _make_vector_io_response( mocker, [ @@ -1000,7 +1002,9 @@ async def _side_effect(**kwargs: Any) -> Any: await _collect_sse_events(response) prompt = mock_streaming_query_agent.run_stream_events.call_args.args[0] - expected_header = f"file_search found {constants.INLINE_RAG_MAX_CHUNKS} chunks:" + expected_header = ( + f"file_search found {constants.DEFAULT_INLINE_RAG_MAX_CHUNKS} chunks:" + ) assert expected_header in prompt # Both sources must appear in the context (overlapping scores guarantee this) @@ -1029,23 +1033,22 @@ async def test_streaming_query_rag_content_limit_caps_inline_rag( # pylint: dis - Context chunk count equals the lowered INLINE_RAG_MAX_CHUNKS - Only the highest-scored chunks appear in the context """ - mocker.patch("utils.vector_search.constants.INLINE_RAG_MAX_CHUNKS", 3) - entry = mocker.MagicMock() entry.rag_id = "big-source" entry.vector_db_id = "vs-big-source" entry.score_multiplier = 1.0 - test_config.configuration.byok_rag = [entry] - test_config.configuration.rag.inline = ["big-source"] - test_config.configuration.reranker.enabled = False + test_config.configuration.rag.byok.stores = [entry] + test_config.configuration.rag.retrieval.inline.sources = ["big-source"] + test_config.configuration.rag.retrieval.inline.max_chunks = 3 + test_config.configuration.rag.retrieval.inline.reranker.enabled = False mock_holder_class = mocker.patch( "app.endpoints.streaming_query.AsyncLlamaStackClientHolder" ) mock_client = _build_base_streaming_mock_client(mocker) - num_chunks = constants.BYOK_RAG_MAX_CHUNKS + num_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS chunks_data = [ (f"Chunk content {i}", f"chunk-{i}", round(0.50 + i * 0.03, 2)) for i in range(num_chunks) diff --git a/tests/unit/app/endpoints/test_rags.py b/tests/unit/app/endpoints/test_rags.py index 563c223fe..3134d864b 100644 --- a/tests/unit/app/endpoints/test_rags.py +++ b/tests/unit/app/endpoints/test_rags.py @@ -269,24 +269,28 @@ def _make_byok_config(tmp_path: Any) -> AppConfig: "user_data_collection": {}, "authentication": {"module": "noop"}, "authorization": {"access_rules": []}, - "byok_rag": [ - { - "rag_id": "ocp-4.18-docs", - "rag_type": "inline::faiss", - "embedding_model": "all-MiniLM-L6-v2", - "embedding_dimension": 384, - "vector_db_id": "vs_abc123", - "db_path": str(db_file), + "rag": { + "byok": { + "stores": [ + { + "rag_id": "ocp-4.18-docs", + "backend": "faiss", + "embedding_model": "all-MiniLM-L6-v2", + "embedding_dimension": 384, + "vector_db_id": "vs_abc123", + "db_path": str(db_file), + }, + { + "rag_id": "company-kb", + "backend": "faiss", + "embedding_model": "all-MiniLM-L6-v2", + "embedding_dimension": 384, + "vector_db_id": "vs_def456", + "db_path": str(db_file), + }, + ], }, - { - "rag_id": "company-kb", - "rag_type": "inline::faiss", - "embedding_model": "all-MiniLM-L6-v2", - "embedding_dimension": 384, - "vector_db_id": "vs_def456", - "db_path": str(db_file), - }, - ], + }, } ) return cfg @@ -379,7 +383,7 @@ def __init__(self) -> None: def test_resolve_rag_id_to_vector_db_id_with_mapping(tmp_path: Path) -> None: """Test that _resolve_rag_id_to_vector_db_id maps rag_id to vector_db_id.""" byok_config = _make_byok_config(str(tmp_path)) - byok_rags = byok_config.configuration.byok_rag + byok_rags = byok_config.configuration.rag.byok.stores assert _resolve_rag_id_to_vector_db_id("ocp-4.18-docs", byok_rags) == "vs_abc123" assert _resolve_rag_id_to_vector_db_id("company-kb", byok_rags) == "vs_def456" @@ -387,5 +391,5 @@ def test_resolve_rag_id_to_vector_db_id_with_mapping(tmp_path: Path) -> None: def test_resolve_rag_id_to_vector_db_id_passthrough(tmp_path: Path) -> None: """Test that unmapped IDs are passed through unchanged.""" byok_config = _make_byok_config(str(tmp_path)) - byok_rags = byok_config.configuration.byok_rag + byok_rags = byok_config.configuration.rag.byok.stores assert _resolve_rag_id_to_vector_db_id("vs_unknown", byok_rags) == "vs_unknown" diff --git a/tests/unit/models/config/test_byok_rag.py b/tests/unit/models/config/test_byok_rag.py index 1ac098299..291408caa 100644 --- a/tests/unit/models/config/test_byok_rag.py +++ b/tests/unit/models/config/test_byok_rag.py @@ -1,4 +1,4 @@ -"""Unit tests for ByokRag model.""" +"""Unit tests for RagStore model.""" import pytest from pydantic import ValidationError @@ -6,76 +6,76 @@ from constants import ( DEFAULT_EMBEDDING_DIMENSION, DEFAULT_EMBEDDING_MODEL, - DEFAULT_RAG_TYPE, + DEFAULT_RAG_BACKEND, DEFAULT_SCORE_MULTIPLIER, ) -from models.config import ByokRag +from models.config import ByokConfiguration, RagStore -def test_byok_rag_configuration_default_values() -> None: - """Test the ByokRag constructor. +def test_rag_store_configuration_default_values() -> None: + """Test the RagStore constructor. - Verify that ByokRag initializes correctly when only required fields are provided. + Verify that RagStore initializes correctly when only required fields are provided. Asserts that the instance stores the given `rag_id`, `vector_db_id`, and `db_path`, and that unspecified fields use the module's default values for - `rag_type`, `embedding_model`, `embedding_dimension`, and + `backend`, `embedding_model`, `embedding_dimension`, and `score_multiplier`. """ - byok_rag = ByokRag( # pyright: ignore[reportCallIssue] + rag_store = RagStore( # pyright: ignore[reportCallIssue] rag_id="rag_id", vector_db_id="vector_db_id", db_path="tests/configuration/rag.txt", ) - assert byok_rag is not None - assert byok_rag.rag_id == "rag_id" - assert byok_rag.rag_type == DEFAULT_RAG_TYPE - assert byok_rag.embedding_model == DEFAULT_EMBEDDING_MODEL - assert byok_rag.embedding_dimension == DEFAULT_EMBEDDING_DIMENSION - assert byok_rag.vector_db_id == "vector_db_id" - assert byok_rag.db_path == "tests/configuration/rag.txt" - assert byok_rag.score_multiplier == DEFAULT_SCORE_MULTIPLIER + assert rag_store is not None + assert rag_store.rag_id == "rag_id" + assert rag_store.backend == DEFAULT_RAG_BACKEND + assert rag_store.embedding_model == DEFAULT_EMBEDDING_MODEL + assert rag_store.embedding_dimension == DEFAULT_EMBEDDING_DIMENSION + assert rag_store.vector_db_id == "vector_db_id" + assert rag_store.db_path == "tests/configuration/rag.txt" + assert rag_store.score_multiplier == DEFAULT_SCORE_MULTIPLIER -def test_byok_rag_configuration_nondefault_values() -> None: - """Test the ByokRag constructor. +def test_rag_store_configuration_nondefault_values() -> None: + """Test the RagStore constructor. - Verify that ByokRag class accepts and stores non-default configuration values. + Verify that RagStore class accepts and stores non-default configuration values. - Asserts that rag_id, rag_type, embedding_model, embedding_dimension, and + Asserts that rag_id, backend, embedding_model, embedding_dimension, and vector_db_id match the provided inputs and that db_path is converted to a Path. """ - byok_rag = ByokRag( + rag_store = RagStore( rag_id="rag_id", - rag_type="rag_type", + backend="faiss", embedding_model="embedding_model", embedding_dimension=1024, vector_db_id="vector_db_id", db_path="tests/configuration/rag.txt", score_multiplier=1.0, ) - assert byok_rag is not None - assert byok_rag.rag_id == "rag_id" - assert byok_rag.rag_type == "rag_type" - assert byok_rag.embedding_model == "embedding_model" - assert byok_rag.embedding_dimension == 1024 - assert byok_rag.vector_db_id == "vector_db_id" - assert byok_rag.db_path == "tests/configuration/rag.txt" + assert rag_store is not None + assert rag_store.rag_id == "rag_id" + assert rag_store.backend == "faiss" + assert rag_store.embedding_model == "embedding_model" + assert rag_store.embedding_dimension == 1024 + assert rag_store.vector_db_id == "vector_db_id" + assert rag_store.db_path == "tests/configuration/rag.txt" -def test_byok_rag_configuration_wrong_dimension() -> None: - """Test the ByokRag constructor. +def test_rag_store_configuration_wrong_dimension() -> None: + """Test the RagStore constructor. - Verify constructing ByokRag with embedding_dimension less than or equal to + Verify constructing RagStore with embedding_dimension less than or equal to zero raises a ValidationError. The raised ValidationError's message must contain "should be greater than 0". """ with pytest.raises(ValidationError, match="should be greater than 0"): - _ = ByokRag( + _ = RagStore( rag_id="rag_id", - rag_type="rag_type", + backend="faiss", embedding_model="embedding_model", embedding_dimension=-1024, vector_db_id="vector_db_id", @@ -84,10 +84,10 @@ def test_byok_rag_configuration_wrong_dimension() -> None: ) -def test_byok_rag_configuration_empty_rag_id() -> None: - """Test the ByokRag constructor. +def test_rag_store_configuration_empty_rag_id() -> None: + """Test the RagStore constructor. - Validate that constructing a ByokRag with an empty `rag_id` raises a validation error. + Validate that constructing a RagStore with an empty `rag_id` raises a validation error. Expects a `pydantic.ValidationError` whose message contains "String should have at least 1 character". @@ -95,9 +95,9 @@ def test_byok_rag_configuration_empty_rag_id() -> None: with pytest.raises( ValidationError, match="String should have at least 1 character" ): - _ = ByokRag( + _ = RagStore( rag_id="", - rag_type="rag_type", + backend="faiss", embedding_model="embedding_model", embedding_dimension=1024, vector_db_id="vector_db_id", @@ -106,21 +106,21 @@ def test_byok_rag_configuration_empty_rag_id() -> None: ) -def test_byok_rag_configuration_empty_rag_type() -> None: - """Test the ByokRag constructor. +def test_rag_store_configuration_empty_backend() -> None: + """Test the RagStore constructor. - Verify that constructing a ByokRag with an empty `rag_type` raises a validation error. + Verify that constructing a RagStore with an empty `backend` raises a validation error. Raises: - ValidationError: if `rag_type` is an empty string; error message + ValidationError: if `backend` is an empty string; error message includes "String should have at least 1 character". """ with pytest.raises( ValidationError, match="String should have at least 1 character" ): - _ = ByokRag( + _ = RagStore( rag_id="rag_id", - rag_type="", + backend="", embedding_model="embedding_model", embedding_dimension=1024, vector_db_id="vector_db_id", @@ -129,10 +129,23 @@ def test_byok_rag_configuration_empty_rag_type() -> None: ) -def test_byok_rag_configuration_empty_embedding_model() -> None: - """Test the ByokRag constructor. +def test_rag_store_configuration_unsupported_backend() -> None: + """Test that unsupported backend values are rejected.""" + with pytest.raises(ValidationError, match="Unsupported RAG backend"): + _ = RagStore( + rag_id="rag_id", + backend="unsupported", + embedding_model="embedding_model", + embedding_dimension=1024, + vector_db_id="vector_db_id", + db_path="tests/configuration/rag.txt", + ) + - Verify that constructing a ByokRag with an empty `embedding_model` raises a validation error. +def test_rag_store_configuration_empty_embedding_model() -> None: + """Test the RagStore constructor. + + Verify that constructing a RagStore with an empty `embedding_model` raises a validation error. Expects a pydantic.ValidationError whose message contains "String should have at least 1 character". @@ -140,9 +153,9 @@ def test_byok_rag_configuration_empty_embedding_model() -> None: with pytest.raises( ValidationError, match="String should have at least 1 character" ): - _ = ByokRag( + _ = RagStore( rag_id="rag_id", - rag_type="rag_type", + backend="faiss", embedding_model="", embedding_dimension=1024, vector_db_id="vector_db_id", @@ -151,10 +164,10 @@ def test_byok_rag_configuration_empty_embedding_model() -> None: ) -def test_byok_rag_configuration_empty_vector_db_id() -> None: - """Test the ByokRag constructor. +def test_rag_store_configuration_empty_vector_db_id() -> None: + """Test the RagStore constructor. - Ensure constructing a ByokRag with an empty `vector_db_id` raises a ValidationError. + Ensure constructing a RagStore with an empty `vector_db_id` raises a ValidationError. Asserts that Pydantic validation fails with a message containing "String should have at least 1 character". @@ -162,9 +175,9 @@ def test_byok_rag_configuration_empty_vector_db_id() -> None: with pytest.raises( ValidationError, match="String should have at least 1 character" ): - _ = ByokRag( + _ = RagStore( rag_id="rag_id", - rag_type="rag_type", + backend="faiss", embedding_model="embedding_model", embedding_dimension=1024, vector_db_id="", @@ -173,26 +186,26 @@ def test_byok_rag_configuration_empty_vector_db_id() -> None: ) -def test_byok_rag_configuration_custom_score_multiplier() -> None: - """Test ByokRag with custom score_multiplier.""" - byok_rag = ByokRag( +def test_rag_store_configuration_custom_score_multiplier() -> None: + """Test RagStore with custom score_multiplier.""" + rag_store = RagStore( rag_id="rag_id", - rag_type="rag_type", + backend="faiss", vector_db_id="vector_db_id", embedding_model="embedding_model", embedding_dimension=1024, db_path="tests/configuration/rag.txt", score_multiplier=2.5, ) - assert byok_rag.score_multiplier == 2.5 + assert rag_store.score_multiplier == 2.5 -def test_byok_rag_configuration_score_multiplier_must_be_positive() -> None: +def test_rag_store_configuration_score_multiplier_must_be_positive() -> None: """Test that score_multiplier must be greater than 0.""" with pytest.raises(ValidationError, match="greater than 0"): - _ = ByokRag( + _ = RagStore( rag_id="rag_id", - rag_type="rag_type", + backend="faiss", vector_db_id="vector_db_id", embedding_model="embedding_model", embedding_dimension=1024, @@ -202,23 +215,23 @@ def test_byok_rag_configuration_score_multiplier_must_be_positive() -> None: def test_byok_rag_faiss_requires_db_path() -> None: - """Test that inline::faiss requires db_path.""" + """Test that faiss backend requires db_path.""" with pytest.raises(ValidationError, match="db_path is required"): - _ = ByokRag( + _ = RagStore( rag_id="rag_id", - rag_type="inline::faiss", + backend="faiss", vector_db_id="vector_db_id", ) def test_byok_rag_pgvector_defaults() -> None: """Test pgvector auto-populates connection fields with env var defaults.""" - store = ByokRag( + store = RagStore( rag_id="pg_store", - rag_type="remote::pgvector", + backend="pgvector", vector_db_id="vs_pg", ) - assert store.rag_type == "remote::pgvector" + assert store.backend == "pgvector" assert store.host == "${env.POSTGRES_HOST}" assert store.port == "${env.POSTGRES_PORT}" assert store.db == "${env.POSTGRES_DATABASE}" @@ -230,9 +243,9 @@ def test_byok_rag_pgvector_defaults() -> None: def test_byok_rag_pgvector_custom_connection_fields() -> None: """Test pgvector accepts custom connection field values.""" - store = ByokRag( + store = RagStore( rag_id="pg_store", - rag_type="remote::pgvector", + backend="pgvector", vector_db_id="vs_pg", host="db.example.com", port="5433", @@ -249,9 +262,9 @@ def test_byok_rag_pgvector_custom_connection_fields() -> None: def test_byok_rag_pgvector_partial_overrides() -> None: """Test pgvector fills only missing connection fields with defaults.""" - store = ByokRag( + store = RagStore( rag_id="pg_store", - rag_type="remote::pgvector", + backend="pgvector", vector_db_id="vs_pg", host="custom-host", ) @@ -261,9 +274,47 @@ def test_byok_rag_pgvector_partial_overrides() -> None: def test_byok_rag_pgvector_does_not_require_db_path() -> None: """Test pgvector does not require db_path.""" - store = ByokRag( + store = RagStore( rag_id="pg_store", - rag_type="remote::pgvector", + backend="pgvector", vector_db_id="vs_pg", ) assert store.db_path is None + + +def test_byok_configuration_rejects_duplicate_rag_ids() -> None: + """Test that duplicate rag_id values are rejected.""" + with pytest.raises(ValidationError, match="Duplicate rag_id 'docs'"): + ByokConfiguration( + stores=[ + RagStore( + rag_id="docs", + vector_db_id="vs_1", + db_path="/tmp/a.db", + ), + RagStore( + rag_id="docs", + vector_db_id="vs_2", + db_path="/tmp/b.db", + ), + ], + ) + + +def test_byok_configuration_allows_unique_rag_ids() -> None: + """Test that unique rag_id values are accepted.""" + config = ByokConfiguration( + stores=[ + RagStore( + rag_id="docs-a", + vector_db_id="vs_1", + db_path="/tmp/a.db", + ), + RagStore( + rag_id="docs-b", + vector_db_id="vs_2", + db_path="/tmp/b.db", + ), + ], + ) + assert len(config.stores) == 2 diff --git a/tests/unit/models/config/test_dump_configuration.py b/tests/unit/models/config/test_dump_configuration.py index e1cd67816..2310b8c97 100644 --- a/tests/unit/models/config/test_dump_configuration.py +++ b/tests/unit/models/config/test_dump_configuration.py @@ -11,7 +11,7 @@ import constants from models.config import ( - ByokRag, + ByokConfiguration, CompactionConfiguration, Configuration, CORSConfiguration, @@ -23,6 +23,8 @@ QuotaHandlersConfiguration, QuotaLimiterConfiguration, QuotaSchedulerConfiguration, + RagConfiguration, + RagStore, ServiceConfiguration, SkillsConfiguration, TLSConfiguration, @@ -98,10 +100,10 @@ def test_dump_configuration_minimal_cfg(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content assert "azure_entra_id" in content - assert "reranker" in content + assert "reranker" in content["rag"]["retrieval"]["inline"] # check the whole deserialized JSON file content assert content == { @@ -193,7 +195,6 @@ def test_dump_configuration_minimal_cfg(tmp_path: Path) -> None: "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -211,13 +212,24 @@ def test_dump_configuration_minimal_cfg(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": {"max_chunks": 10, "stores": []}, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -225,10 +237,6 @@ def test_dump_configuration_minimal_cfg(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -307,10 +315,10 @@ def test_dump_configuration_valid_values(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content assert "azure_entra_id" in content - assert "reranker" in content + assert "reranker" in content["rag"]["retrieval"]["inline"] # check the whole deserialized JSON file content assert content == { @@ -416,7 +424,6 @@ def test_dump_configuration_valid_values(tmp_path: Path) -> None: "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -434,13 +441,27 @@ def test_dump_configuration_valid_values(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": { + "max_chunks": 10, + "stores": [], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -448,10 +469,6 @@ def test_dump_configuration_valid_values(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -666,10 +683,10 @@ def test_dump_configuration_with_quota_limiters(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content assert "azure_entra_id" in content - assert "reranker" in content + assert "reranker" in content["rag"]["retrieval"]["inline"] # check the whole deserialized JSON file content assert content == { @@ -775,7 +792,6 @@ def test_dump_configuration_with_quota_limiters(tmp_path: Path) -> None: "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -808,13 +824,27 @@ def test_dump_configuration_with_quota_limiters(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": { + "max_chunks": 10, + "stores": [], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -822,10 +852,6 @@ def test_dump_configuration_with_quota_limiters(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -926,7 +952,7 @@ def test_dump_configuration_with_quota_limiters_different_values( assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content # check the whole deserialized JSON file content @@ -1033,7 +1059,6 @@ def test_dump_configuration_with_quota_limiters_different_values( "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -1066,13 +1091,27 @@ def test_dump_configuration_with_quota_limiters_different_values( }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": { + "max_chunks": 10, + "stores": [], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -1080,10 +1119,6 @@ def test_dump_configuration_with_quota_limiters_different_values( }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -1136,13 +1171,17 @@ def test_dump_configuration_byok(tmp_path: Path) -> None: default_provider="default_provider", default_model="default_model", ), - byok_rag=[ - ByokRag( - rag_id="rag_id", - vector_db_id="vector_db_id", - db_path="tests/configuration/rag.txt", + rag=RagConfiguration( + byok=ByokConfiguration( + stores=[ + RagStore( + rag_id="rag_id", + vector_db_id="vector_db_id", + db_path="tests/configuration/rag.txt", + ), + ], ), - ], + ), ) assert cfg is not None dump_file = tmp_path / "test.json" @@ -1164,7 +1203,7 @@ def test_dump_configuration_byok(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content # check the whole deserialized JSON file content @@ -1271,22 +1310,6 @@ def test_dump_configuration_byok(tmp_path: Path) -> None: "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [ - { - "db_path": "tests/configuration/rag.txt", - "embedding_dimension": 768, - "embedding_model": "sentence-transformers/all-mpnet-base-v2", - "rag_id": "rag_id", - "rag_type": "inline::faiss", - "vector_db_id": "vector_db_id", - "score_multiplier": 1.0, - "host": None, - "port": None, - "db": None, - "user": None, - "password": None, - }, - ], "quota_handlers": { "sqlite": None, "postgres": None, @@ -1304,13 +1327,42 @@ def test_dump_configuration_byok(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": { + "max_chunks": 10, + "stores": [ + { + "rag_id": "rag_id", + "backend": "faiss", + "embedding_model": "sentence-transformers/all-mpnet-base-v2", + "embedding_dimension": 768, + "vector_db_id": "vector_db_id", + "db_path": "tests/configuration/rag.txt", + "score_multiplier": 1.0, + "host": None, + "port": None, + "db": None, + "user": None, + "password": None, + }, + ], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -1318,10 +1370,6 @@ def test_dump_configuration_byok(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -1397,7 +1445,7 @@ def test_dump_configuration_pg_namespace(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content # check the whole deserialized JSON file content @@ -1504,7 +1552,6 @@ def test_dump_configuration_pg_namespace(tmp_path: Path) -> None: "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -1522,13 +1569,27 @@ def test_dump_configuration_pg_namespace(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": { + "max_chunks": 10, + "stores": [], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -1536,10 +1597,6 @@ def test_dump_configuration_pg_namespace(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -1773,10 +1830,10 @@ def test_dump_configuration_allow_degraded_mode(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content assert "azure_entra_id" in content - assert "reranker" in content + assert "reranker" in content["rag"]["retrieval"]["inline"] # check the whole deserialized JSON file content assert content == { @@ -1882,7 +1939,6 @@ def test_dump_configuration_allow_degraded_mode(tmp_path: Path) -> None: "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -1900,13 +1956,27 @@ def test_dump_configuration_allow_degraded_mode(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": { + "max_chunks": 10, + "stores": [], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -1914,10 +1984,6 @@ def test_dump_configuration_allow_degraded_mode(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -1997,10 +2063,10 @@ def test_dump_configuration_max_retries_settings(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content assert "azure_entra_id" in content - assert "reranker" in content + assert "reranker" in content["rag"]["retrieval"]["inline"] # check the whole deserialized JSON file content assert content == { @@ -2106,7 +2172,6 @@ def test_dump_configuration_max_retries_settings(tmp_path: Path) -> None: "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -2124,13 +2189,27 @@ def test_dump_configuration_max_retries_settings(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": { + "max_chunks": 10, + "stores": [], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -2138,10 +2217,6 @@ def test_dump_configuration_max_retries_settings(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -2221,10 +2296,10 @@ def test_dump_configuration_retry_count_settings(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content assert "azure_entra_id" in content - assert "reranker" in content + assert "reranker" in content["rag"]["retrieval"]["inline"] # check the whole deserialized JSON file content assert content == { @@ -2330,7 +2405,6 @@ def test_dump_configuration_retry_count_settings(tmp_path: Path) -> None: "buffer_max_ratio": 0.3, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -2348,13 +2422,27 @@ def test_dump_configuration_retry_count_settings(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": { + "max_chunks": 10, + "stores": [], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -2362,10 +2450,6 @@ def test_dump_configuration_retry_count_settings(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } @@ -2451,10 +2535,10 @@ def test_dump_configuration_specific_compaction_values(tmp_path: Path) -> None: assert "customization" in content assert "inference" in content assert "database" in content - assert "byok_rag" in content + assert "rag" in content assert "quota_handlers" in content assert "azure_entra_id" in content - assert "reranker" in content + assert "reranker" in content["rag"]["retrieval"]["inline"] assert "compaction" in content # check the whole deserialized JSON file content @@ -2561,7 +2645,6 @@ def test_dump_configuration_specific_compaction_values(tmp_path: Path) -> None: "buffer_max_ratio": 0.5, }, "approvals": _DEFAULT_APPROVALS_DUMP, - "byok_rag": [], "quota_handlers": { "sqlite": None, "postgres": None, @@ -2579,13 +2662,24 @@ def test_dump_configuration_specific_compaction_values(tmp_path: Path) -> None: }, "azure_entra_id": None, "rag": { - "inline": [], - "tool": [], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": None, + "byok": {"max_chunks": 10, "stores": []}, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": None, + "max_chunks": 5, + }, + "retrieval": { + "inline": { + "sources": [], + "max_chunks": 10, + "reranker": { + "enabled": False, + "model": "cross-encoder/ms-marco-MiniLM-L6-v2", + }, + }, + "tool": {"sources": [], "max_chunks": 10, "reranker": None}, + }, }, "rlsapi_v1": { "allow_verbose_infer": False, @@ -2593,10 +2687,6 @@ def test_dump_configuration_specific_compaction_values(tmp_path: Path) -> None: }, "splunk": None, "deployment_environment": "development", - "reranker": { - "enabled": False, - "model": "cross-encoder/ms-marco-MiniLM-L6-v2", - }, "saved_prompts": _DEFAULT_SAVED_PROMPTS_DUMP, "skills": None, } diff --git a/tests/unit/models/config/test_rag_configuration.py b/tests/unit/models/config/test_rag_configuration.py index bc44ef154..9a72f9660 100644 --- a/tests/unit/models/config/test_rag_configuration.py +++ b/tests/unit/models/config/test_rag_configuration.py @@ -7,7 +7,79 @@ from pydantic import ValidationError import constants -from models.config import OkpConfiguration, RagConfiguration +from models.config import ( + ByokConfiguration, + OkpConfiguration, + RagConfiguration, + RagStore, + RetrievalConfiguration, + RetrievalStrategyConfiguration, +) + + +class TestRetrievalStrategyConfiguration: + """Tests for RetrievalStrategyConfiguration model.""" + + def test_default_values(self) -> None: + """Test default values.""" + config = RetrievalStrategyConfiguration() + assert config.sources == [] + assert config.max_chunks == constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + + def test_custom_values(self) -> None: + """Test custom sources and max_chunks.""" + config = RetrievalStrategyConfiguration( + sources=["store-1", "okp"], max_chunks=20 + ) + assert config.sources == ["store-1", "okp"] + assert config.max_chunks == 20 + + +class TestRetrievalConfiguration: + """Tests for RetrievalConfiguration model.""" + + def test_default_values(self) -> None: + """Test default inline and tool strategies.""" + config = RetrievalConfiguration() + assert config.inline.sources == [] + assert config.inline.max_chunks == constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + assert config.tool.sources == [] + assert config.tool.max_chunks == constants.DEFAULT_TOOL_RAG_MAX_CHUNKS + + def test_custom_values(self) -> None: + """Test custom inline and tool strategies.""" + config = RetrievalConfiguration( + inline=RetrievalStrategyConfiguration( + sources=["store-1", "okp"], max_chunks=8 + ), + tool=RetrievalStrategyConfiguration(sources=["store-1"], max_chunks=12), + ) + assert config.inline.sources == ["store-1", "okp"] + assert config.inline.max_chunks == 8 + assert config.tool.sources == ["store-1"] + assert config.tool.max_chunks == 12 + + +class TestByokConfiguration: + """Tests for ByokConfiguration model.""" + + def test_default_values(self) -> None: + """Test default values.""" + config = ByokConfiguration() + assert config.stores == [] + assert config.max_chunks == constants.DEFAULT_BYOK_RAG_MAX_CHUNKS + + def test_with_stores(self) -> None: + """Test with store entries.""" + store = RagStore( + rag_id="test", + vector_db_id="vs_123", + db_path="/tmp/test.db", + ) + config = ByokConfiguration(stores=[store], max_chunks=15) + assert len(config.stores) == 1 + assert config.stores[0].rag_id == "test" + assert config.max_chunks == 15 class TestRagConfiguration: @@ -16,39 +88,92 @@ class TestRagConfiguration: def test_default_values(self) -> None: """Test that RagConfiguration has correct default values.""" config = RagConfiguration() - assert config.inline == [] - assert config.tool == [] + assert config.byok.stores == [] + assert config.byok.max_chunks == constants.DEFAULT_BYOK_RAG_MAX_CHUNKS + assert config.okp.offline is True + assert config.okp.max_chunks == constants.DEFAULT_OKP_RAG_MAX_CHUNKS + assert config.retrieval.inline.sources == [] + assert config.retrieval.tool.sources == [] def test_inline_with_byok_ids(self) -> None: - """Test inline list with BYOK rag IDs.""" - config = RagConfiguration(inline=["store-1", "store-2"]) - assert config.inline == ["store-1", "store-2"] - assert config.tool == [] + """Test inline sources with BYOK rag IDs.""" + stores = [ + RagStore(rag_id="store-1", vector_db_id="vs_1", db_path="/tmp/s1.db"), + RagStore(rag_id="store-2", vector_db_id="vs_2", db_path="/tmp/s2.db"), + ] + config = RagConfiguration( + byok=ByokConfiguration(stores=stores), + retrieval=RetrievalConfiguration( + inline=RetrievalStrategyConfiguration(sources=["store-1", "store-2"]), + ), + ) + assert config.retrieval.inline.sources == ["store-1", "store-2"] + assert config.retrieval.tool.sources == [] def test_inline_with_okp_rag(self) -> None: - """Test inline list including the special OKP ID.""" - config = RagConfiguration(inline=[constants.OKP_RAG_ID, "store-1"]) - assert constants.OKP_RAG_ID in config.inline - assert "store-1" in config.inline + """Test inline sources including the special OKP ID.""" + store = RagStore(rag_id="store-1", vector_db_id="vs_1", db_path="/tmp/s1.db") + config = RagConfiguration( + byok=ByokConfiguration(stores=[store]), + retrieval=RetrievalConfiguration( + inline=RetrievalStrategyConfiguration( + sources=[constants.OKP_RAG_ID, "store-1"] + ), + ), + ) + assert constants.OKP_RAG_ID in config.retrieval.inline.sources + assert "store-1" in config.retrieval.inline.sources def test_tool_with_okp_rag_and_byok(self) -> None: - """Test tool list with OKP and BYOK IDs.""" + """Test tool sources with OKP and BYOK IDs.""" + store = RagStore(rag_id="store-1", vector_db_id="vs_1", db_path="/tmp/s1.db") config = RagConfiguration( - inline=["store-1"], - tool=[constants.OKP_RAG_ID, "store-1"], + byok=ByokConfiguration(stores=[store]), + retrieval=RetrievalConfiguration( + inline=RetrievalStrategyConfiguration(sources=["store-1"]), + tool=RetrievalStrategyConfiguration( + sources=[constants.OKP_RAG_ID, "store-1"] + ), + ), ) - assert config.inline == ["store-1"] - assert config.tool == [constants.OKP_RAG_ID, "store-1"] + assert config.retrieval.inline.sources == ["store-1"] + assert config.retrieval.tool.sources == [constants.OKP_RAG_ID, "store-1"] def test_tool_empty_list(self) -> None: - """Test that an explicit empty tool list disables tool RAG.""" - config = RagConfiguration(tool=[]) - assert config.tool == [] + """Test that an explicit empty tool sources list disables tool RAG.""" + config = RagConfiguration( + retrieval=RetrievalConfiguration( + tool=RetrievalStrategyConfiguration(sources=[]), + ), + ) + assert config.retrieval.tool.sources == [] def test_tool_default_is_empty_list(self) -> None: - """Test that tool defaults to an empty list.""" + """Test that tool sources defaults to an empty list.""" config = RagConfiguration() - assert config.tool == [] + assert config.retrieval.tool.sources == [] + + def test_unknown_inline_source_rejected(self) -> None: + """Test that inline sources referencing undeclared rag_ids are rejected.""" + store = RagStore(rag_id="store-1", vector_db_id="vs_1", db_path="/tmp/s1.db") + with pytest.raises(ValidationError, match="unknown RAG IDs"): + RagConfiguration( + byok=ByokConfiguration(stores=[store]), + retrieval=RetrievalConfiguration( + inline=RetrievalStrategyConfiguration( + sources=["store-1", "nonexistent"] + ), + ), + ) + + def test_unknown_tool_source_rejected(self) -> None: + """Test that tool sources referencing undeclared rag_ids are rejected.""" + with pytest.raises(ValidationError, match="unknown RAG IDs"): + RagConfiguration( + retrieval=RetrievalConfiguration( + tool=RetrievalStrategyConfiguration(sources=["missing-store"]), + ), + ) def test_no_unknown_fields_allowed(self) -> None: """Test that RagConfiguration rejects unknown fields.""" @@ -57,13 +182,28 @@ def test_no_unknown_fields_allowed(self) -> None: def test_fully_custom_config(self) -> None: """Test RagConfiguration with all fields set.""" + store = RagStore( + rag_id="store-1", + vector_db_id="vs_123", + db_path="/tmp/test.db", + ) config = RagConfiguration( - inline=[constants.OKP_RAG_ID, "store-1"], - tool=["store-1"], + byok=ByokConfiguration(stores=[store], max_chunks=15), + okp=OkpConfiguration(offline=False, max_chunks=3), + retrieval=RetrievalConfiguration( + inline=RetrievalStrategyConfiguration( + sources=[constants.OKP_RAG_ID, "store-1"], max_chunks=8 + ), + tool=RetrievalStrategyConfiguration(sources=["store-1"], max_chunks=12), + ), ) - assert constants.OKP_RAG_ID in config.inline - assert "store-1" in config.inline - assert config.tool == ["store-1"] + assert constants.OKP_RAG_ID in config.retrieval.inline.sources + assert "store-1" in config.retrieval.inline.sources + assert config.retrieval.tool.sources == ["store-1"] + assert config.byok.max_chunks == 15 + assert config.okp.max_chunks == 3 + assert config.retrieval.inline.max_chunks == 8 + assert config.retrieval.tool.max_chunks == 12 class TestOkpConfiguration: @@ -74,6 +214,7 @@ def test_default_values(self) -> None: config = OkpConfiguration() assert config.offline is True assert config.chunk_filter_query is None + assert config.max_chunks == constants.DEFAULT_OKP_RAG_MAX_CHUNKS def test_offline_false(self) -> None: """Test offline can be set to False (online mode).""" @@ -85,6 +226,11 @@ def test_custom_chunk_filter_query(self) -> None: config = OkpConfiguration(chunk_filter_query="product:*openshift*") assert config.chunk_filter_query == "product:*openshift*" + def test_custom_max_chunks(self) -> None: + """Test that max_chunks can be customised.""" + config = OkpConfiguration(max_chunks=3) + assert config.max_chunks == 3 + def test_no_unknown_fields_allowed(self) -> None: """Test that OkpConfiguration rejects unknown fields.""" with pytest.raises(ValidationError, match="Extra inputs are not permitted"): diff --git a/tests/unit/telemetry/conftest.py b/tests/unit/telemetry/conftest.py index 6b2db6a82..f17cc5a28 100644 --- a/tests/unit/telemetry/conftest.py +++ b/tests/unit/telemetry/conftest.py @@ -12,6 +12,7 @@ Action, AuthenticationConfiguration, AuthorizationConfiguration, + ByokConfiguration, Configuration, CORSConfiguration, Customization, @@ -23,7 +24,11 @@ JwtRoleRule, LlamaStackConfiguration, ModelContextProtocolServer, + OkpConfiguration, PostgreSQLDatabaseConfiguration, + RagConfiguration, + RetrievalConfiguration, + RetrievalStrategyConfiguration, ServiceConfiguration, SQLiteDatabaseConfiguration, TLSConfiguration, @@ -287,13 +292,33 @@ def build_fully_populated_config() -> Configuration: ), ], conversation_cache=None, - byok_rag=[], + rag=RagConfiguration.model_construct( + byok=ByokConfiguration.model_construct( + max_chunks=10, + stores=[], + ), + okp=OkpConfiguration.model_construct( + rhokp_url=None, + offline=True, + chunk_filter_query=None, + max_chunks=5, + ), + retrieval=RetrievalConfiguration.model_construct( + inline=RetrievalStrategyConfiguration.model_construct( + sources=[], + max_chunks=10, + ), + tool=RetrievalStrategyConfiguration.model_construct( + sources=[], + max_chunks=10, + ), + ), + ), a2a_state=None, quota_handlers=None, azure_entra_id=None, splunk=None, deployment_environment="production", - solr=None, ) @@ -363,13 +388,33 @@ def build_minimal_config() -> Configuration: ), mcp_servers=[], conversation_cache=None, - byok_rag=[], + rag=RagConfiguration.model_construct( + byok=ByokConfiguration.model_construct( + max_chunks=10, + stores=[], + ), + okp=OkpConfiguration.model_construct( + rhokp_url=None, + offline=True, + chunk_filter_query=None, + max_chunks=5, + ), + retrieval=RetrievalConfiguration.model_construct( + inline=RetrievalStrategyConfiguration.model_construct( + sources=[], + max_chunks=10, + ), + tool=RetrievalStrategyConfiguration.model_construct( + sources=[], + max_chunks=10, + ), + ), + ), a2a_state=None, quota_handlers=None, azure_entra_id=None, splunk=None, deployment_environment="development", - solr=None, ) diff --git a/tests/unit/test_configuration.py b/tests/unit/test_configuration.py index fe1d1fc20..89c7191cf 100644 --- a/tests/unit/test_configuration.py +++ b/tests/unit/test_configuration.py @@ -1005,7 +1005,11 @@ def test_rag_id_mapping_includes_solr_when_okp_in_inline() -> None: }, "user_data_collection": {}, "authentication": {"module": "noop"}, - "rag": {"inline": [constants.OKP_RAG_ID]}, + "rag": { + "retrieval": { + "inline": {"sources": [constants.OKP_RAG_ID]}, + }, + }, } ) assert constants.SOLR_DEFAULT_VECTOR_STORE_ID in cfg.rag_id_mapping @@ -1029,7 +1033,11 @@ def test_rag_id_mapping_includes_solr_when_okp_in_tool() -> None: }, "user_data_collection": {}, "authentication": {"module": "noop"}, - "rag": {"tool": [constants.OKP_RAG_ID]}, + "rag": { + "retrieval": { + "tool": {"sources": [constants.OKP_RAG_ID]}, + }, + }, } ) assert constants.SOLR_DEFAULT_VECTOR_STORE_ID in cfg.rag_id_mapping @@ -1055,13 +1063,17 @@ def test_rag_id_mapping_with_byok(tmp_path: Path) -> None: }, "user_data_collection": {}, "authentication": {"module": "noop"}, - "byok_rag": [ - { - "rag_id": "my-kb", - "vector_db_id": "vs-001", - "db_path": str(db_file), + "rag": { + "byok": { + "stores": [ + { + "rag_id": "my-kb", + "vector_db_id": "vs-001", + "db_path": str(db_file), + }, + ], }, - ], + }, } ) assert cfg.rag_id_mapping == {"vs-001": "my-kb"} @@ -1083,14 +1095,20 @@ def test_rag_id_mapping_with_byok_and_okp(tmp_path: Path) -> None: }, "user_data_collection": {}, "authentication": {"module": "noop"}, - "rag": {"inline": [constants.OKP_RAG_ID]}, - "byok_rag": [ - { - "rag_id": "my-kb", - "vector_db_id": "vs-001", - "db_path": str(db_file), + "rag": { + "retrieval": { + "inline": {"sources": [constants.OKP_RAG_ID]}, }, - ], + "byok": { + "stores": [ + { + "rag_id": "my-kb", + "vector_db_id": "vs-001", + "db_path": str(db_file), + }, + ], + }, + }, } ) assert "vs-001" in cfg.rag_id_mapping @@ -1142,13 +1160,17 @@ def test_score_multiplier_mapping_with_byok_defaults(tmp_path: Path) -> None: }, "user_data_collection": {}, "authentication": {"module": "noop"}, - "byok_rag": [ - { - "rag_id": "my-kb", - "vector_db_id": "vs-001", - "db_path": str(db_file), + "rag": { + "byok": { + "stores": [ + { + "rag_id": "my-kb", + "vector_db_id": "vs-001", + "db_path": str(db_file), + }, + ], }, - ], + }, } ) assert cfg.score_multiplier_mapping == {"vs-001": 1.0} @@ -1172,20 +1194,24 @@ def test_score_multiplier_mapping_with_custom_values(tmp_path: Path) -> None: }, "user_data_collection": {}, "authentication": {"module": "noop"}, - "byok_rag": [ - { - "rag_id": "kb1", - "vector_db_id": "vs-001", - "db_path": str(db_file1), - "score_multiplier": 1.5, - }, - { - "rag_id": "kb2", - "vector_db_id": "vs-002", - "db_path": str(db_file2), - "score_multiplier": 0.75, + "rag": { + "byok": { + "stores": [ + { + "rag_id": "kb1", + "vector_db_id": "vs-001", + "db_path": str(db_file1), + "score_multiplier": 1.5, + }, + { + "rag_id": "kb2", + "vector_db_id": "vs-002", + "db_path": str(db_file2), + "score_multiplier": 0.75, + }, + ], }, - ], + }, } ) assert cfg.score_multiplier_mapping == {"vs-001": 1.5, "vs-002": 0.75} @@ -1332,17 +1358,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "ca_cert_path": "file", }, }, - "byok_rag": [ - { - "rag_id": "Weight message strong wind land bar.", - "rag_type": "Learn person tell increase dog even.", - "embedding_model": "By our television. Southern full a course.", - "embedding_dimension": 753, - "vector_db_id": "Indicate see door specific hard region one.", - "db_path": "A none owner visit wish medical cut Mrs. Later nig", - "score_multiplier": 388.45, - } - ], "a2a_state": {"sqlite": None, "postgres": None}, "quota_handlers": { "sqlite": {"db_path": "Experience five able citizen work member call cond"}, @@ -1401,21 +1416,40 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "Second say body know music while.", "rag": { - "inline": [ - "Local authority pressure pretty. Travel something ", - "Watch meet able such.", - "Different apply size.", - ], - "tool": [ - "Full develop under his.", - "Black political father project become.", - "Once however son place.", - ], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": "Foreign space system.", + "byok": { + "stores": [ + { + "rag_id": "Weight message strong wind land bar.", + "backend": "Learn person tell increase dog even.", + "embedding_model": "By our television. Southern full a course.", + "embedding_dimension": 753, + "vector_db_id": "Indicate see door specific hard region one.", + "db_path": "A none owner visit wish medical cut Mrs. Later nig", + "score_multiplier": 388.45, + } + ], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": "Foreign space system.", + }, + "retrieval": { + "inline": { + "sources": [ + "Local authority pressure pretty. Travel something ", + "Watch meet able such.", + "Different apply size.", + ], + }, + "tool": { + "sources": [ + "Full develop under his.", + "Black political father project become.", + "Once however son place.", + ], + }, + }, }, }, { @@ -1608,26 +1642,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "ca_cert_path": "certs", }, }, - "byok_rag": [ - { - "rag_id": "Tonight relate there record.", - "rag_type": "Politics development real play main chair capital ", - "embedding_model": "Prepare memory outside.", - "embedding_dimension": 449, - "vector_db_id": "Political right gun law public group rock.", - "db_path": "Consider still recognize church. Area suggest noth", - "score_multiplier": 183.85, - }, - { - "rag_id": "One again under respond poor beyond.", - "rag_type": "Six base physical.", - "embedding_model": "Surface that choice.", - "embedding_dimension": 736, - "vector_db_id": "Forget level other agreement.", - "db_path": "Argue pull out race town.", - "score_multiplier": 225.21, - }, - ], "a2a_state": {"sqlite": None, "postgres": None}, "quota_handlers": { "sqlite": None, @@ -1690,14 +1704,48 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "Vote mean answer simply turn project.", "rag": { - "inline": [ - "Billion job provide take other.", - "Eight total figure surface development include out", - "Which from cover not choice bring sister front.", - ], - "tool": ["Ground appear group institution."], + "byok": { + "stores": [ + { + "rag_id": "Tonight relate there record.", + "backend": "Politics development real play main chair capital ", + "embedding_model": "Prepare memory outside.", + "embedding_dimension": 449, + "vector_db_id": "Political right gun law public group rock.", + "db_path": "Consider still recognize church. Area suggest noth", + "score_multiplier": 183.85, + }, + { + "rag_id": "One again under respond poor beyond.", + "backend": "Six base physical.", + "embedding_model": "Surface that choice.", + "embedding_dimension": 736, + "vector_db_id": "Forget level other agreement.", + "db_path": "Argue pull out race town.", + "score_multiplier": 225.21, + }, + ], + }, + "okp": { + "rhokp_url": None, + "offline": False, + "chunk_filter_query": None, + }, + "retrieval": { + "inline": { + "sources": [ + "Billion job provide take other.", + "Eight total figure surface development include out", + "Which from cover not choice bring sister front.", + ], + }, + "tool": { + "sources": [ + "Ground appear group institution.", + ], + }, + }, }, - "okp": {"rhokp_url": None, "offline": False, "chunk_filter_query": None}, }, { "name": "Patricia Henderson", @@ -1805,17 +1853,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "sqlite": None, "postgres": None, }, - "byok_rag": [ - { - "rag_id": "Something worker campaign war through.", - "rag_type": "Check simple since next then statement.", - "embedding_model": "Class third author series.", - "embedding_dimension": 211, - "vector_db_id": "Less put site alone amount.", - "db_path": "Live child most throughout.", - "score_multiplier": 252.41, - } - ], "a2a_state": {"sqlite": None, "postgres": None}, "quota_handlers": { "sqlite": None, @@ -1868,17 +1905,39 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "Mouth view form.", "rag": { - "inline": [ - "Interesting during product himself attack Democrat", - "Decision I order particularly.", - "Couple reflect relate two agree local.", - ], - "tool": ["Her society move lay.", "Network material like."], - }, - "okp": { - "rhokp_url": "xyzzy", - "offline": False, - "chunk_filter_query": "Beautiful society within.", + "byok": { + "stores": [ + { + "rag_id": "Something worker campaign war through.", + "backend": "Check simple since next then statement.", + "embedding_model": "Class third author series.", + "embedding_dimension": 211, + "vector_db_id": "Less put site alone amount.", + "db_path": "Live child most throughout.", + "score_multiplier": 252.41, + } + ], + }, + "okp": { + "rhokp_url": "xyzzy", + "offline": False, + "chunk_filter_query": "Beautiful society within.", + }, + "retrieval": { + "inline": { + "sources": [ + "Interesting during product himself attack Democrat", + "Decision I order particularly.", + "Couple reflect relate two agree local.", + ], + }, + "tool": { + "sources": [ + "Her society move lay.", + "Network material like.", + ], + }, + }, }, }, { @@ -2006,35 +2065,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "ca_cert_path": None, }, }, - "byok_rag": [ - { - "rag_id": "Ever analysis three perhaps.", - "rag_type": "Ever truth skin.", - "embedding_model": "Type toward never hair relate before.", - "embedding_dimension": 619, - "vector_db_id": "Learn computer positive nor yet notice.", - "db_path": "Sort rule soldier relationship. Wife front kid cit", - "score_multiplier": 319.63, - }, - { - "rag_id": "Question to front often.", - "rag_type": "But catch hear happy.", - "embedding_model": "Hard message wait least focus left daughter reflec", - "embedding_dimension": 97, - "vector_db_id": "Create visit green. Throw more tend throw game.", - "db_path": "Rest could recent test door.", - "score_multiplier": 224.06, - }, - { - "rag_id": "Read hand over fight president feel letter. Over h", - "rag_type": "Set visit describe seat space play.", - "embedding_model": "Lawyer early term direction.", - "embedding_dimension": 119, - "vector_db_id": "Day store girl writer have would participant.", - "db_path": "Later research explain first lose probably.", - "score_multiplier": 627.97, - }, - ], "a2a_state": { "sqlite": {"db_path": "Write herself each generation finally attorney."}, "postgres": None, @@ -2093,16 +2123,55 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "Want hair product.", "rag": { - "inline": [ - "Himself fear read here finally ask teacher.", - "Enjoy standard off.", - ], - "tool": ["Them author financial production."], - }, - "okp": { - "rhokp_url": "xyzzy", - "offline": False, - "chunk_filter_query": "Industry as appear us. Lead dream public compare.", + "byok": { + "stores": [ + { + "rag_id": "Ever analysis three perhaps.", + "backend": "Ever truth skin.", + "embedding_model": "Type toward never hair relate before.", + "embedding_dimension": 619, + "vector_db_id": "Learn computer positive nor yet notice.", + "db_path": "Sort rule soldier relationship. Wife front kid cit", + "score_multiplier": 310.63, + }, + { + "rag_id": "Question to front often.", + "backend": "But catch hear happy.", + "embedding_model": "Hard message wait least focus left daughter reflec", + "embedding_dimension": 97, + "vector_db_id": "Create visit green. Throw more tend throw game.", + "db_path": "Rest could recent test door.", + "score_multiplier": 224.06, + }, + { + "rag_id": "Read hand over fight president feel letter. Over h", + "backend": "Set visit describe seat space play.", + "embedding_model": "Lawyer early term direction.", + "embedding_dimension": 119, + "vector_db_id": "Day store girl writer have would participant.", + "db_path": "Later research explain first lose probably.", + "score_multiplier": 627.97, + }, + ], + }, + "okp": { + "rhokp_url": "xyzzy", + "offline": False, + "chunk_filter_query": "Industry as appear us. Lead dream public compare.", + }, + "retrieval": { + "inline": { + "sources": [ + "Himself fear read here finally ask teacher.", + "Enjoy standard off.", + ], + }, + "tool": { + "sources": [ + "Them author financial production.", + ], + }, + }, }, }, { @@ -2217,26 +2286,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "sqlite": {"db_path": "Court size your eye choose."}, "postgres": None, }, - "byok_rag": [ - { - "rag_id": "Authority kind apply arm manager local reveal.", - "rag_type": "Seem authority miss.", - "embedding_model": "Have news quality.", - "embedding_dimension": 310, - "vector_db_id": "Education hot full her. Serve mention save executi", - "db_path": "Every popular bit.", - "score_multiplier": 918.43, - }, - { - "rag_id": "Avoid baby miss want education.", - "rag_type": "Sing answer rule soon.", - "embedding_model": "Year let example you paper develop tough.", - "embedding_dimension": 985, - "vector_db_id": "Operation conference phone.", - "db_path": "All effort True see.", - "score_multiplier": 788.57, - }, - ], "a2a_state": { "sqlite": {"db_path": "Green example walk become return front."}, "postgres": { @@ -2288,21 +2337,49 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "Consumer center sign skin total.", "rag": { - "inline": [ - "True four lawyer sound. Light fund former art.", - "Perhaps theory remain. Marriage person put food.", - "Run behind single material else media.", - ], - "tool": [ - "Another Congress part seat bit.", - "Able main door under. Early consumer speech less c", - "Eat read shake three. Development cell mission.", - ], - }, - "okp": { - "rhokp_url": None, - "offline": True, - "chunk_filter_query": "And drug brother tell specific realize hit.", + "byok": { + "stores": [ + { + "rag_id": "Authority kind apply arm manager local reveal.", + "backend": "Seem authority miss.", + "embedding_model": "Have news quality.", + "embedding_dimension": 310, + "vector_db_id": "Education hot full her. Serve mention save executi", + "db_path": "Every popular bit.", + "score_multiplier": 918.43, + }, + { + "rag_id": "Avoid baby miss want education.", + "backend": "Sing answer rule soon.", + "embedding_model": "Year let example you paper develop tough.", + "embedding_dimension": 985, + "vector_db_id": "Operation conference phone.", + "db_path": "All effort True see.", + "score_multiplier": 788.57, + }, + ], + }, + "okp": { + "rhokp_url": None, + "offline": True, + "chunk_filter_query": "And drug brother tell specific realize hit.", + }, + "retrieval": { + "inline": { + "sources": [ + "True four lawyer sound. Light fund former art.", + "Perhaps theory remain. Marriage person put food.", + "Run behind single material else media.", + ], + }, + "tool": { + "sources": [ + "Another Congress part seat bit.", + "Able main door under. Early consumer speech less c", + "Eat read shake three. Development cell mission.", + ], + }, + }, }, }, { @@ -2474,35 +2551,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "sqlite": None, "postgres": None, }, - "byok_rag": [ - { - "rag_id": "Nor reduce physical section serious. She still rep", - "rag_type": "Hospital political recognize operation tree.", - "embedding_model": "Drug concern old job discover firm imagine.", - "embedding_dimension": 192, - "vector_db_id": "Relationship training argue body market old per.", - "db_path": "Consumer while positive. Why because quite respons", - "score_multiplier": 283.58, - }, - { - "rag_id": "Past detail as star. Teacher spend sit push maybe ", - "rag_type": "After good nature. War option science approach.", - "embedding_model": "Air serve court measure most play item.", - "embedding_dimension": 491, - "vector_db_id": "Other open wonder.", - "db_path": "Car everybody during. Nor believe audience tax soo", - "score_multiplier": 159.31, - }, - { - "rag_id": "Fire feeling person real party game method.", - "rag_type": "Middle together second money need fly.", - "embedding_model": "Do item when politics.", - "embedding_dimension": 896, - "vector_db_id": "Reason decision region past research.", - "db_path": "Every any nice vote civil.", - "score_multiplier": 776.23, - }, - ], "a2a_state": { "sqlite": None, "postgres": { @@ -2561,13 +2609,56 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "Successful cut arrive ever against maybe.", "rag": { - "inline": [ - "Themselves scene just.", - "Sport develop particular when. Task agreement walk", - ], - "tool": ["Anything visit late."], + "byok": { + "stores": [ + { + "rag_id": "Nor reduce physical section serious. She still rep", + "backend": "Hospital political recognize operation tree.", + "embedding_model": "Drug concern old job discover firm imagine.", + "embedding_dimension": 192, + "vector_db_id": "Relationship training argue body market old per.", + "db_path": "Consumer while positive. Why because quite respons", + "score_multiplier": 283.58, + }, + { + "rag_id": "Past detail as star. Teacher spend sit push maybe ", + "backend": "After good nature. War option science approach.", + "embedding_model": "Air serve court measure most play item.", + "embedding_dimension": 491, + "vector_db_id": "Other open wonder.", + "db_path": "Car everybody during. Nor believe audience tax soo", + "score_multiplier": 159.31, + }, + { + "rag_id": "Fire feeling person real party game method.", + "backend": "Middle together second money need fly.", + "embedding_model": "Do item when politics.", + "embedding_dimension": 896, + "vector_db_id": "Reason decision region past research.", + "db_path": "Every any nice vote civil.", + "score_multiplier": 776.23, + }, + ], + }, + "okp": { + "rhokp_url": "xyzzy", + "offline": True, + "chunk_filter_query": None, + }, + "retrieval": { + "inline": { + "sources": [ + "Themselves scene just.", + "Sport develop particular when. Task agreement walk", + ], + }, + "tool": { + "sources": [ + "Anything visit late.", + ], + }, + }, }, - "okp": {"rhokp_url": "xyzzy", "offline": True, "chunk_filter_query": None}, }, { "name": "Mr. Michael Wilson", @@ -2703,35 +2794,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "sqlite": None, "postgres": None, }, - "byok_rag": [ - { - "rag_id": "Sometimes once win young bar right. Star keep cult", - "rag_type": "Produce energy skill art.", - "embedding_model": "Beautiful series message.", - "embedding_dimension": 739, - "vector_db_id": "Visit night city.", - "db_path": "Paper investment game.", - "score_multiplier": 962.12, - }, - { - "rag_id": "Standard might new national produce thank bill.", - "rag_type": "Bar else center dinner great. Wrong ability big.", - "embedding_model": "Building try left general.", - "embedding_dimension": 973, - "vector_db_id": "Issue never physical stuff edge fire research.", - "db_path": "Help hope our would discussion. Than plan task.", - "score_multiplier": 732.93, - }, - { - "rag_id": "Air culture explain child.", - "rag_type": "Reach must moment.", - "embedding_model": "Manage anyone police someone church.", - "embedding_dimension": 691, - "vector_db_id": "Far tough individual painting send minute.", - "db_path": "Head major down soon.", - "score_multiplier": 485.53, - }, - ], "a2a_state": { "sqlite": None, "postgres": { @@ -2792,14 +2854,57 @@ def test_score_multiplier_mapping_not_loaded() -> None: "splunk": None, "deployment_environment": "Must no land member.", "rag": { - "inline": [ - "Image police section carry. Order walk state commu", - "Society be night participant seat.", - "Minute skin again.", - ], - "tool": ["Use hotel often deal light teacher. Improve more m"], + "byok": { + "stores": [ + { + "rag_id": "Sometimes once win young bar right. Star keep cult", + "backend": "Produce energy skill art.", + "embedding_model": "Beautiful series message.", + "embedding_dimension": 739, + "vector_db_id": "Visit night city.", + "db_path": "Paper investment game.", + "score_multiplier": 962.12, + }, + { + "rag_id": "Standard might new national produce thank bill.", + "backend": "Bar else center dinner great. Wrong ability big.", + "embedding_model": "Building try left general.", + "embedding_dimension": 973, + "vector_db_id": "Issue never physical stuff edge fire research.", + "db_path": "Help hope our would discussion. Than plan task.", + "score_multiplier": 732.93, + }, + { + "rag_id": "Air culture explain child.", + "backend": "Reach must moment.", + "embedding_model": "Manage anyone police someone church.", + "embedding_dimension": 691, + "vector_db_id": "Far tough individual painting send minute.", + "db_path": "Head major down soon.", + "score_multiplier": 485.53, + }, + ], + }, + "okp": { + "rhokp_url": None, + "offline": False, + "chunk_filter_query": None, + }, + "retrieval": { + "inline": { + "sources": [ + "Image police section carry. Order walk state commu", + "Society be night participant seat.", + "Minute skin again.", + ], + }, + "tool": { + "sources": [ + "Use hotel often deal light teacher. Improve more m", + ], + }, + }, }, - "okp": {"rhokp_url": None, "offline": False, "chunk_filter_query": None}, }, { "name": "Ruth Davidson", @@ -2919,35 +3024,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "ca_cert_path": None, }, }, - "byok_rag": [ - { - "rag_id": "Raise real rather walk product against.", - "rag_type": "Whose mind serve public character letter.", - "embedding_model": "Miss act loss camera.", - "embedding_dimension": 276, - "vector_db_id": "Return generation beat.", - "db_path": "Discover professional really group.", - "score_multiplier": 546.8, - }, - { - "rag_id": "Those sit there reason.", - "rag_type": "Keep third nothing throw.", - "embedding_model": "Like movie lead since traditional for daughter. Re", - "embedding_dimension": 148, - "vector_db_id": "Sure statement only authority.", - "db_path": "Top social suggest she yourself heavy. Use low bud", - "score_multiplier": 623.44, - }, - { - "rag_id": "Ability who manager several.", - "rag_type": "About ago spend poor event.", - "embedding_model": "Be energy lead.", - "embedding_dimension": 14, - "vector_db_id": "Region behind law affect note.", - "db_path": "View within able over sit. Part eat among appear.", - "score_multiplier": 306.05, - }, - ], "a2a_state": { "sqlite": {"db_path": "Air pretty Democrat husband make travel statement."}, "postgres": { @@ -2993,17 +3069,56 @@ def test_score_multiplier_mapping_not_loaded() -> None: "splunk": None, "deployment_environment": "Second window action enter until very low provide.", "rag": { - "inline": [ - "Consider once budget author trade federal.", - "Knowledge the option positive. Court its effect me", - "Add these care drive want and.", - ], - "tool": ["Guess know picture."], - }, - "okp": { - "rhokp_url": "xyzzy", - "offline": False, - "chunk_filter_query": "Much when find smile try.", + "byok": { + "stores": [ + { + "rag_id": "Raise real rather walk product against.", + "backend": "Whose mind serve public character letter.", + "embedding_model": "Miss act loss camera.", + "embedding_dimension": 276, + "vector_db_id": "Return generation beat.", + "db_path": "Discover professional really group.", + "score_multiplier": 546.8, + }, + { + "rag_id": "Those sit there reason.", + "backend": "Keep third nothing throw.", + "embedding_model": "Like movie lead since traditional for daughter. Re", + "embedding_dimension": 148, + "vector_db_id": "Sure statement only authority.", + "db_path": "Top social suggest she yourself heavy. Use low bud", + "score_multiplier": 623.44, + }, + { + "rag_id": "Ability who manager several.", + "backend": "About ago spend poor event.", + "embedding_model": "Be energy lead.", + "embedding_dimension": 14, + "vector_db_id": "Region behind law affect note.", + "db_path": "View within able over sit. Part eat among appear.", + "score_multiplier": 306.05, + }, + ], + }, + "okp": { + "rhokp_url": "xyzzy", + "offline": False, + "chunk_filter_query": "Much when find smile try.", + }, + "retrieval": { + "inline": { + "sources": [ + "Consider once budget author trade federal.", + "Knowledge the option positive. Court its effect me", + "Add these care drive want and.", + ], + }, + "tool": { + "sources": [ + "Guess know picture.", + ], + }, + }, }, }, { @@ -3121,35 +3236,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "sqlite": {"db_path": "Suggest gun standard fast note stay their."}, "postgres": None, }, - "byok_rag": [ - { - "rag_id": "Hope enough nature. Forward season agreement espec", - "rag_type": "Everyone finish task worry little we.", - "embedding_model": "Third choice enter blue baby behind its.", - "embedding_dimension": 514, - "vector_db_id": "Board how fight.", - "db_path": "Black can heavy write home.", - "score_multiplier": 817.0, - }, - { - "rag_id": "Fish medical really owner different carry.", - "rag_type": "Order window meeting feel.", - "embedding_model": "Occur international consumer.", - "embedding_dimension": 912, - "vector_db_id": "Full tell us century development network scene spe", - "db_path": "Today boy kind key center Mr. Contain reduce coach", - "score_multiplier": 233.12, - }, - { - "rag_id": "Note dog the audience work. We though name.", - "rag_type": "Bad career deep affect.", - "embedding_model": "Budget much see ask.", - "embedding_dimension": 939, - "vector_db_id": "South positive might film control peace seem.", - "db_path": "Go for can player camera.", - "score_multiplier": 268.06, - }, - ], "a2a_state": {"sqlite": None, "postgres": None}, "quota_handlers": { "sqlite": {"db_path": "Suffer best free prove quickly to degree."}, @@ -3197,17 +3283,58 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "Maybe really go court.", "rag": { - "inline": [ - "Without rock staff have campaign.", - "Particular her six.", - "These where I product.", - ], - "tool": [ - "Kind ability hope way.", - "Mean hot pressure onto purpose however.", - ], + "byok": { + "stores": [ + { + "rag_id": "Hope enough nature. Forward season agreement espec", + "backend": "Everyone finish task worry little we.", + "embedding_model": "Third choice enter blue baby behind its.", + "embedding_dimension": 514, + "vector_db_id": "Board how fight.", + "db_path": "Black can heavy write home.", + "score_multiplier": 817.0, + }, + { + "rag_id": "Fish medical really owner different carry.", + "backend": "Order window meeting feel.", + "embedding_model": "Occur international consumer.", + "embedding_dimension": 912, + "vector_db_id": "Full tell us century development network scene spe", + "db_path": "Today boy kind key center Mr. Contain reduce coach", + "score_multiplier": 233.12, + }, + { + "rag_id": "Note dog the audience work. We though name.", + "backend": "Bad career deep affect.", + "embedding_model": "Budget much see ask.", + "embedding_dimension": 939, + "vector_db_id": "South positive might film control peace seem.", + "db_path": "Go for can player camera.", + "score_multiplier": 268.06, + }, + ], + }, + "okp": { + "rhokp_url": "xyzzy", + "offline": True, + "chunk_filter_query": None, + }, + "retrieval": { + "inline": { + "sources": [ + "Without rock staff have campaign.", + "Particular her six.", + "These where I product.", + ], + }, + "tool": { + "sources": [ + "Kind ability hope way.", + "Mean hot pressure onto purpose however.", + ], + }, + }, }, - "okp": {"rhokp_url": "xyzzy", "offline": True, "chunk_filter_query": None}, }, { "name": "William Riley", @@ -3323,26 +3450,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "sqlite": None, "postgres": None, }, - "byok_rag": [ - { - "rag_id": "Charge herself where impact say billion.", - "rag_type": "Blood thus member soldier.", - "embedding_model": "Sound hotel save.", - "embedding_dimension": 922, - "vector_db_id": "Down simple suffer civil. Modern service scene pas", - "db_path": "Ten fall fine firm.", - "score_multiplier": 671.28, - }, - { - "rag_id": "Include space evidence benefit loss skin.", - "rag_type": "Green anyone be.", - "embedding_model": "Focus clearly physical six.", - "embedding_dimension": 237, - "vector_db_id": "Company put eight.", - "db_path": "Step at let oil leave agreement this.", - "score_multiplier": 368.33, - }, - ], "a2a_state": { "sqlite": None, "postgres": { @@ -3413,14 +3520,48 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "Wonder though writer allow instead.", "rag": { - "inline": [ - "Onto political artist.", - "Trip writer half. Amount south give parent.", - "We thought American exist. Nearly cell case partic", - ], - "tool": ["School of book next man short responsibility able."], + "byok": { + "stores": [ + { + "rag_id": "Charge herself where impact say billion.", + "backend": "Blood thus member soldier.", + "embedding_model": "Sound hotel save.", + "embedding_dimension": 922, + "vector_db_id": "Down simple suffer civil. Modern service scene pas", + "db_path": "Ten fall fine firm.", + "score_multiplier": 671.28, + }, + { + "rag_id": "Include space evidence benefit loss skin.", + "backend": "Green anyone be.", + "embedding_model": "Focus clearly physical six.", + "embedding_dimension": 237, + "vector_db_id": "Company put eight.", + "db_path": "Step at let oil leave agreement this.", + "score_multiplier": 368.33, + }, + ], + }, + "okp": { + "rhokp_url": "xyzzy", + "offline": True, + "chunk_filter_query": None, + }, + "retrieval": { + "inline": { + "sources": [ + "Onto political artist.", + "Trip writer half. Amount south give parent.", + "We thought American exist. Nearly cell case partic", + ], + }, + "tool": { + "sources": [ + "School of book next man short responsibility able.", + ], + }, + }, }, - "okp": {"rhokp_url": "xyzzy", "offline": True, "chunk_filter_query": None}, }, { "name": "Rodney Scott", @@ -3556,26 +3697,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "ca_cert_path": "xyzzy", }, }, - "byok_rag": [ - { - "rag_id": "Stop choice sing prepare our both traditional.", - "rag_type": "Four account action. Herself measure speech full t", - "embedding_model": "Positive now since middle.", - "embedding_dimension": 799, - "vector_db_id": "Movie word mouth major identify law manage they.", - "db_path": "Finally hot investment role attorney meet husband.", - "score_multiplier": 515.98, - }, - { - "rag_id": "Throw two action station store respond among.", - "rag_type": "Accept exist also happy.", - "embedding_model": "Box structure arrive. Front suffer civil fund invo", - "embedding_dimension": 443, - "vector_db_id": "Begin born decade instead.", - "db_path": "Interest easy remember here fast win. Despite budg", - "score_multiplier": 2.92, - }, - ], "a2a_state": { "sqlite": {"db_path": "Trouble stop speech traditional."}, "postgres": { @@ -3629,19 +3750,47 @@ def test_score_multiplier_mapping_not_loaded() -> None: }, "deployment_environment": "West local subject clearly. Push question in.", "rag": { - "inline": [ - "Garden up certain success student others may.", - "Face can produce.", - ], - "tool": [ - "Though appear collection night message high.", - "Knowledge cup fact.", - ], - }, - "okp": { - "rhokp_url": "xyzzy", - "offline": False, - "chunk_filter_query": "Maybe assume region thus.", + "byok": { + "stores": [ + { + "rag_id": "Stop choice sing prepare our both traditional.", + "backend": "Four account action. Herself measure speech full t", + "embedding_model": "Positive now since middle.", + "embedding_dimension": 799, + "vector_db_id": "Movie word mouth major identify law manage they.", + "db_path": "Finally hot investment role attorney meet husband.", + "score_multiplier": 515.98, + }, + { + "rag_id": "Throw two action station store respond among.", + "backend": "Accept exist also happy.", + "embedding_model": "Box structure arrive. Front suffer civil fund invo", + "embedding_dimension": 443, + "vector_db_id": "Begin born decade instead.", + "db_path": "Interest easy remember here fast win. Despite budg", + "score_multiplier": 2.92, + }, + ], + }, + "okp": { + "rhokp_url": "xyzzy", + "offline": False, + "chunk_filter_query": "Maybe assume region thus.", + }, + "retrieval": { + "inline": { + "sources": [ + "Garden up certain success student others may.", + "Face can produce.", + ], + }, + "tool": { + "sources": [ + "Though appear collection night message high.", + "Knowledge cup fact.", + ], + }, + }, }, }, { @@ -3796,26 +3945,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "buffer_max_ratio": 743.59, }, "approvals": {"approval_timeout_seconds": 898, "approval_retention_days": 414}, - "byok_rag": [ - { - "rag_id": "Moment program career provide discuss suddenly.", - "rag_type": "Would total admit out behind country.", - "embedding_model": "Over decide simple girl so animal never near.", - "embedding_dimension": 556, - "vector_db_id": "Ground cut current civil better.", - "db_path": "Local major deep go necessary.", - "score_multiplier": 405.6, - }, - { - "rag_id": "The charge there break call information.", - "rag_type": "Money for but give but amount. Buy community your ", - "embedding_model": "President performance activity doctor.", - "embedding_dimension": 571, - "vector_db_id": "Station past election mouth.", - "db_path": "But song owner use. Special deal against crime pus", - "score_multiplier": 481.85, - }, - ], "a2a_state": { "sqlite": {"db_path": "Theory that enough party child."}, "postgres": { @@ -3870,7 +3999,6 @@ def test_score_multiplier_mapping_not_loaded() -> None: "tool": ["North prepare recognize.", "Cold including arm tough pull."], }, "okp": {"rhokp_url": None, "offline": True, "chunk_filter_query": None}, - "reranker": {"enabled": True, "model": "Team serious benefit traditional."}, "skills": { "paths": [ "/", diff --git a/tests/unit/test_llama_stack_configuration.py b/tests/unit/test_llama_stack_configuration.py index 9b23e8baa..e7bafaf85 100644 --- a/tests/unit/test_llama_stack_configuration.py +++ b/tests/unit/test_llama_stack_configuration.py @@ -263,7 +263,7 @@ def test_construct_vector_io_providers_section_adds_new() -> None: { "rag_id": "rag1", "vector_db_id": "store1", - "rag_type": "inline::faiss", + "backend": "faiss", }, ] output = construct_vector_io_providers_section(ls_config, byok_rag) @@ -280,7 +280,7 @@ def test_construct_vector_io_providers_section_idempotent_reenrichment() -> None { "rag_id": "rag1", "vector_db_id": "store1", - "rag_type": "inline::faiss", + "backend": "faiss", }, ] ls_config: dict[str, Any] = {"providers": {}} @@ -310,7 +310,7 @@ def test_construct_vector_io_providers_section_collapses_existing_duplicates() - { "rag_id": "rag1", "vector_db_id": "store1", - "rag_type": "inline::faiss", + "backend": "faiss", }, ] output = construct_vector_io_providers_section(ls_config, byok_rag) @@ -325,7 +325,7 @@ def test_construct_vector_io_providers_section_pgvector() -> None: { "rag_id": "pg1", "vector_db_id": "vs_pg", - "rag_type": "remote::pgvector", + "backend": "pgvector", "host": "${env.POSTGRES_HOST}", "port": "${env.POSTGRES_PORT}", "db": "${env.POSTGRES_DATABASE}", @@ -348,11 +348,11 @@ def test_construct_vector_io_providers_section_mixed() -> None: """Test mixed faiss and pgvector entries generate correct configs.""" ls_config: dict[str, Any] = {"providers": {}} byok_rag = [ - {"rag_id": "f1", "vector_db_id": "vs_f", "rag_type": "inline::faiss"}, + {"rag_id": "f1", "vector_db_id": "vs_f", "backend": "faiss"}, { "rag_id": "pg1", "vector_db_id": "vs_pg", - "rag_type": "remote::pgvector", + "backend": "pgvector", "host": "localhost", "port": "5432", "db": "mydb", @@ -374,9 +374,7 @@ def test_construct_vector_io_providers_section_mixed() -> None: def test_construct_storage_backends_section_skips_pgvector() -> None: """Test pgvector entries are skipped (they use kv_default).""" ls_config: dict[str, Any] = {} - byok_rag = [ - {"rag_id": "pg1", "vector_db_id": "vs_pg", "rag_type": "remote::pgvector"} - ] + byok_rag = [{"rag_id": "pg1", "vector_db_id": "vs_pg", "backend": "pgvector"}] output = construct_storage_backends_section(ls_config, byok_rag) assert len(output) == 0 @@ -386,7 +384,7 @@ def test_construct_storage_backends_section_mixed_faiss_pgvector() -> None: ls_config: dict[str, Any] = {} byok_rag = [ {"rag_id": "f1", "vector_db_id": "vs_f", "db_path": "/tmp/f.db"}, - {"rag_id": "pg1", "vector_db_id": "vs_pg", "rag_type": "remote::pgvector"}, + {"rag_id": "pg1", "vector_db_id": "vs_pg", "backend": "pgvector"}, ] output = construct_storage_backends_section(ls_config, byok_rag) assert len(output) == 1 @@ -401,7 +399,7 @@ def test_enrich_byok_rag_pgvector_end_to_end() -> None: { "rag_id": "pg1", "vector_db_id": "vs_pg", - "rag_type": "remote::pgvector", + "backend": "pgvector", "embedding_model": "sentence-transformers/all-mpnet-base-v2", "embedding_dimension": 768, "host": "${env.POSTGRES_HOST}", @@ -644,7 +642,7 @@ def test_generate_configuration_dedupes_vector_io_on_load(tmp_path: Path) -> Non def test_generate_configuration_with_dict(tmp_path: Path) -> None: """Test generate_configuration accepts dict.""" - config: dict[str, Any] = {"byok_rag": []} + config: dict[str, Any] = {"rag": {"byok": {"stores": []}}} outfile = tmp_path / "output.yaml" generate_configuration("tests/configuration/run.yaml", str(outfile), config) @@ -680,16 +678,20 @@ def test_generate_configuration_with_pydantic_model(tmp_path: Path) -> None: def test_generate_configuration_with_byok(tmp_path: Path) -> None: """Test generate_configuration adds BYOK entries.""" config = { - "byok_rag": [ - { - "rag_id": "rag1", - "vector_db_id": "store1", - "embedding_model": "test-model", - "embedding_dimension": 256, - "rag_type": "inline::faiss", - "db_path": "/tmp/store1.db", + "rag": { + "byok": { + "stores": [ + { + "rag_id": "rag1", + "vector_db_id": "store1", + "embedding_model": "test-model", + "embedding_dimension": 256, + "backend": "faiss", + "db_path": "/tmp/store1.db", + }, + ], }, - ], + }, } outfile = tmp_path / "output.yaml" @@ -719,20 +721,24 @@ def test_generate_configuration_with_byok(tmp_path: Path) -> None: def test_generate_configuration_with_pgvector(tmp_path: Path) -> None: """Test generate_configuration adds pgvector BYOK entries.""" config = { - "byok_rag": [ - { - "rag_id": "pg1", - "vector_db_id": "vs_pg", - "embedding_model": "sentence-transformers/all-mpnet-base-v2", - "embedding_dimension": 768, - "rag_type": "remote::pgvector", - "host": "localhost", - "port": "5432", - "db": "knowledge_db", - "user": "admin", - "password": "secret", + "rag": { + "byok": { + "stores": [ + { + "rag_id": "pg1", + "vector_db_id": "vs_pg", + "embedding_model": "sentence-transformers/all-mpnet-base-v2", + "embedding_dimension": 768, + "backend": "pgvector", + "host": "localhost", + "port": "5432", + "db": "knowledge_db", + "user": "admin", + "password": "secret", + }, + ], }, - ], + }, } outfile = tmp_path / "output.yaml" generate_configuration("tests/configuration/run.yaml", str(outfile), config) diff --git a/tests/unit/test_llama_stack_synthesize.py b/tests/unit/test_llama_stack_synthesize.py index ad1ffbc3c..ecf67948e 100644 --- a/tests/unit/test_llama_stack_synthesize.py +++ b/tests/unit/test_llama_stack_synthesize.py @@ -404,14 +404,18 @@ def test_synthesize_enriches_byok_rag_like_legacy() -> None: """BYOK RAG enrichment runs during synthesis for legacy parity (R7).""" lcs = { "llama_stack": {"config": {"baseline": "empty"}}, - "byok_rag": [ - { - "rag_id": "kb1", - "vector_db_id": "kb1", - "embedding_model": "nomic-ai/nomic-embed-text-v1.5", - "embedding_dimension": 768, - } - ], + "rag": { + "byok": { + "stores": [ + { + "rag_id": "kb1", + "vector_db_id": "kb1", + "embedding_model": "nomic-ai/nomic-embed-text-v1.5", + "embedding_dimension": 768, + } + ], + }, + }, } result = synthesize_configuration(lcs) # enrichment created the storage backends + vector_io provider section diff --git a/tests/unit/utils/test_config_dumper.py b/tests/unit/utils/test_config_dumper.py index 0c30e133e..1772da86b 100644 --- a/tests/unit/utils/test_config_dumper.py +++ b/tests/unit/utils/test_config_dumper.py @@ -77,7 +77,10 @@ def test_dump_schema(tmpdir: Path) -> None: "AuthenticationConfiguration", "AuthorizationConfiguration", "AzureEntraIdConfiguration", - "ByokRag", + "ByokConfiguration", + "RagStore", + "RetrievalConfiguration", + "RetrievalStrategyConfiguration", "CORSConfiguration", "CompactionConfiguration", "Configuration", diff --git a/tests/unit/utils/test_responses.py b/tests/unit/utils/test_responses.py index eb9c4b1b1..34a54e1fa 100644 --- a/tests/unit/utils/test_responses.py +++ b/tests/unit/utils/test_responses.py @@ -58,9 +58,9 @@ from models.common.responses.types import InputTool, InputToolMCP from models.config import ( ApprovalFilter, - ByokRag, InferenceConfiguration, ModelContextProtocolServer, + RagStore, ) from utils.query import normalize_vertex_ai_model_id from utils.responses import ( @@ -1711,13 +1711,13 @@ class TestResolveVectorStoreIds: """Tests for resolve_vector_store_ids function.""" @staticmethod - def _make_byok_rag(rag_id: str, vector_db_id: str) -> ByokRag: - """Create a ByokRag instance for testing.""" - return ByokRag( + def _make_byok_rag(rag_id: str, vector_db_id: str) -> RagStore: + """Create a RagStore instance for testing.""" + return RagStore( rag_id=rag_id, vector_db_id=vector_db_id, db_path="tests/configuration/rag.txt", - rag_type="rag", + backend="faiss", embedding_model="model", embedding_dimension=768, score_multiplier=1.0, @@ -1781,9 +1781,12 @@ async def test_translates_byok_ids_in_prepare_tools( mock_byok_rag.rag_id = "ocp_docs" mock_byok_rag.vector_db_id = "vs-001" mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [mock_byok_rag] - mock_config.configuration.rag.tool = [] - mock_config.configuration.rag.inline = [] + mock_config.configuration.rag.byok.stores = [mock_byok_rag] + mock_config.configuration.rag.retrieval.tool.sources = [] + mock_config.rag.retrieval.tool.max_chunks = ( + constants.DEFAULT_TOOL_RAG_MAX_CHUNKS + ) + mock_config.configuration.rag.retrieval.inline.sources = [] mocker.patch("utils.responses.configuration", mock_config) result = await prepare_tools(mock_client, ["ocp_docs"], False, "token") @@ -1802,9 +1805,12 @@ async def test_passes_through_unknown_ids_in_prepare_tools( # Configure empty BYOK RAG mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] - mock_config.configuration.rag.tool = [] - mock_config.configuration.rag.inline = [] + mock_config.configuration.rag.byok.stores = [] + mock_config.configuration.rag.retrieval.tool.sources = [] + mock_config.rag.retrieval.tool.max_chunks = ( + constants.DEFAULT_TOOL_RAG_MAX_CHUNKS + ) + mock_config.configuration.rag.retrieval.inline.sources = [] mocker.patch("utils.responses.configuration", mock_config) result = await prepare_tools(mock_client, ["raw-internal-id"], False, "token") @@ -1833,9 +1839,12 @@ async def test_does_not_translate_when_ids_fetched_from_llama_stack( mock_byok_rag.rag_id = "vs-internal" mock_byok_rag.vector_db_id = "vs-translated" mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [mock_byok_rag] - mock_config.configuration.rag.tool = [] - mock_config.configuration.rag.inline = [] + mock_config.configuration.rag.byok.stores = [mock_byok_rag] + mock_config.configuration.rag.retrieval.tool.sources = [] + mock_config.rag.retrieval.tool.max_chunks = ( + constants.DEFAULT_TOOL_RAG_MAX_CHUNKS + ) + mock_config.configuration.rag.retrieval.inline.sources = [] mocker.patch("utils.responses.configuration", mock_config) result = await prepare_tools(mock_client, None, False, "token") @@ -1857,9 +1866,15 @@ async def test_uses_rag_tool_config_when_no_per_request_ids( mocker.patch("utils.responses.get_mcp_tools", return_value=None) mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] - mock_config.configuration.rag.tool = ["rag-tool-id-1", "rag-tool-id-2"] - mock_config.configuration.rag.inline = [] + mock_config.configuration.rag.byok.stores = [] + mock_config.configuration.rag.retrieval.tool.sources = [ + "rag-tool-id-1", + "rag-tool-id-2", + ] + mock_config.rag.retrieval.tool.max_chunks = ( + constants.DEFAULT_TOOL_RAG_MAX_CHUNKS + ) + mock_config.configuration.rag.retrieval.inline.sources = [] mocker.patch("utils.responses.configuration", mock_config) result = await prepare_tools(mock_client, None, False, "token") @@ -1882,9 +1897,12 @@ async def test_rag_tool_config_ids_are_translated( mock_byok_rag.rag_id = "ocp_docs" mock_byok_rag.vector_db_id = "vs-001" mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [mock_byok_rag] - mock_config.configuration.rag.tool = ["ocp_docs"] - mock_config.configuration.rag.inline = [] + mock_config.configuration.rag.byok.stores = [mock_byok_rag] + mock_config.configuration.rag.retrieval.tool.sources = ["ocp_docs"] + mock_config.rag.retrieval.tool.max_chunks = ( + constants.DEFAULT_TOOL_RAG_MAX_CHUNKS + ) + mock_config.configuration.rag.retrieval.inline.sources = [] mocker.patch("utils.responses.configuration", mock_config) result = await prepare_tools(mock_client, None, False, "token") @@ -1901,9 +1919,9 @@ async def test_inline_rag_disables_tool_rag(self, mocker: MockerFixture) -> None mocker.patch("utils.responses.get_mcp_tools", return_value=None) mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] - mock_config.configuration.rag.tool = [] - mock_config.configuration.rag.inline = [ + mock_config.configuration.rag.byok.stores = [] + mock_config.configuration.rag.retrieval.tool.sources = [] + mock_config.configuration.rag.retrieval.inline.sources = [ "inline-store-id" ] # inline is configured mocker.patch("utils.responses.configuration", mock_config) @@ -1923,9 +1941,12 @@ async def test_per_request_ids_override_rag_tool_config( mocker.patch("utils.responses.get_mcp_tools", return_value=None) mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] - mock_config.configuration.rag.tool = ["config-id-1"] - mock_config.configuration.rag.inline = [] + mock_config.configuration.rag.byok.stores = [] + mock_config.configuration.rag.retrieval.tool.sources = ["config-id-1"] + mock_config.rag.retrieval.tool.max_chunks = ( + constants.DEFAULT_TOOL_RAG_MAX_CHUNKS + ) + mock_config.configuration.rag.retrieval.inline.sources = [] mocker.patch("utils.responses.configuration", mock_config) result = await prepare_tools(mock_client, ["request-id-1"], False, "token") @@ -1949,9 +1970,12 @@ async def test_all_registered_dbs_used_when_neither_tool_nor_inline_configured( mocker.patch("utils.responses.get_mcp_tools", return_value=None) mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] - mock_config.configuration.rag.tool = [] - mock_config.configuration.rag.inline = [] + mock_config.configuration.rag.byok.stores = [] + mock_config.configuration.rag.retrieval.tool.sources = [] + mock_config.rag.retrieval.tool.max_chunks = ( + constants.DEFAULT_TOOL_RAG_MAX_CHUNKS + ) + mock_config.configuration.rag.retrieval.inline.sources = [] mocker.patch("utils.responses.configuration", mock_config) result = await prepare_tools(mock_client, None, False, "token") @@ -3404,7 +3428,7 @@ async def test_client_tools_without_merge_header( return_value=mock_holder, ) mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] + mock_config.configuration.rag.byok.stores = [] mock_config.mcp_servers = [] mocker.patch("utils.responses.configuration", mock_config) @@ -3429,7 +3453,7 @@ async def test_client_tools_with_merge_header(self, mocker: MockerFixture) -> No return_value=mock_holder, ) mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] + mock_config.configuration.rag.byok.stores = [] mock_config.mcp_servers = [] mocker.patch("utils.responses.configuration", mock_config) @@ -3466,7 +3490,7 @@ async def test_merge_header_conflict_raises_409( return_value=mock_holder, ) mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] + mock_config.configuration.rag.byok.stores = [] mock_config.mcp_servers = [] mocker.patch("utils.responses.configuration", mock_config) @@ -3525,7 +3549,7 @@ async def test_merge_header_no_server_tools_returns_client_only( return_value=mock_holder, ) mock_config = mocker.Mock() - mock_config.configuration.byok_rag = [] + mock_config.configuration.rag.byok.stores = [] mock_config.mcp_servers = [] mocker.patch("utils.responses.configuration", mock_config) mocker.patch( diff --git a/tests/unit/utils/test_vector_search.py b/tests/unit/utils/test_vector_search.py index e53be0148..4cbfb1981 100644 --- a/tests/unit/utils/test_vector_search.py +++ b/tests/unit/utils/test_vector_search.py @@ -22,7 +22,7 @@ _extract_byok_rag_chunks, _extract_solr_document_metadata, _fetch_byok_rag, - _fetch_solr_rag, + _fetch_okp_rag, _format_rag_context, _get_okp_base_url, _get_solr_vector_store_ids, @@ -518,8 +518,8 @@ class TestFetchByokRag: async def test_byok_no_inline_ids(self, mocker: MockerFixture) -> None: """Test when no inline BYOK sources are configured.""" config_mock = mocker.Mock(spec=AppConfig) - config_mock.configuration.rag.inline = [] - config_mock.configuration.byok_rag = [] + config_mock.configuration.rag.retrieval.inline.sources = [] + config_mock.configuration.rag.byok.stores = [] mocker.patch("utils.vector_search.configuration", config_mock) client_mock = mocker.AsyncMock() @@ -537,8 +537,9 @@ async def test_byok_enabled_success(self, mocker: MockerFixture) -> None: byok_rag_mock = mocker.Mock() byok_rag_mock.rag_id = "rag_1" byok_rag_mock.vector_db_id = "vs_1" - config_mock.configuration.rag.inline = ["rag_1"] - config_mock.configuration.byok_rag = [byok_rag_mock] + config_mock.configuration.rag.retrieval.inline.sources = ["rag_1"] + config_mock.configuration.rag.byok.stores = [byok_rag_mock] + config_mock.rag.byok.max_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS config_mock.score_multiplier_mapping = {"vs_1": 1.5} config_mock.rag_id_mapping = {"vs_1": "rag_1"} mocker.patch("utils.vector_search.configuration", config_mock) @@ -576,8 +577,9 @@ async def test_user_facing_ids_translated_to_internal_ids( byok_rag_mock = mocker.Mock() byok_rag_mock.rag_id = "my-kb" byok_rag_mock.vector_db_id = "vs-internal-001" - config_mock.configuration.byok_rag = [byok_rag_mock] - config_mock.configuration.rag.inline = ["my-kb"] + config_mock.configuration.rag.byok.stores = [byok_rag_mock] + config_mock.configuration.rag.retrieval.inline.sources = ["my-kb"] + config_mock.rag.byok.max_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS config_mock.score_multiplier_mapping = {"vs-internal-001": 1.0} config_mock.rag_id_mapping = {"vs-internal-001": "my-kb"} mocker.patch("utils.vector_search.configuration", config_mock) @@ -601,7 +603,10 @@ async def test_user_facing_ids_translated_to_internal_ids( client_mock.vector_io.query.assert_called_once_with( vector_store_id="vs-internal-001", query="test query", - params={"max_chunks": constants.BYOK_RAG_MAX_CHUNKS, "mode": "vector"}, + params={ + "max_chunks": constants.DEFAULT_BYOK_RAG_MAX_CHUNKS, + "mode": "vector", + }, ) @pytest.mark.asyncio @@ -616,8 +621,12 @@ async def test_multiple_user_facing_ids_each_translated( byok_rag_2 = mocker.Mock() byok_rag_2.rag_id = "kb-part2" byok_rag_2.vector_db_id = "vs-bbb-222" - config_mock.configuration.byok_rag = [byok_rag_1, byok_rag_2] - config_mock.configuration.rag.inline = ["kb-part1", "kb-part2"] + config_mock.configuration.rag.byok.stores = [byok_rag_1, byok_rag_2] + config_mock.configuration.rag.retrieval.inline.sources = [ + "kb-part1", + "kb-part2", + ] + config_mock.rag.byok.max_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS config_mock.score_multiplier_mapping = {"vs-aaa-111": 1.0, "vs-bbb-222": 1.0} config_mock.rag_id_mapping = { "vs-aaa-111": "kb-part1", @@ -658,8 +667,8 @@ async def test_no_inline_rag_configured_skips_byok( ) -> None: """Test that BYOK inline RAG is skipped when rag.inline is empty.""" config_mock = mocker.Mock(spec=AppConfig) - config_mock.configuration.rag.inline = [] - config_mock.configuration.byok_rag = [] + config_mock.configuration.rag.retrieval.inline.sources = [] + config_mock.configuration.rag.byok.stores = [] mocker.patch("utils.vector_search.configuration", config_mock) client_mock = mocker.AsyncMock() @@ -678,8 +687,8 @@ async def test_request_id_not_in_inline_config_skips_byok( ) -> None: """Test that a request vector_store_id not registered in rag.inline is filtered out.""" config_mock = mocker.Mock(spec=AppConfig) - config_mock.configuration.rag.inline = ["registered-id"] - config_mock.configuration.byok_rag = [] + config_mock.configuration.rag.retrieval.inline.sources = ["registered-id"] + config_mock.configuration.rag.byok.stores = [] mocker.patch("utils.vector_search.configuration", config_mock) client_mock = mocker.AsyncMock() @@ -694,7 +703,7 @@ async def test_request_id_not_in_inline_config_skips_byok( class TestFetchSolrRag: - """Tests for _fetch_solr_rag async function.""" + """Tests for _fetch_okp_rag async function.""" @pytest.mark.asyncio async def test_solr_disabled(self, mocker: MockerFixture) -> None: @@ -704,7 +713,7 @@ async def test_solr_disabled(self, mocker: MockerFixture) -> None: mocker.patch("utils.vector_search.configuration", config_mock) client_mock = mocker.AsyncMock() - rag_chunks, referenced_docs = await _fetch_solr_rag(client_mock, "test query") + rag_chunks, referenced_docs = await _fetch_okp_rag(client_mock, "test query") assert rag_chunks == [] assert referenced_docs == [] @@ -718,6 +727,7 @@ async def test_solr_enabled_success(self, mocker: MockerFixture) -> None: config_mock.inline_solr_enabled = True config_mock.okp.offline = True config_mock.okp.rhokp_url = "https://okp.test" + config_mock.rag.okp.max_chunks = constants.DEFAULT_OKP_RAG_MAX_CHUNKS mocker.patch("utils.vector_search.configuration", config_mock) # Mock chunk @@ -735,7 +745,7 @@ async def test_solr_enabled_success(self, mocker: MockerFixture) -> None: client_mock = mocker.AsyncMock() client_mock.vector_io.query.return_value = query_response - rag_chunks, _referenced_docs = await _fetch_solr_rag(client_mock, "test query") + rag_chunks, _referenced_docs = await _fetch_okp_rag(client_mock, "test query") assert len(rag_chunks) > 0 assert rag_chunks[0].content == "Solr content" @@ -750,6 +760,7 @@ async def test_solr_enabled_passes_request_mode_to_vector_io( config_mock.inline_solr_enabled = True config_mock.okp.offline = True config_mock.okp.rhokp_url = "https://okp.test" + config_mock.rag.okp.max_chunks = constants.DEFAULT_OKP_RAG_MAX_CHUNKS mocker.patch("utils.vector_search.configuration", config_mock) chunk_mock = mocker.Mock() @@ -764,7 +775,7 @@ async def test_solr_enabled_passes_request_mode_to_vector_io( client_mock = mocker.AsyncMock() client_mock.vector_io.query.return_value = query_response - await _fetch_solr_rag( + await _fetch_okp_rag( client_mock, "test query", SolrVectorSearchRequest(mode="semantic", filters={"fq": ["x:y"]}), @@ -783,8 +794,12 @@ class TestBuildRagContext: async def test_both_sources_disabled(self, mocker: MockerFixture) -> None: """Test when both BYOK inline and Solr inline are not configured.""" config_mock = mocker.Mock(spec=AppConfig) - config_mock.configuration.rag.inline = [] - config_mock.configuration.byok_rag = [] + config_mock.configuration.rag.retrieval.inline.sources = [] + config_mock.configuration.rag.byok.stores = [] + config_mock.rag.retrieval.inline.max_chunks = ( + constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + ) + config_mock.rag.byok.max_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS config_mock.inline_solr_enabled = False mocker.patch("utils.vector_search.configuration", config_mock) @@ -803,8 +818,12 @@ async def test_byok_enabled_only(self, mocker: MockerFixture) -> None: byok_rag_mock = mocker.Mock() byok_rag_mock.rag_id = "rag_1" byok_rag_mock.vector_db_id = "vs_1" - config_mock.configuration.rag.inline = ["rag_1"] - config_mock.configuration.byok_rag = [byok_rag_mock] + config_mock.configuration.rag.retrieval.inline.sources = ["rag_1"] + config_mock.configuration.rag.byok.stores = [byok_rag_mock] + config_mock.rag.retrieval.inline.max_chunks = ( + constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + ) + config_mock.rag.byok.max_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS config_mock.inline_solr_enabled = False config_mock.score_multiplier_mapping = {"vs_1": 1.0} config_mock.rag_id_mapping = {"vs_1": "rag_1"} @@ -840,8 +859,12 @@ async def test_reranker_enabled_calls_cross_encoder( byok_rag_mock = mocker.Mock() byok_rag_mock.rag_id = "rag_1" byok_rag_mock.vector_db_id = "vs_1" - config_mock.configuration.rag.inline = ["rag_1"] - config_mock.configuration.byok_rag = [byok_rag_mock] + config_mock.configuration.rag.retrieval.inline.sources = ["rag_1"] + config_mock.configuration.rag.byok.stores = [byok_rag_mock] + config_mock.rag.retrieval.inline.max_chunks = ( + constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + ) + config_mock.rag.byok.max_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS config_mock.inline_solr_enabled = False config_mock.score_multiplier_mapping = {"vs_1": 1.0} config_mock.rag_id_mapping = {"vs_1": "rag_1"} @@ -891,8 +914,12 @@ async def test_reranker_disabled_skips_cross_encoder( byok_rag_mock = mocker.Mock() byok_rag_mock.rag_id = "rag_1" byok_rag_mock.vector_db_id = "vs_1" - config_mock.configuration.rag.inline = ["rag_1"] - config_mock.configuration.byok_rag = [byok_rag_mock] + config_mock.configuration.rag.retrieval.inline.sources = ["rag_1"] + config_mock.configuration.rag.byok.stores = [byok_rag_mock] + config_mock.rag.retrieval.inline.max_chunks = ( + constants.DEFAULT_INLINE_RAG_MAX_CHUNKS + ) + config_mock.rag.byok.max_chunks = constants.DEFAULT_BYOK_RAG_MAX_CHUNKS config_mock.inline_solr_enabled = False config_mock.score_multiplier_mapping = {"vs_1": 1.0} config_mock.rag_id_mapping = {"vs_1": "rag_1"} From a35320b15d8cb3e9efd2c1f81e6468ad15e45754 Mon Sep 17 00:00:00 2001 From: are-ces <195810094+are-ces@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:41:16 +0200 Subject: [PATCH 2/5] LCORE-1426: update e2e configs to new unified rag format Convert all e2e YAML configs from old format (top-level byok_rag, rag_type, flat rag.inline/tool) to new unified format (rag.byok.stores, backend, rag.retrieval.inline.sources/tool.sources). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lightspeed-stack-auth-noop-token.yaml | 24 ++++++++++--------- .../lightspeed-stack-byok-pdf.yaml | 24 ++++++++++--------- .../lightspeed-stack-inline-rag.yaml | 24 ++++++++++--------- .../library-mode/lightspeed-stack.yaml | 24 ++++++++++--------- .../lightspeed-stack-auth-noop-token.yaml | 24 ++++++++++--------- .../lightspeed-stack-inline-rag.yaml | 24 ++++++++++--------- .../server-mode/lightspeed-stack-rhelai.yaml | 24 ++++++++++--------- .../server-mode/lightspeed-stack-rhoai.yaml | 24 ++++++++++--------- .../server-mode/lightspeed-stack.yaml | 24 ++++++++++--------- 9 files changed, 117 insertions(+), 99 deletions(-) diff --git a/tests/e2e/configuration/library-mode/lightspeed-stack-auth-noop-token.yaml b/tests/e2e/configuration/library-mode/lightspeed-stack-auth-noop-token.yaml index ca8b4476c..842a1ccfa 100644 --- a/tests/e2e/configuration/library-mode/lightspeed-stack-auth-noop-token.yaml +++ b/tests/e2e/configuration/library-mode/lightspeed-stack-auth-noop-token.yaml @@ -30,15 +30,17 @@ inference: default_provider: openai default_model: gpt-4o-mini -byok_rag: - - rag_id: e2e-test-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: ${env.FAISS_VECTOR_STORE_ID} - db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} - score_multiplier: 1.0 - rag: - tool: - - e2e-test-docs + byok: + stores: + - rag_id: e2e-test-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: ${env.FAISS_VECTOR_STORE_ID} + db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} + score_multiplier: 1.0 + retrieval: + tool: + sources: + - e2e-test-docs diff --git a/tests/e2e/configuration/library-mode/lightspeed-stack-byok-pdf.yaml b/tests/e2e/configuration/library-mode/lightspeed-stack-byok-pdf.yaml index da9924d84..608da8104 100644 --- a/tests/e2e/configuration/library-mode/lightspeed-stack-byok-pdf.yaml +++ b/tests/e2e/configuration/library-mode/lightspeed-stack-byok-pdf.yaml @@ -34,15 +34,17 @@ inference: # store (tests/e2e/rag/pdf_kv_store.db) so this feature is self-contained and # needs no externally-provisioned vector-store id. See tests/e2e/rag/README.md # for how the fixture was produced. -byok_rag: - - rag_id: pdf-field-notes - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_4a27375c-b8da-4134-96fc-b8198d111015 - db_path: ${env.PDF_KV_RAG_PATH:=~/.llama/storage/rag/pdf_kv_store.db} - score_multiplier: 1.0 - rag: - inline: - - pdf-field-notes + byok: + stores: + - rag_id: pdf-field-notes + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: vs_4a27375c-b8da-4134-96fc-b8198d111015 + db_path: ${env.PDF_KV_RAG_PATH:=~/.llama/storage/rag/pdf_kv_store.db} + score_multiplier: 1.0 + retrieval: + inline: + sources: + - pdf-field-notes diff --git a/tests/e2e/configuration/library-mode/lightspeed-stack-inline-rag.yaml b/tests/e2e/configuration/library-mode/lightspeed-stack-inline-rag.yaml index f73c4d9c3..b7f409a1a 100644 --- a/tests/e2e/configuration/library-mode/lightspeed-stack-inline-rag.yaml +++ b/tests/e2e/configuration/library-mode/lightspeed-stack-inline-rag.yaml @@ -29,15 +29,17 @@ inference: default_provider: openai default_model: gpt-4o-mini -byok_rag: - - rag_id: e2e-test-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: ${env.FAISS_VECTOR_STORE_ID} - db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} - score_multiplier: 1.0 - rag: - inline: - - e2e-test-docs + byok: + stores: + - rag_id: e2e-test-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: ${env.FAISS_VECTOR_STORE_ID} + db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} + score_multiplier: 1.0 + retrieval: + inline: + sources: + - e2e-test-docs diff --git a/tests/e2e/configuration/library-mode/lightspeed-stack.yaml b/tests/e2e/configuration/library-mode/lightspeed-stack.yaml index 077b2e9ab..0bffce0e4 100644 --- a/tests/e2e/configuration/library-mode/lightspeed-stack.yaml +++ b/tests/e2e/configuration/library-mode/lightspeed-stack.yaml @@ -23,15 +23,17 @@ authentication: inference: default_provider: openai default_model: gpt-4o-mini -byok_rag: - - rag_id: e2e-test-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: ${env.FAISS_VECTOR_STORE_ID} - db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} - score_multiplier: 1.0 - rag: - tool: - - e2e-test-docs + byok: + stores: + - rag_id: e2e-test-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: ${env.FAISS_VECTOR_STORE_ID} + db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} + score_multiplier: 1.0 + retrieval: + tool: + sources: + - e2e-test-docs diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack-auth-noop-token.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack-auth-noop-token.yaml index 49ee71d59..42dfc8dc2 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack-auth-noop-token.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack-auth-noop-token.yaml @@ -33,15 +33,17 @@ inference: default_provider: openai default_model: gpt-4o-mini -byok_rag: - - rag_id: e2e-test-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: ${env.FAISS_VECTOR_STORE_ID} - db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} - score_multiplier: 1.0 - rag: - tool: - - e2e-test-docs + byok: + stores: + - rag_id: e2e-test-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: ${env.FAISS_VECTOR_STORE_ID} + db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} + score_multiplier: 1.0 + retrieval: + tool: + sources: + - e2e-test-docs diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack-inline-rag.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack-inline-rag.yaml index 1a2850162..fefea3b6e 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack-inline-rag.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack-inline-rag.yaml @@ -27,15 +27,17 @@ inference: default_provider: openai default_model: gpt-4o-mini -byok_rag: - - rag_id: e2e-test-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: ${env.FAISS_VECTOR_STORE_ID} - db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} - score_multiplier: 1.0 - rag: - inline: - - e2e-test-docs + byok: + stores: + - rag_id: e2e-test-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: ${env.FAISS_VECTOR_STORE_ID} + db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} + score_multiplier: 1.0 + retrieval: + inline: + sources: + - e2e-test-docs diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack-rhelai.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack-rhelai.yaml index 4313c7605..9ed0073d6 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack-rhelai.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack-rhelai.yaml @@ -21,15 +21,17 @@ authentication: inference: default_provider: vllm default_model: ${env.VLLM_MODEL} -byok_rag: - - rag_id: e2e-test-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: ${env.FAISS_VECTOR_STORE_ID} - db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} - score_multiplier: 1.0 - rag: - tool: - - e2e-test-docs + byok: + stores: + - rag_id: e2e-test-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: ${env.FAISS_VECTOR_STORE_ID} + db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} + score_multiplier: 1.0 + retrieval: + tool: + sources: + - e2e-test-docs diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack-rhoai.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack-rhoai.yaml index 4313c7605..9ed0073d6 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack-rhoai.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack-rhoai.yaml @@ -21,15 +21,17 @@ authentication: inference: default_provider: vllm default_model: ${env.VLLM_MODEL} -byok_rag: - - rag_id: e2e-test-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: ${env.FAISS_VECTOR_STORE_ID} - db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} - score_multiplier: 1.0 - rag: - tool: - - e2e-test-docs + byok: + stores: + - rag_id: e2e-test-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: ${env.FAISS_VECTOR_STORE_ID} + db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} + score_multiplier: 1.0 + retrieval: + tool: + sources: + - e2e-test-docs diff --git a/tests/e2e/configuration/server-mode/lightspeed-stack.yaml b/tests/e2e/configuration/server-mode/lightspeed-stack.yaml index de945bb2e..d355bdf0a 100644 --- a/tests/e2e/configuration/server-mode/lightspeed-stack.yaml +++ b/tests/e2e/configuration/server-mode/lightspeed-stack.yaml @@ -21,15 +21,17 @@ authentication: inference: default_provider: openai default_model: gpt-4o-mini -byok_rag: - - rag_id: e2e-test-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: ${env.FAISS_VECTOR_STORE_ID} - db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} - score_multiplier: 1.0 - rag: - tool: - - e2e-test-docs \ No newline at end of file + byok: + stores: + - rag_id: e2e-test-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: ${env.FAISS_VECTOR_STORE_ID} + db_path: ${env.KV_RAG_PATH:=~/.llama/storage/rag/kv_store.db} + score_multiplier: 1.0 + retrieval: + tool: + sources: + - e2e-test-docs \ No newline at end of file From 0814b596247f737f644c677862eb0d8f8904f794 Mon Sep 17 00:00:00 2001 From: are-ces <195810094+are-ces@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:41:32 +0200 Subject: [PATCH 3/5] LCORE-1426: update docs and regenerate OpenAPI schema - Convert all YAML examples in byok_guide.md and rag_guide.md to new unified rag config format - Update example configs (byok-okp-rag, quota-limiter) - Regenerate OpenAPI schema from updated models Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/byok_guide.md | 283 +++++++----- docs/openapi.json | 410 +++++++++++------- docs/rag_guide.md | 167 ++++--- examples/lightspeed-stack-byok-okp-rag.yaml | 73 ++-- .../quota-limiter-configuration-sqlite.yaml | 12 - 5 files changed, 555 insertions(+), 390 deletions(-) diff --git a/docs/byok_guide.md b/docs/byok_guide.md index dafdc9355..62f02a782 100644 --- a/docs/byok_guide.md +++ b/docs/byok_guide.md @@ -10,6 +10,7 @@ The BYOK (Bring Your Own Knowledge) feature in Lightspeed Core enables users to * [What is BYOK?](#what-is-byok) * [How BYOK Works](#how-byok-works) + * [Prioritization of BYOK content](#prioritization-of-byok-content) * [Prerequisites](#prerequisites) * [Configuration Guide](#configuration-guide) * [Step 1: Prepare Your Knowledge Sources](#step-1-prepare-your-knowledge-sources) @@ -77,17 +78,45 @@ Both modes rely on: - **Vector Database**: Your indexed knowledge sources stored as vector embeddings - **Embedding Model**: Converts queries and documents into vector representations for similarity matching -Inline RAG additionally supports: -- **Score Multiplier**: Optional weight applied per BYOK vector store when mixing multiple sources. Allows custom prioritization of content. +### Prioritization of BYOK content -> [!NOTE] -> OKP and BYOK scores are not directly comparable (different scoring systems), so -> `score_multiplier` does not apply to OKP results. To control the amount of retrieved -> context, set the `BYOK_RAG_MAX_CHUNKS` and `OKP_RAG_MAX_CHUNKS` constants in `src/constants.py` -> (defaults: 10 and 5 respectively). For Tool RAG, use `TOOL_RAG_MAX_CHUNKS` (default: 10). -> The `INLINE_RAG_MAX_CHUNKS` constant (value: 10) caps the final merged inline RAG -> chunks (BYOK + OKP) delivered to the LLM. Tool RAG is controlled independently -> by `TOOL_RAG_MAX_CHUNKS`. +When multiple BYOK stores are configured for Inline RAG, their results are merged and ranked. Two mechanisms control prioritization: + +- **Score Multiplier** (`score_multiplier`): A per-store weight applied to raw similarity scores during Inline RAG. Values > 1.0 boost a store's results; values < 1.0 reduce them. Only affects BYOK stores — OKP scores use a different scoring system and are not comparable. + +- **Reranker**: When enabled, a cross-encoder model re-scores the merged chunk pool (BYOK + OKP) using semantic similarity to the query. This normalizes scores across sources, making OKP and BYOK results directly comparable. BYOK score boosts are applied after reranking. + +**Chunk limits** control how many chunks flow through the pipeline. Configure them in `lightspeed-stack.yaml`: + +| Config path | Default | Description | +|-------------|---------|-------------| +| `rag.byok.max_chunks` | 10 | Total chunks fetched across all BYOK stores | +| `rag.okp.max_chunks` | 5 | Chunks fetched from OKP | +| `rag.retrieval.inline.max_chunks` | 10 | Final cap on merged inline RAG chunks delivered to the LLM | +| `rag.retrieval.tool.max_chunks` | 10 | Max chunks retrieved via Tool RAG (`file_search`) | + +```mermaid +flowchart TD + subgraph Sources["Source Fetching"] + B1["BYOK Store 1"] --> BPool + B2["BYOK Store 2"] --> BPool + BN["BYOK Store N"] --> BPool + BPool["BYOK Pool\ncapped at rag.byok.max_chunks"] + OKP["OKP (Solr)\ncapped at rag.okp.max_chunks"] + end + + BPool --> Pool["Merged Pool\n(all chunks, sorted by score)"] + OKP --> Pool + + Pool --> Decision{Reranker\nenabled?} + + Decision -->|Yes| Rerank["Cross-Encoder Rerank\n+ BYOK score boost"] + Decision -->|No| Cut + + Rerank --> Cut["Top K cut\nrag.retrieval.inline.max_chunks"] + + Cut --> Context["Final Inline RAG Context"] +``` --- @@ -113,13 +142,7 @@ Before implementing BYOK, ensure you have: ## Configuration Guide -> [!WARNING] -> **Deprecated in 0.7.0**: The top-level `byok_rag`, `rag`, `okp`, and `reranker` sections -> are deprecated. In 0.7.0, all RAG-related configuration is unified under a single `rag` -> section: BYOK stores move to `rag.byok.stores` (with `backend` instead of `rag_type`), -> retrieval strategies move to `rag.retrieval.inline`/`rag.retrieval.tool`, OKP moves to -> `rag.okp`, and the reranker moves to `rag.retrieval.inline.reranker`. -> See the [v0.7.0 Migration Guide](migrations/v0.7.0.md) for full details and examples. + ### Step 1: Prepare Your Knowledge Sources @@ -157,7 +180,7 @@ class CustomMetadataProcessor(MetadataProcessor): **Important Notes:** - Supported formats: - Faiss Vector-IO -- **The embedding model (and its dimension) used to *build* the vector store must exactly match the one configured for querying** in the `byok_rag` section (see Step 3). A mismatch does not raise an error — it silently returns no or irrelevant results, because the query vector and the stored vectors are then incomparable. The default is `sentence-transformers/all-mpnet-base-v2` (dimension `768`). +- **The embedding model (and its dimension) used to *build* the vector store must exactly match the one configured for querying** in the `rag.byok.stores` section (see Step 3). A mismatch does not raise an error — it silently returns no or irrelevant results, because the query vector and the stored vectors are then incomparable. The default is `sentence-transformers/all-mpnet-base-v2` (dimension `768`). ### Step 3: Configure Embedding Model @@ -177,23 +200,25 @@ Alternatively, you can download your own embedding model and update the path in 1. **Download your preferred embedding model** from Hugging Face or other sources 2. **Place the model** in your desired directory (e.g., `/path/to/your/embedding_models/`) -The embedding model is specified per knowledge source in the `byok_rag` section of `lightspeed-stack.yaml` via the `embedding_model` field. The default is `sentence-transformers/all-mpnet-base-v2` with a dimension of `768`. +The embedding model is specified per knowledge source in the `rag.byok.stores` section of `lightspeed-stack.yaml` via the `embedding_model` field. The default is `sentence-transformers/all-mpnet-base-v2` with a dimension of `768`. **Note**: Ensure the same embedding model is used for both vector database creation and querying. ### Step 4: Configure BYOK Knowledge Sources -Declare your knowledge sources in the `byok_rag` section of your `lightspeed-stack.yaml`. The required configuration is automatically generated at startup when using `make run`, `make run-stack`, `docker-compose`, or library mode. +Declare your knowledge sources in the `rag.byok.stores` section of your `lightspeed-stack.yaml`. The required configuration is automatically generated at startup when using `make run`, `make run-stack`, `docker-compose`, or library mode. ```yaml -byok_rag: - - rag_id: my-docs # Unique identifier for this knowledge source - rag_type: inline::faiss # Vector store type (default: inline::faiss) - embedding_model: sentence-transformers/all-mpnet-base-v2 # Embedding model (default) - embedding_dimension: 768 # Must match your embedding model's output - vector_db_id: vs_8c94967b-81cc-4028-a294-9cfac6fd9ae2 # Generated by rag-content during index creation - db_path: /path/to/vector_db/faiss_store.db # Path to the vector database file - score_multiplier: 1.0 # Weight for Inline RAG result ranking (default: 1.0) +rag: + byok: + stores: + - rag_id: my-docs # Unique identifier for this knowledge source + backend: faiss # Vector store type (default: faiss) + embedding_model: sentence-transformers/all-mpnet-base-v2 # Embedding model (default) + embedding_dimension: 768 # Must match your embedding model's output + vector_db_id: vs_8c94967b-81cc-4028-a294-9cfac6fd9ae2 # Generated by rag-content during index creation + db_path: /path/to/vector_db/faiss_store.db # Path to the vector database file + score_multiplier: 1.0 # Weight for Inline RAG result ranking (default: 1.0) ``` **Common fields (all providers):** @@ -201,19 +226,19 @@ byok_rag: | Field | Required | Default | Description | |-----------------------|----------|-------------------------------------------|-------------------------------------------------------------------------------------------| | `rag_id` | Yes | — | Unique identifier for the knowledge source | -| `rag_type` | No | `inline::faiss` | Vector store provider type (`inline::faiss` or `remote::pgvector`) | +| `backend` | No | `faiss` | Vector store provider type (`faiss` or `pgvector`) | | `embedding_model` | No | `sentence-transformers/all-mpnet-base-v2` | Embedding model identifier or path | | `embedding_dimension` | No | `768` | Embedding vector dimensionality | | `vector_db_id` | Yes | — | Vector store ID generated by rag-content (e.g. `vs_8c94967b-81cc-4028-a294-9cfac6fd9ae2`) | | `score_multiplier` | No | `1.0` | Weight for Inline RAG ranking (values > 1.0 boost; < 1.0 reduce) | -**FAISS fields** (`rag_type: inline::faiss`): +**FAISS fields** (`backend: faiss`): | Field | Required | Default | Description | |-----------|----------|---------|----------------------------------| | `db_path` | Yes | — | Path to the vector database file | -**pgvector fields** (`rag_type: remote::pgvector`): +**pgvector fields** (`backend: pgvector`): | Field | Required | Default | Description | |------------|----------|--------------------------|---------------------| @@ -228,47 +253,55 @@ byok_rag: You can configure multiple BYOK sources. When using Inline RAG, `score_multiplier` adjusts the relative importance of each store's results: ```yaml -byok_rag: - - rag_id: ocp-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_3a7f9b2e-45dc-4e1a-b8f2-1c9d0e3f5a6b - db_path: /data/vector_dbs/ocp_docs/faiss_store.db - score_multiplier: 1.0 - - - rag_id: internal-kb - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_d4c8e1f0-92ab-4d3c-a5e7-6b8f0c2d1e3a - db_path: /data/vector_dbs/internal_kb/faiss_store.db - score_multiplier: 1.2 # Boost results from this store +rag: + byok: + stores: + - rag_id: ocp-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: vs_3a7f9b2e-45dc-4e1a-b8f2-1c9d0e3f5a6b + db_path: /data/vector_dbs/ocp_docs/faiss_store.db + score_multiplier: 1.0 + + - rag_id: internal-kb + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: vs_d4c8e1f0-92ab-4d3c-a5e7-6b8f0c2d1e3a + db_path: /data/vector_dbs/internal_kb/faiss_store.db + score_multiplier: 1.2 # Boost results from this store ``` **⚠️ Important**: The `vector_db_id` value must exactly match the ID generated by the rag-content tool during index creation (e.g. `vs_8c94967b-81cc-4028-a294-9cfac6fd9ae2`). This identifier links your configuration to the specific vector database index. ### Step 5: Configure RAG Strategy -Add a `rag` section to your `lightspeed-stack.yaml` to choose how BYOK knowledge is used. -Each list entry is a `rag_id` from `byok_rag`, or the special value `okp` for OKP. +Add a `rag.retrieval` section to your `lightspeed-stack.yaml` to choose how BYOK knowledge is used. +Each list entry is a `rag_id` from `rag.byok.stores`, or the special value `okp` for OKP. ```yaml rag: - # Inline RAG: inject context before the LLM request (no tool calls needed) - inline: - - my-docs # rag_id from byok_rag - - okp # include OKP context inline - - # Tool RAG: the LLM can call file_search to retrieve context on demand - # If omitted, tool RAG is disabled. If both tool and inline are omitted, all registered stores are used as fallback - tool: - - my-docs # expose this BYOK store as the file_search tool - - okp # expose OKP as the file_search tool - -# OKP provider settings (only relevant when okp is listed above) -okp: - offline: true # true = use parent_id for source URLs, false = use reference_url + # byok.stores is defined in Step 4 above — only the rag_id values are + # referenced here; you do not need to repeat the full store definitions. + + retrieval: + # Inline RAG: inject context before the LLM request (no tool calls needed) + inline: + sources: + - my-docs # rag_id from rag.byok.stores + - okp # include OKP context inline + + # Tool RAG: the LLM can call file_search to retrieve context on demand + # If omitted, tool RAG is disabled. If both tool and inline are omitted, all registered stores are used as fallback + tool: + sources: + - my-docs # expose this BYOK store as the file_search tool + - okp # expose OKP as the file_search tool + + # OKP provider settings (only relevant when okp is listed above) + okp: + offline: true # true = use parent_id for source URLs, false = use reference_url ``` Both modes can be enabled simultaneously. Choose based on your latency and control preferences: @@ -289,37 +322,41 @@ Both modes can be enabled simultaneously. Choose based on your latency and contr ### 1. FAISS (Recommended) - **Type**: Local vector database with SQLite metadata - **Best for**: Small to medium-sized knowledge bases -- **Configuration**: `rag_type: inline::faiss` +- **Configuration**: `backend: faiss` - **Storage**: SQLite database file ```yaml -byok_rag: - - rag_id: faiss-knowledge - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_8c94967b-81cc-4028-a294-9cfac6fd9ae2 - db_path: /path/to/faiss_store.db +rag: + byok: + stores: + - rag_id: faiss-knowledge + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: vs_8c94967b-81cc-4028-a294-9cfac6fd9ae2 + db_path: /path/to/faiss_store.db ``` ### 2. pgvector (PostgreSQL) - **Type**: PostgreSQL with pgvector extension - **Best for**: Large-scale deployments, shared knowledge bases -- **Configuration**: `rag_type: remote::pgvector` +- **Configuration**: `backend: pgvector` - **Requirements**: PostgreSQL with pgvector extension ```yaml -byok_rag: - - rag_id: pgvector-knowledge - rag_type: remote::pgvector - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: rhdocs - host: ${env.POSTGRES_HOST} - port: ${env.POSTGRES_PORT} - db: ${env.POSTGRES_DATABASE} - user: ${env.POSTGRES_USER} - password: ${env.POSTGRES_PASSWORD} +rag: + byok: + stores: + - rag_id: pgvector-knowledge + backend: pgvector + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: rhdocs + host: ${env.POSTGRES_HOST} + port: ${env.POSTGRES_PORT} + db: ${env.POSTGRES_DATABASE} + user: ${env.POSTGRES_USER} + password: ${env.POSTGRES_PASSWORD} ``` > [!NOTE] @@ -346,19 +383,22 @@ service: port: 8080 auth_enabled: false -byok_rag: - - rag_id: company-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_f1a2b3c4-56de-4f78-90ab-cdef12345678 - db_path: /home/user/vector_dbs/company_docs/faiss_store.db - rag: - inline: - - company-docs - tool: - - company-docs + byok: + stores: + - rag_id: company-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: vs_f1a2b3c4-56de-4f78-90ab-cdef12345678 + db_path: /home/user/vector_dbs/company_docs/faiss_store.db + retrieval: + inline: + sources: + - company-docs + tool: + sources: + - company-docs ``` ### Example 2: Multiple Knowledge Sources with pgvector @@ -372,32 +412,35 @@ service: port: 8080 auth_enabled: false -byok_rag: - - rag_id: local-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_e9d8c7b6-43af-4b2d-8e1f-0a9b8c7d6e5f - db_path: /data/vector_dbs/local/faiss_store.db - score_multiplier: 1.0 - - rag_id: enterprise-kb - rag_type: remote::pgvector - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: enterprise_docs - host: ${env.POSTGRES_HOST} - port: ${env.POSTGRES_PORT} - db: ${env.POSTGRES_DATABASE} - user: ${env.POSTGRES_USER} - password: ${env.POSTGRES_PASSWORD} - rag: - inline: - - local-docs - - enterprise-kb - tool: - - local-docs - - enterprise-kb + byok: + stores: + - rag_id: local-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: vs_e9d8c7b6-43af-4b2d-8e1f-0a9b8c7d6e5f + db_path: /data/vector_dbs/local/faiss_store.db + score_multiplier: 1.0 + - rag_id: enterprise-kb + backend: pgvector + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: enterprise_docs + host: ${env.POSTGRES_HOST} + port: ${env.POSTGRES_PORT} + db: ${env.POSTGRES_DATABASE} + user: ${env.POSTGRES_USER} + password: ${env.POSTGRES_PASSWORD} + retrieval: + inline: + sources: + - local-docs + - enterprise-kb + tool: + sources: + - local-docs + - enterprise-kb ``` > [!NOTE] diff --git a/docs/openapi.json b/docs/openapi.json index ba4df6865..306664c30 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -6729,7 +6729,6 @@ "authorization": { "access_rules": [] }, - "byok_rag": [], "conversation_cache": {}, "database": { "sqlite": { @@ -6760,6 +6759,26 @@ "period": 1 } }, + "rag": { + "byok": { + "max_chunks": 10, + "stores": [] + }, + "okp": { + "max_chunks": 5, + "offline": true + }, + "retrieval": { + "inline": { + "max_chunks": 10, + "sources": [] + }, + "tool": { + "max_chunks": 10, + "sources": [] + } + } + }, "service": { "access_log": true, "auth_enabled": false, @@ -11980,131 +11999,28 @@ ], "title": "Body_create_file_v1_files_post" }, - "ByokRag": { + "ByokConfiguration": { "properties": { - "rag_id": { - "type": "string", - "minLength": 1, - "title": "RAG ID", - "description": "Unique RAG ID" - }, - "rag_type": { - "type": "string", - "minLength": 1, - "title": "RAG type", - "description": "Type of RAG database (e.g. 'inline::faiss', 'remote::pgvector').", - "default": "inline::faiss" - }, - "embedding_model": { - "type": "string", - "minLength": 1, - "title": "Embedding model", - "description": "Embedding model identification", - "default": "sentence-transformers/all-mpnet-base-v2" - }, - "embedding_dimension": { + "max_chunks": { "type": "integer", "exclusiveMinimum": 0.0, - "title": "Embedding dimension", - "description": "Dimensionality of embedding vectors.", - "default": 768 - }, - "vector_db_id": { - "type": "string", - "minLength": 1, - "title": "Vector DB ID", - "description": "Vector database identification." - }, - "db_path": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "DB path", - "description": "Path to RAG database. Required for inline::faiss." - }, - "score_multiplier": { - "type": "number", - "exclusiveMinimum": 0.0, - "title": "Score multiplier", - "description": "Multiplier applied to relevance scores from this vector store. Used to weight results when querying multiple knowledge sources. Values > 1 boost this store's results; values < 1 reduce them.", - "default": 1.0 - }, - "host": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "PostgreSQL host", - "description": "PostgreSQL host for remote::pgvector. Defaults to ${env.POSTGRES_HOST} when rag_type is remote::pgvector." - }, - "port": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "PostgreSQL port", - "description": "PostgreSQL port for remote::pgvector. Defaults to ${env.POSTGRES_PORT} when rag_type is remote::pgvector." - }, - "db": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "PostgreSQL database", - "description": "PostgreSQL database name for remote::pgvector. Defaults to ${env.POSTGRES_DATABASE} when rag_type is remote::pgvector." - }, - "user": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "PostgreSQL user", - "description": "PostgreSQL user for remote::pgvector. Defaults to ${env.POSTGRES_USER} when rag_type is remote::pgvector." + "title": "Max BYOK chunks", + "description": "Maximum total number of chunks returned across all BYOK stores.", + "default": 10 }, - "password": { - "anyOf": [ - { - "type": "string", - "format": "password", - "writeOnly": true - }, - { - "type": "null" - } - ], - "title": "PostgreSQL password", - "description": "PostgreSQL password for remote::pgvector. Defaults to ${env.POSTGRES_PASSWORD} when rag_type is remote::pgvector." + "stores": { + "items": { + "$ref": "#/components/schemas/RagStore" + }, + "type": "array", + "title": "BYOK RAG stores", + "description": "List of BYOK RAG store configurations." } }, "additionalProperties": false, "type": "object", - "required": [ - "rag_id", - "vector_db_id" - ], - "title": "ByokRag", - "description": "BYOK (Bring Your Own Knowledge) RAG configuration." + "title": "ByokConfiguration", + "description": "BYOK (Bring Your Own Knowledge) configuration." }, "CORSConfiguration": { "properties": { @@ -12309,14 +12225,6 @@ "title": "Approvals configuration", "description": "Settings for human-in-the-loop approval of MCP tool invocations" }, - "byok_rag": { - "items": { - "$ref": "#/components/schemas/ByokRag" - }, - "type": "array", - "title": "BYOK RAG configuration", - "description": "BYOK RAG configuration. This configuration can be used to reconfigure Llama Stack through its run.yaml configuration file" - }, "a2a_state": { "$ref": "#/components/schemas/A2AStateConfiguration", "title": "A2A state configuration", @@ -12363,17 +12271,7 @@ "rag": { "$ref": "#/components/schemas/RagConfiguration", "title": "RAG configuration", - "description": "Configuration for all RAG strategies (inline and tool-based)." - }, - "okp": { - "$ref": "#/components/schemas/OkpConfiguration", - "title": "OKP configuration", - "description": "OKP provider settings. Only used when 'okp' is listed in rag.inline or rag.tool." - }, - "reranker": { - "$ref": "#/components/schemas/RerankerConfiguration", - "title": "Reranker configuration", - "description": "Configuration for neural reranking of RAG chunks using cross-encoder." + "description": "Unified RAG configuration: BYOK stores, OKP provider, and retrieval strategies (inline and tool-based)." }, "skills": { "anyOf": [ @@ -12426,7 +12324,6 @@ "authorization": { "access_rules": [] }, - "byok_rag": [], "conversation_cache": {}, "database": { "sqlite": { @@ -12457,6 +12354,26 @@ "period": 1 } }, + "rag": { + "byok": { + "max_chunks": 10, + "stores": [] + }, + "okp": { + "max_chunks": 5, + "offline": true + }, + "retrieval": { + "inline": { + "max_chunks": 10, + "sources": [] + }, + "tool": { + "max_chunks": 10, + "sources": [] + } + } + }, "service": { "access_log": true, "auth_enabled": false, @@ -15066,12 +14983,19 @@ ], "title": "OKP chunk filter query", "description": "Additional OKP filter query applied to every OKP search request. Use Solr boolean syntax, e.g. 'product:ansible AND product:*openshift*'." + }, + "max_chunks": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Max OKP chunks", + "description": "Maximum number of chunks fetched from OKP.", + "default": 5 } }, "additionalProperties": false, "type": "object", "title": "OkpConfiguration", - "description": "OKP (Offline Knowledge Portal) provider configuration.\n\nControls provider-specific behaviour for the OKP vector store.\nOnly relevant when ``\"okp\"`` is listed in ``rag.inline`` or ``rag.tool``." + "description": "OKP (Offline Knowledge Portal) provider configuration.\n\nControls provider-specific behaviour for the OKP vector store.\nOnly relevant when ``\"okp\"`` is listed in ``rag.retrieval.inline.sources``\nor ``rag.retrieval.tool.sources``." }, "OpenAIResponseAnnotationCitation": { "properties": { @@ -18070,27 +17994,152 @@ }, "RagConfiguration": { "properties": { - "inline": { - "items": { - "type": "string" - }, - "type": "array", - "title": "Inline RAG IDs", - "description": "RAG IDs whose sources are injected as context before the LLM call. Use 'okp' to enable OKP inline RAG. Empty by default (no inline RAG)." + "byok": { + "$ref": "#/components/schemas/ByokConfiguration", + "title": "BYOK configuration", + "description": "Bring Your Own Knowledge store configurations and settings." }, - "tool": { - "items": { - "type": "string" - }, - "type": "array", - "title": "Tool RAG IDs", - "description": "RAG IDs made available to the LLM as a file_search tool. Use 'okp' to include the OKP vector store. When omitted, all registered BYOK vector stores are used (backward compatibility)." + "okp": { + "$ref": "#/components/schemas/OkpConfiguration", + "title": "OKP configuration", + "description": "OKP provider settings. Only used when 'okp' is listed in retrieval.inline.sources or retrieval.tool.sources." + }, + "retrieval": { + "$ref": "#/components/schemas/RetrievalConfiguration", + "title": "Retrieval configuration", + "description": "Inline and tool retrieval strategy settings." } }, "additionalProperties": false, "type": "object", "title": "RagConfiguration", - "description": "RAG strategy configuration.\n\nControls which RAG sources are used for inline and tool-based retrieval.\n\nEach strategy lists RAG IDs to include. The special ID ``\"okp\"`` defined in constants,\nactivates the OKP provider; all other IDs refer to entries in ``byok_rag``.\n\nBackward compatibility:\n - ``inline`` defaults to ``[]`` (no inline RAG).\n - ``tool`` defaults to ``[]`` (no tool RAG).\n\nIf no RAG strategy is defined (inline and tool are empty),\nthe RAG tool will register all stores available to llama-stack." + "description": "Unified RAG configuration.\n\nGroups all RAG-related settings: BYOK stores, OKP provider, and\nretrieval strategies (inline and tool)." + }, + "RagStore": { + "properties": { + "rag_id": { + "type": "string", + "minLength": 1, + "title": "RAG ID", + "description": "Unique RAG ID" + }, + "backend": { + "type": "string", + "minLength": 1, + "title": "RAG type", + "description": "Type of RAG database (e.g. 'inline::faiss', 'remote::pgvector').", + "default": "faiss" + }, + "embedding_model": { + "type": "string", + "minLength": 1, + "title": "Embedding model", + "description": "Embedding model identification", + "default": "sentence-transformers/all-mpnet-base-v2" + }, + "embedding_dimension": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Embedding dimension", + "description": "Dimensionality of embedding vectors.", + "default": 768 + }, + "vector_db_id": { + "type": "string", + "minLength": 1, + "title": "Vector DB ID", + "description": "Vector database identification." + }, + "db_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "DB path", + "description": "Path to RAG database. Required for inline::faiss." + }, + "score_multiplier": { + "type": "number", + "exclusiveMinimum": 0.0, + "title": "Score multiplier", + "description": "Multiplier applied to relevance scores from this vector store. Used to weight results when querying multiple knowledge sources. Values > 1 boost this store's results; values < 1 reduce them.", + "default": 1.0 + }, + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "PostgreSQL host", + "description": "PostgreSQL host for remote::pgvector. Defaults to ${env.POSTGRES_HOST} when backend is pgvector." + }, + "port": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "PostgreSQL port", + "description": "PostgreSQL port for remote::pgvector. Defaults to ${env.POSTGRES_PORT} when backend is pgvector." + }, + "db": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "PostgreSQL database", + "description": "PostgreSQL database name for remote::pgvector. Defaults to ${env.POSTGRES_DATABASE} when backend is pgvector." + }, + "user": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "PostgreSQL user", + "description": "PostgreSQL user for remote::pgvector. Defaults to ${env.POSTGRES_USER} when backend is pgvector." + }, + "password": { + "anyOf": [ + { + "type": "string", + "format": "password", + "writeOnly": true + }, + { + "type": "null" + } + ], + "title": "PostgreSQL password", + "description": "PostgreSQL password for remote::pgvector. Defaults to ${env.POSTGRES_PASSWORD} when backend is pgvector." + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "rag_id", + "vector_db_id" + ], + "title": "RagStore", + "description": "BYOK (Bring Your Own Knowledge) RAG store configuration." }, "ReadinessResponse": { "properties": { @@ -19041,6 +19090,59 @@ ], "sse_example": "event: response.created\ndata: {\"type\":\"response.created\",\"sequence_number\":0,\"response\":{\"id\":\"resp_abc\",\"object\":\"response\",\"created_at\":1704067200,\"status\":\"in_progress\",\"model\":\"openai/gpt-4o-mini\",\"output\":[],\"store\":true,\"text\":{\"format\":{\"type\":\"text\"}},\"conversation\":\"0d21ba731f21f798dc9680125d5d6f49\",\"available_quotas\":{},\"output_text\":\"\"}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":1,\"response_id\":\"resp_abc\",\"output_index\":0,\"item\":{\"id\":\"msg_abc\",\"type\":\"message\",\"status\":\"in_progress\",\"role\":\"assistant\",\"content\":[]}}\n\n...\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"sequence_number\":30,\"response\":{\"id\":\"resp_abc\",\"object\":\"response\",\"created_at\":1704067200,\"status\":\"completed\",\"model\":\"openai/gpt-4o-mini\",\"output\":[{\"id\":\"msg_abc\",\"type\":\"message\",\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Hello! How can I help?\",\"annotations\":[]}]}],\"store\":true,\"text\":{\"format\":{\"type\":\"text\"}},\"usage\":{\"input_tokens\":10,\"output_tokens\":6,\"total_tokens\":16,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens_details\":{\"reasoning_tokens\":0}},\"conversation\":\"0d21ba731f21f798dc9680125d5d6f49\",\"available_quotas\":{\"daily\":1000,\"monthly\":50000},\"output_text\":\"Hello! How can I help?\"}}\n\ndata: [DONE]\n\n" }, + "RetrievalConfiguration": { + "properties": { + "inline": { + "$ref": "#/components/schemas/RetrievalStrategyConfiguration", + "title": "Inline retrieval", + "description": "Inline RAG: context injected before the LLM request." + }, + "tool": { + "$ref": "#/components/schemas/RetrievalStrategyConfiguration", + "title": "Tool retrieval", + "description": "Tool RAG: LLM can call file_search on demand." + } + }, + "additionalProperties": false, + "type": "object", + "title": "RetrievalConfiguration", + "description": "Configuration for inline and tool retrieval strategies." + }, + "RetrievalStrategyConfiguration": { + "properties": { + "sources": { + "items": { + "type": "string" + }, + "type": "array", + "title": "RAG source IDs", + "description": "RAG IDs to use for this retrieval strategy. Use 'okp' to include the OKP vector store." + }, + "max_chunks": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Max chunks", + "description": "Maximum number of chunks returned by this retrieval strategy.", + "default": 10 + }, + "reranker": { + "anyOf": [ + { + "$ref": "#/components/schemas/RerankerConfiguration" + }, + { + "type": "null" + } + ], + "title": "Reranker configuration", + "description": "Neural reranking of RAG chunks using cross-encoder. Only applicable to inline retrieval." + } + }, + "additionalProperties": false, + "type": "object", + "title": "RetrievalStrategyConfiguration", + "description": "Configuration for a single retrieval strategy (inline or tool)." + }, "RlsapiV1Attachment": { "properties": { "contents": { diff --git a/docs/rag_guide.md b/docs/rag_guide.md index 2aa3a0c87..6d6624322 100644 --- a/docs/rag_guide.md +++ b/docs/rag_guide.md @@ -34,6 +34,33 @@ Lightspeed Core Stack (LCS) supports two complementary RAG strategies: Both strategies can be enabled independently via the `rag` section of `lightspeed-stack.yaml`. See [BYOK Feature Documentation](byok_guide.md) for configuration details. +### Inline RAG chunk flow + +```mermaid +flowchart TD + subgraph Sources["Source Fetching"] + B1["BYOK Store 1"] --> BPool + B2["BYOK Store 2"] --> BPool + BN["BYOK Store N"] --> BPool + BPool["BYOK Pool\ncapped at rag.byok.max_chunks"] + OKP["OKP (Solr)\ncapped at rag.okp.max_chunks"] + end + + BPool --> Pool["Merged Pool\n(all chunks, sorted by score)"] + OKP --> Pool + + Pool --> Decision{Reranker\nenabled?} + + Decision -->|Yes| Rerank["Cross-Encoder Rerank\n+ BYOK score boost"] + Decision -->|No| Cut + + Rerank --> Cut["Top K cut\nrag.retrieval.inline.max_chunks"] + + Cut --> Context["Final Inline RAG Context"] +``` + +Each BYOK store is queried in parallel, and the merged BYOK results are capped at `rag.byok.max_chunks` total. OKP fetches up to `rag.okp.max_chunks`. Together these form the reranking pool. If the reranker is enabled, the full pool is reranked with a cross-encoder and BYOK score boosts are applied. The result is capped at `rag.retrieval.inline.max_chunks`. + The **Embedding Model** is used to convert queries and documents into vector representations for similarity matching. > [!NOTE] @@ -57,32 +84,28 @@ Use the [`rag-content`](https://github.com/lightspeed-core/rag-content) reposito Download a local embedding model such as `sentence-transformers/all-mpnet-base-v2` by using the script in [`rag-content`](https://github.com/lightspeed-core/rag-content) or manually download and place in your desired path. > [!NOTE] -> The embedding model can also be downloaded automatically at first start-up (which will be slower). In the `byok_rag` section of `lightspeed-stack.yaml`, specify a supported model name as `embedding_model` instead of a local path. The model will be downloaded to the `~/.cache/huggingface/hub` folder. +> The embedding model can also be downloaded automatically at first start-up (which will be slower). In the `rag.byok.stores` section of `lightspeed-stack.yaml`, specify a supported model name as `embedding_model` instead of a local path. The model will be downloaded to the `~/.cache/huggingface/hub` folder. --- ## Configure BYOK Knowledge Sources -> [!WARNING] -> **Deprecated in 0.7.0**: The top-level `byok_rag`, `rag`, `okp`, and `reranker` sections -> are deprecated. In 0.7.0, all RAG-related configuration is unified under a single `rag` -> section: stores move to `rag.byok.stores` (with `backend` instead of `rag_type`), -> retrieval strategies move to `rag.retrieval.inline`/`rag.retrieval.tool`, OKP moves to -> `rag.okp`, and the reranker moves to `rag.retrieval.inline.reranker`. -> See the [v0.7.0 Migration Guide](migrations/v0.7.0.md) for full details and examples. -BYOK knowledge sources are configured in the `byok_rag` section of `lightspeed-stack.yaml`. The required configuration is automatically generated at startup when using `make run`, `make run-stack`, `docker-compose`, or library mode — no manual enrichment is needed. + +BYOK knowledge sources are configured in the `rag.byok.stores` section of `lightspeed-stack.yaml`. The required configuration is automatically generated at startup when using `make run`, `make run-stack`, `docker-compose`, or library mode — no manual enrichment is needed. ### FAISS example ```yaml -byok_rag: - - rag_id: custom-index - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 # or path to local model - embedding_dimension: 768 - vector_db_id: vs_8c94967b-81cc-4028-a294-9cfac6fd9ae2 # Generated by rag-content during index creation - db_path: # e.g. /home/USER/vector_db/faiss_store.db +rag: + byok: + stores: + - rag_id: custom-index + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 # or path to local model + embedding_dimension: 768 + vector_db_id: vs_8c94967b-81cc-4028-a294-9cfac6fd9ae2 # Generated by rag-content during index creation + db_path: # e.g. /home/USER/vector_db/faiss_store.db ``` Where: @@ -111,17 +134,19 @@ Each pgvector-backed table follows this schema: > The `vector_store_id` (e.g. `rhdocs`) is used to point to the table named `vector_store_rhdocs` in the specified database, which stores the vector embeddings. ```yaml -byok_rag: - - rag_id: pgvector-example - rag_type: remote::pgvector - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: rhdocs # becomes PostgreSQL table 'vector_store_rhdocs' - host: ${env.POSTGRES_HOST} - port: ${env.POSTGRES_PORT} - db: ${env.POSTGRES_DATABASE} - user: ${env.POSTGRES_USER} - password: ${env.POSTGRES_PASSWORD} +rag: + byok: + stores: + - rag_id: pgvector-example + backend: pgvector + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: rhdocs # becomes PostgreSQL table 'vector_store_rhdocs' + host: ${env.POSTGRES_HOST} + port: ${env.POSTGRES_PORT} + db: ${env.POSTGRES_DATABASE} + user: ${env.POSTGRES_USER} + password: ${env.POSTGRES_PASSWORD} ``` > [!NOTE] @@ -233,21 +258,23 @@ The OKP (Offline Knowledge Portal) Solr Vector IO is a read-only vector search p ```yaml rag: - inline: - - okp # inject OKP context before the LLM request - tool: - - okp # expose OKP as the file_search tool - -okp: - rhokp_url: ${env.RH_SERVER_OKP} # OKP base URL (env var or literal URL) - offline: true # true = use parent_id for source URLs (offline mode) - # false = use reference_url (online mode) + retrieval: + inline: + sources: + - okp # inject OKP context before the LLM request + tool: + sources: + - okp # expose OKP as the file_search tool + okp: + rhokp_url: ${env.RH_SERVER_OKP} # OKP base URL (env var or literal URL) + offline: true # true = use parent_id for source URLs (offline mode) + # false = use reference_url (online mode) ``` -Set `rhokp_url` to the base URL of your OKP server. Use `${env.RH_SERVER_OKP}` to read the URL from the environment; when omitted or empty, a default from the application constants is used. +Set `rhokp_url` to the base URL of your OKP server under `rag.okp`. Use `${env.RH_SERVER_OKP}` to read the URL from the environment; when omitted or empty, a default from the application constants is used. > [!NOTE] -> When `okp` is listed in `rag.inline` or `rag.tool`, Lightspeed Stack automatically enriches +> When `okp` is listed in `rag.retrieval.inline.sources` or `rag.retrieval.tool.sources`, Lightspeed Stack automatically enriches > the underlying configuration at startup with the required `vector_io` provider and `registered_resources` > entries for the OKP vector store. No manual registration is needed. @@ -273,14 +300,15 @@ curl -sX POST http://localhost:8080/v1/query \ **Query Filtering:** -To further filter the OKP context, set the `chunk_filter_query` field in the `okp` section of +To further filter the OKP context, set the `chunk_filter_query` field in the `rag.okp` section of `lightspeed-stack.yaml`. Filters follow the OKP key:value format and are applied as a static `fq` parameter on every OKP search request. ```yaml -okp: - rhokp_url: ${env.RH_SERVER_OKP} - chunk_filter_query: "product:*openshift*" +rag: + okp: + rhokp_url: ${env.RH_SERVER_OKP} + chunk_filter_query: "product:*openshift*" ``` Per-request filtering is also available on all inference endpoints via request field **`solr`**: `mode` (`semantic`, `hybrid`, or `lexical`) and `filters` (key:value format). Legacy payloads that omit `mode`/`filters` and send filter key:value pairs at the top level still work with `mode` set to `hybrid`. @@ -299,28 +327,24 @@ Example: **Prerequisites:** -- The OKP server must be running and accessible at the URL given in `okp.rhokp_url` (or `${env.RH_SERVER_OKP}`). +- The OKP server must be running and accessible at the URL given in `rag.okp.rhokp_url` (or `${env.RH_SERVER_OKP}`). For instructions on how to pull and run the OKP image, visit: https://github.com/lightspeed-core/lightspeed-providers/lightspeed_stack_providers/providers/remote/solr_vector_io/solr_vector_io/README.md **Chunk volume:** -> [!WARNING] -> **Deprecated in 0.7.0**: The chunk limit constants below are replaced by configurable -> fields in `lightspeed-stack.yaml` (`rag.byok.max_chunks`, `rag.okp.max_chunks`, -> `rag.retrieval.inline.max_chunks`, `rag.retrieval.tool.max_chunks`). -> See the [v0.7.0 Migration Guide](migrations/v0.7.0.md) for details. OKP and BYOK scores are not directly comparable (different scoring systems), so -`score_multiplier` (a BYOK-only concept) does not apply to OKP results. To control -the number of retrieved chunks, set the constants in `src/constants.py`: +`score_multiplier` (a BYOK-only concept) does not apply to OKP results. However, when +the reranker is enabled, it normalizes scores across sources using a cross-encoder model. +To control the number of retrieved chunks, configure `max_chunks` in `lightspeed-stack.yaml`: -| Constant | Value | Description | -|----------|-------|-------------| -| `INLINE_RAG_MAX_CHUNKS` | 10 | Hard upper bound on the final merged inline RAG chunks (BYOK + OKP) delivered to the LLM | -| `OKP_RAG_MAX_CHUNKS` | 5 | Fetch hint for OKP (Inline RAG); controls how many chunks enter the reranking pool | -| `BYOK_RAG_MAX_CHUNKS` | 10 | Fetch hint for BYOK stores (Inline RAG); controls how many chunks enter the reranking pool | -| `TOOL_RAG_MAX_CHUNKS` | 10 | Max chunks retrieved via Tool RAG (`file_search`); independent from `INLINE_RAG_MAX_CHUNKS` | +| Config path | Default | Description | +|-------------|---------|-------------| +| `rag.retrieval.inline.max_chunks` | 10 | Hard upper bound on the final merged inline RAG chunks (BYOK + OKP) delivered to the LLM | +| `rag.okp.max_chunks` | 5 | Fetch limit for OKP (Inline RAG); controls how many chunks enter the reranking pool | +| `rag.byok.max_chunks` | 10 | Fetch limit for BYOK stores (Inline RAG); controls how many chunks enter the reranking pool | +| `rag.retrieval.tool.max_chunks` | 10 | Max chunks retrieved via Tool RAG (`file_search`); independent from inline max_chunks | **Limitations:** @@ -330,7 +354,7 @@ the number of retrieved chunks, set the constants in `src/constants.py`: # Complete Configuration Reference -To enable RAG functionality, configure the `byok_rag` and `rag` sections in your `lightspeed-stack.yaml`. +To enable RAG functionality, configure the `rag` section (including `rag.byok.stores` and `rag.retrieval`) in your `lightspeed-stack.yaml`. Below is an example of a working `lightspeed-stack.yaml` configuration with: @@ -348,22 +372,25 @@ service: port: 8080 auth_enabled: false -byok_rag: - - rag_id: ocp-docs - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_3a7f9b2e-45dc-4e1a-b8f2-1c9d0e3f5a6b - db_path: /home/USER/lightspeed-stack/vector_dbs/ocp_docs/faiss_store.db - rag: - inline: - - ocp-docs - tool: - - ocp-docs + byok: + stores: + - rag_id: ocp-docs + backend: faiss + embedding_model: sentence-transformers/all-mpnet-base-v2 + embedding_dimension: 768 + vector_db_id: vs_3a7f9b2e-45dc-4e1a-b8f2-1c9d0e3f5a6b + db_path: /home/USER/lightspeed-stack/vector_dbs/ocp_docs/faiss_store.db + retrieval: + inline: + sources: + - ocp-docs + tool: + sources: + - ocp-docs ``` -The BYOK vector store providers and registered resources are automatically generated at startup from the `byok_rag` entries above. Models and inference providers must be configured separately in your `run.yaml`. +The BYOK vector store providers and registered resources are automatically generated at startup from the `rag.byok.stores` entries above. Models and inference providers must be configured separately in your `run.yaml`. --- diff --git a/examples/lightspeed-stack-byok-okp-rag.yaml b/examples/lightspeed-stack-byok-okp-rag.yaml index 7cbb36fc8..08760dfa0 100644 --- a/examples/lightspeed-stack-byok-okp-rag.yaml +++ b/examples/lightspeed-stack-byok-okp-rag.yaml @@ -34,40 +34,45 @@ quota_handlers: scheduler: # scheduler ticks in seconds period: 10 -byok_rag: - - rag_id: ocp-docs # referenced in rag.inline / rag.tool - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_123 # Vector store ID (from index generation) - db_path: /tmp/ocp.faiss - score_multiplier: 1.0 # Weight for this vector store's results (Inline RAG only) - - rag_id: knowledge-base # referenced in rag.inline / rag.tool - rag_type: inline::faiss - embedding_model: sentence-transformers/all-mpnet-base-v2 - embedding_dimension: 768 - vector_db_id: vs_456 # Vector store ID (from index generation) - db_path: /tmp/kb.faiss - score_multiplier: 1.2 # Weight for this vector store's results (Inline RAG only) - # RAG configuration rag: - # Inline RAG: context injected before the LLM request from the listed sources - # List rag_ids from byok_rag, or 'okp' to include OKP - inline: - - ocp-docs - - knowledge-base - - okp - # Tool RAG: LLM can call file_search on demand to retrieve context - # List rag_ids from byok_rag, or 'okp' to include OKP - # Omit to use all registered BYOK stores (backward compatibility) - tool: - - ocp-docs - - knowledge-base + byok: + max_chunks: 10 # Max total chunks across all BYOK stores + stores: + - rag_id: ocp-docs # Referenced in retrieval.inline / retrieval.tool + backend: faiss + embedding_dimension: 1024 + vector_db_id: vs_123 # Llama-stack vector_store_id + db_path: /tmp/ocp.faiss + score_multiplier: 1.0 # Weight for this vector store's results (Inline RAG only) + - rag_id: knowledge-base # Referenced in retrieval.inline / retrieval.tool + backend: faiss + embedding_dimension: 384 + vector_db_id: vs_456 # Llama-stack vector_store_id + db_path: /tmp/kb.faiss + score_multiplier: 1.2 # Weight for this vector store's results (Inline RAG only) + + # OKP provider settings (only used when 'okp' is listed in retrieval sources) + okp: + offline: true # true = use parent_id for source URLs, false = use reference_url + max_chunks: 5 # Max chunks fetched from OKP + # Additional Solr filter query applied to every OKP search request. + # Use Solr boolean syntax + # chunk_filter_query: "product:*ansible* AND product:*openshift*" -# OKP provider settings (only used when 'okp' is listed in rag.inline or rag.tool) -okp: - offline: true # true = use parent_id for source URLs, false = use reference_url - # Additional Solr filter query applied to every OKP search request. - # Use Solr boolean syntax - # chunk_filter_query: "product:*ansible* AND product:*openshift*" + retrieval: + # Inline RAG: context injected before the LLM request from the listed sources + # List rag_ids from byok stores, or 'okp' to include OKP + inline: + sources: + - ocp-docs + - knowledge-base + - okp + max_chunks: 10 # Cap on merged inline result + # Tool RAG: LLM can call file_search on demand to retrieve context + # List rag_ids from byok stores, or 'okp' to include OKP + tool: + sources: + - ocp-docs + - knowledge-base + max_chunks: 10 # Tool RAG limit diff --git a/examples/quota-limiter-configuration-sqlite.yaml b/examples/quota-limiter-configuration-sqlite.yaml index 2bceaafb7..300f2caa0 100644 --- a/examples/quota-limiter-configuration-sqlite.yaml +++ b/examples/quota-limiter-configuration-sqlite.yaml @@ -33,18 +33,6 @@ conversation_cache: ssl_mode: disable gss_encmode: disable -#byok_rag: -# - rag_id: ocp_docs -# rag_type: inline::faiss -# embedding_dimension: 1024 -# vector_db_id: vector_byok_1 -# db_path: /tmp/ocp.faiss -# - rag_id: knowledge_base -# rag_type: inline::faiss -# embedding_dimension: 384 -# vector_db_id: vector_byok_2 -# db_path: /tmp/kb.faiss - quota_handlers: sqlite: db_path: quota.sqlite From b39daa04a0cb48d0268360eff1723eaacd02194b Mon Sep 17 00:00:00 2001 From: are-ces <195810094+are-ces@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:01:25 +0200 Subject: [PATCH 4/5] LCORE-1426: restore removed comments and clean up dead constant - Restore upstream comments on PROVIDER_TYPE_MAP, API_KEY_FIELD_MAP, DEFAULT_BASELINE_RESOURCE that were lost during rebase - Remove unused DEFAULT_RAG_TYPE constant - Add clarifying comment on chunk limit defaults Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/openapi.json | 2 +- src/constants.py | 7 +++---- src/llama_stack_configuration.py | 8 ++++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/openapi.json b/docs/openapi.json index 306664c30..f6199181e 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -18026,7 +18026,7 @@ "backend": { "type": "string", "minLength": 1, - "title": "RAG type", + "title": "RAG backend", "description": "Type of RAG database (e.g. 'inline::faiss', 'remote::pgvector').", "default": "faiss" }, diff --git a/src/constants.py b/src/constants.py index f2847584f..02bf4a95b 100644 --- a/src/constants.py +++ b/src/constants.py @@ -190,9 +190,6 @@ # Default RAG backend for bring-your-own-knowledge RAG configurations DEFAULT_RAG_BACKEND: Final[str] = "faiss" -# Default RAG type (fully-qualified Llama Stack provider type) -DEFAULT_RAG_TYPE: Final[str] = "inline::faiss" - # Default sentence transformer model for embedding generation, that type needs # to be supported by Llama Stack and configured properly in providers and # models sections @@ -208,7 +205,9 @@ USER_QUOTA_LIMITER: Final[str] = "user_limiter" CLUSTER_QUOTA_LIMITER: Final[str] = "cluster_limiter" -# Default chunk limits (used as Pydantic field defaults in RagConfiguration) +# Default chunk limits (used as Pydantic field defaults in RagConfiguration). +# These replace the old hardcoded INLINE_RAG_MAX_CHUNKS, TOOL_RAG_MAX_CHUNKS, +# BYOK_RAG_MAX_CHUNKS, and OKP_RAG_MAX_CHUNKS constants. DEFAULT_INLINE_RAG_MAX_CHUNKS: Final[int] = 10 DEFAULT_TOOL_RAG_MAX_CHUNKS: Final[int] = 10 DEFAULT_BYOK_RAG_MAX_CHUNKS: Final[int] = 10 diff --git a/src/llama_stack_configuration.py b/src/llama_stack_configuration.py index 14a0f6964..ada0464c6 100644 --- a/src/llama_stack_configuration.py +++ b/src/llama_stack_configuration.py @@ -34,6 +34,10 @@ logger = get_logger(__name__) +# Maps a UnifiedInferenceProvider.type (canonical, backend-agnostic vocabulary) +# to the Llama Stack provider_type emitted by apply_high_level_inference. The +# completeness of this map against UnifiedInferenceProvider.type is asserted by +# a unit test so a new Literal value cannot be added without a mapping. PROVIDER_TYPE_MAP: dict[str, str] = { "openai": "remote::openai", "ollama": "remote::ollama", @@ -46,10 +50,14 @@ "vllm_rhel_ai": "remote::vllm", } +# Maps Llama Stack provider_type -> config field name for the auth token. +# Providers not listed default to "api_key". API_KEY_FIELD_MAP: dict[str, str] = { "remote::vllm": "api_token", } +# Package-relative path to the built-in default baseline run.yaml shipped with +# LCORE, used when unified mode selects baseline "default" without a profile. DEFAULT_BASELINE_RESOURCE: Path = Path(__file__).parent / "data" / "default_run.yaml" VECTOR_IO_TEMPLATES: dict[str, dict[str, Any]] = { From ce33ba1ce655fd6b24f12558a5bb9b390751fdce Mon Sep 17 00:00:00 2001 From: are-ces <195810094+are-ces@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:35:40 +0200 Subject: [PATCH 5/5] LCORE-1426: fix test_models_dumper schema names after rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update expected schema names: ByokRag → ByokConfiguration, add RagStore, RetrievalConfiguration, RetrievalStrategyConfiguration. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/unit/utils/test_models_dumper.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit/utils/test_models_dumper.py b/tests/unit/utils/test_models_dumper.py index 113e28a12..35e261d5e 100644 --- a/tests/unit/utils/test_models_dumper.py +++ b/tests/unit/utils/test_models_dumper.py @@ -7585,7 +7585,7 @@ def test_dump_models(tmpdir: Path) -> None: "AuthorizationConfiguration", "AuthorizedResponse", "AzureEntraIdConfiguration", - "ByokRag", + "ByokConfiguration", "CORSConfiguration", "CompactionConfiguration", "Configuration", @@ -7694,9 +7694,12 @@ def test_dump_models(tmpdir: Path) -> None: "RAGListResponse", "RHIdentityConfiguration", "RagConfiguration", + "RagStore", "ReadinessResponse", "ReferencedDocument", "RerankerConfiguration", + "RetrievalConfiguration", + "RetrievalStrategyConfiguration", "ResponseInput", "ResponseItem", "ResponsesRequest",