From 5c9639bbbb93f6414e8d07db226933dbc8455bf1 Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Fri, 21 Aug 2026 13:14:05 +0300 Subject: [PATCH] Move ConfigMismatchError to the store exceptions module The CLI reports this error, and importing it from the settings repository pulled lancedb onto the CLI's import path. Deferring the import into cli() bought nothing, since cli() runs on every invocation: it cost about 1.9 seconds on a cold start, `--help` included. It now sits beside the other store exceptions, in a module that imports nothing, and every importer points there. --- haiku_rag_slim/haiku/rag/cli.py | 5 +---- haiku_rag_slim/haiku/rag/client/__init__.py | 6 ++---- haiku_rag_slim/haiku/rag/client/rebuild.py | 2 +- haiku_rag_slim/haiku/rag/store/__init__.py | 2 ++ haiku_rag_slim/haiku/rag/store/exceptions.py | 6 ++++++ haiku_rag_slim/haiku/rag/store/repositories/settings.py | 7 +------ tests/store/test_read_only.py | 2 +- tests/test_cli.py | 2 +- tests/test_mcp.py | 2 +- tests/test_rebuild.py | 2 +- tests/test_settings.py | 2 +- 11 files changed, 18 insertions(+), 20 deletions(-) diff --git a/haiku_rag_slim/haiku/rag/cli.py b/haiku_rag_slim/haiku/rag/cli.py index b6d3539c..c7ab75cd 100644 --- a/haiku_rag_slim/haiku/rag/cli.py +++ b/haiku_rag_slim/haiku/rag/cli.py @@ -23,6 +23,7 @@ from haiku.rag.config import ( # noqa: E402 from haiku.rag.logging import configure_cli_logging # noqa: E402 from haiku.rag.store.exceptions import ( # noqa: E402 AmbiguousDatabaseError, + ConfigMismatchError, MigrationRequiredError, ReadOnlyError, SourceUnavailableError, @@ -42,10 +43,6 @@ _cli = typer.Typer( def cli(): - # Imported here rather than at module scope: the settings module pulls in - # lancedb, and the CLI's startup must not pay for it. - from haiku.rag.store.repositories.settings import ConfigMismatchError - try: _cli() except ( diff --git a/haiku_rag_slim/haiku/rag/client/__init__.py b/haiku_rag_slim/haiku/rag/client/__init__.py index bbc59db5..8003381f 100644 --- a/haiku_rag_slim/haiku/rag/client/__init__.py +++ b/haiku_rag_slim/haiku/rag/client/__init__.py @@ -20,6 +20,7 @@ from haiku.rag.converters import get_converter from haiku.rag.reranking import get_reranker from haiku.rag.store.engine import Store from haiku.rag.store.exceptions import ( + ConfigMismatchError, MigrationRequiredError, ReadOnlyError, SourceUnavailableError, @@ -30,10 +31,7 @@ from haiku.rag.store.models.document_item import extract_items from haiku.rag.store.repositories.chunk import ChunkRepository from haiku.rag.store.repositories.document import DocumentRepository from haiku.rag.store.repositories.document_item import DocumentItemRepository -from haiku.rag.store.repositories.settings import ( - ConfigMismatchError, - SettingsRepository, -) +from haiku.rag.store.repositories.settings import SettingsRepository from haiku.rag.utils import escape_sql_string, locate_database if TYPE_CHECKING: diff --git a/haiku_rag_slim/haiku/rag/client/rebuild.py b/haiku_rag_slim/haiku/rag/client/rebuild.py index 6b1c568b..3f5f6b3f 100644 --- a/haiku_rag_slim/haiku/rag/client/rebuild.py +++ b/haiku_rag_slim/haiku/rag/client/rebuild.py @@ -161,7 +161,7 @@ async def _set_embedder(client: "HaikuRAG") -> None: usable, so just the recorded provider/name are updated. A changed dimension requires regenerating every embedding via a full rebuild. """ - from haiku.rag.store.repositories.settings import ConfigMismatchError + from haiku.rag.store.exceptions import ConfigMismatchError settings_repo = SettingsRepository(client.store) stored = await settings_repo.get_current_settings() diff --git a/haiku_rag_slim/haiku/rag/store/__init__.py b/haiku_rag_slim/haiku/rag/store/__init__.py index d8edff9e..81521f90 100644 --- a/haiku_rag_slim/haiku/rag/store/__init__.py +++ b/haiku_rag_slim/haiku/rag/store/__init__.py @@ -1,5 +1,6 @@ from .exceptions import ( AmbiguousDatabaseError, + ConfigMismatchError, MigrationRequiredError, ReadOnlyError, SourceUnavailableError, @@ -12,5 +13,6 @@ __all__ = [ "MigrationRequiredError", "ReadOnlyError", "AmbiguousDatabaseError", + "ConfigMismatchError", "SourceUnavailableError", ] diff --git a/haiku_rag_slim/haiku/rag/store/exceptions.py b/haiku_rag_slim/haiku/rag/store/exceptions.py index 61a5b55c..3ad60070 100644 --- a/haiku_rag_slim/haiku/rag/store/exceptions.py +++ b/haiku_rag_slim/haiku/rag/store/exceptions.py @@ -4,6 +4,12 @@ class ReadOnlyError(Exception): pass +class ConfigMismatchError(Exception): + """Raised when stored config doesn't match current config.""" + + pass + + class MigrationRequiredError(Exception): """Database requires migration. Run 'haiku-rag migrate' to upgrade.""" diff --git a/haiku_rag_slim/haiku/rag/store/repositories/settings.py b/haiku_rag_slim/haiku/rag/store/repositories/settings.py index a8b2c16c..80ff54d7 100644 --- a/haiku_rag_slim/haiku/rag/store/repositories/settings.py +++ b/haiku_rag_slim/haiku/rag/store/repositories/settings.py @@ -2,17 +2,12 @@ import json import logging from haiku.rag.store.engine import Store +from haiku.rag.store.exceptions import ConfigMismatchError from haiku.rag.store.schema import SettingsRecord, query_to_pydantic logger = logging.getLogger(__name__) -class ConfigMismatchError(Exception): - """Raised when stored config doesn't match current config.""" - - pass - - class SettingsRepository: """Repository for Settings operations.""" diff --git a/tests/store/test_read_only.py b/tests/store/test_read_only.py index 7ff281b1..26e74b1d 100644 --- a/tests/store/test_read_only.py +++ b/tests/store/test_read_only.py @@ -394,7 +394,7 @@ class TestAppReadVerbsDoNotWrite: """A write CLI verb opens writable: drift raises before any write.""" from haiku.rag.app import HaikuRAGApp from haiku.rag.config import AppConfig - from haiku.rag.store.repositories.settings import ConfigMismatchError + from haiku.rag.store.exceptions import ConfigMismatchError async with Store(temp_db_path, create=True) as store: stored_name_before = ( diff --git a/tests/test_cli.py b/tests/test_cli.py index 17c1df41..597fe2a4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -332,7 +332,7 @@ class TestCliConfigMismatchError: def test_a_config_mismatch_exits_with_its_remedy(self): """The message says which database and what to run, so it is worth more than a traceback.""" - from haiku.rag.store.repositories.settings import ConfigMismatchError + from haiku.rag.store.exceptions import ConfigMismatchError with patch("haiku.rag.cli._cli") as mock_cli: mock_cli.side_effect = ConfigMismatchError( diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 860bf2a7..191549f9 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -645,7 +645,7 @@ class TestMCPClientLifetime: read-only mode and raises in writable mode. The MCP server no longer opts out of it for deletion.""" from haiku.rag.config import get_config - from haiku.rag.store.repositories.settings import ConfigMismatchError + from haiku.rag.store.exceptions import ConfigMismatchError drifted = get_config().model_copy(deep=True) drifted.embeddings.model.name = "a-different-model" diff --git a/tests/test_rebuild.py b/tests/test_rebuild.py index 971f797b..f9b0e951 100644 --- a/tests/test_rebuild.py +++ b/tests/test_rebuild.py @@ -1059,7 +1059,7 @@ async def test_rebuild_set_embedder_works_on_empty_database(temp_db_path): async def test_rebuild_set_embedder_raises_on_vector_dim_mismatch(temp_db_path): """SET_EMBEDDER refuses when the vector dimension changed — a full rebuild is needed.""" from haiku.rag.config import AppConfig - from haiku.rag.store.repositories.settings import ConfigMismatchError + from haiku.rag.store.exceptions import ConfigMismatchError async with HaikuRAG(temp_db_path, create=True): pass diff --git a/tests/test_settings.py b/tests/test_settings.py index 704b9eda..7b271d39 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -3,7 +3,7 @@ import logging import pytest from haiku.rag.config import AppConfig, get_config -from haiku.rag.store.repositories.settings import ConfigMismatchError +from haiku.rag.store.exceptions import ConfigMismatchError @pytest.mark.asyncio