From cad472e9994b7bd20623b4833649a6604df33371 Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Thu, 27 Aug 2026 17:44:23 +0300 Subject: [PATCH] Let a config-only command work on no database `settings` and `download-models` read the configuration and open nothing, but resolved a scope to be constructed, so `--db-name nope` failed them over a selection they never use. `HaikuRAGApp` takes no scope for those, and asking it for one is an error rather than a silent default. --- haiku_rag_slim/haiku/rag/app.py | 15 ++++++++--- haiku_rag_slim/haiku/rag/cli.py | 8 ++---- tests/test_cli.py | 44 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/haiku_rag_slim/haiku/rag/app.py b/haiku_rag_slim/haiku/rag/app.py index 54ced988..472c3bd2 100644 --- a/haiku_rag_slim/haiku/rag/app.py +++ b/haiku_rag_slim/haiku/rag/app.py @@ -35,16 +35,25 @@ logger = logging.getLogger(__name__) class HaikuRAGApp: def __init__( self, - scope: "DatabaseScope", + scope: "DatabaseScope | None" = None, config: AppConfig | None = None, read_only: bool = False, ): - """The databases this command works on, resolved by whoever built it.""" - self.scope = scope + """The databases this command works on, resolved by whoever built it. + + `scope` is None for configuration-only commands. + """ + self._scope = scope self.config = config if config is not None else get_config() self.read_only = read_only self.console = Console() + @property + def scope(self) -> "DatabaseScope": + """The databases this command works on.""" + assert self._scope is not None, "this command works on no database" + return self._scope + @property def _one(self) -> "DatabaseRef": """The one database this command works on. diff --git a/haiku_rag_slim/haiku/rag/cli.py b/haiku_rag_slim/haiku/rag/cli.py index 7a97d9ff..a5e2324c 100644 --- a/haiku_rag_slim/haiku/rag/cli.py +++ b/haiku_rag_slim/haiku/rag/cli.py @@ -461,9 +461,7 @@ def settings(): config = get_config() # Neither of these opens a database; the scope is whatever is configured. - app = HaikuRAGApp( - scope=resolve_scope(covers_set=True), config=config, read_only=True - ) + app = HaikuRAGApp(config=config, read_only=True) app.show_settings() @@ -781,9 +779,7 @@ def tag_restore( def download_models_cmd(): from haiku.rag.app import HaikuRAGApp - app = HaikuRAGApp( - scope=resolve_scope(covers_set=True), config=get_config(), read_only=True - ) + app = HaikuRAGApp(config=get_config(), read_only=True) try: asyncio.run(app.download_models()) except Exception as e: diff --git a/tests/test_cli.py b/tests/test_cli.py index 757064d5..375aa556 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -326,6 +326,50 @@ class TestSelectingADatabaseByName: assert cli_module._db_name is None +class TestCommandsThatWorkOnNoDatabase: + """`settings` and `download-models` read the configuration. A name that + selects a database is nothing to them, including a wrong one.""" + + def test_settings_ignores_an_unknown_name(self, tmp_path, monkeypatch): + import haiku.rag.config as config_module + + monkeypatch.setattr(config_module, "_config", None) + config_file = tmp_path / "haiku.rag.yaml" + config_file.write_text("lancedb:\n databases:\n alpha: /data/a.lancedb\n") + + result = runner.invoke( + cli, ["--config", str(config_file), "--db-name", "nope", "settings"] + ) + + assert result.exit_code == 0, result.output + assert "haiku.rag configuration" in result.output + + def test_download_models_ignores_an_unknown_name(self, tmp_path, monkeypatch): + import haiku.rag.config as config_module + + monkeypatch.setattr(config_module, "_config", None) + config_file = tmp_path / "haiku.rag.yaml" + config_file.write_text("lancedb:\n databases:\n alpha: /data/a.lancedb\n") + downloaded: list[object] = [] + + async def nothing_to_download(config): + downloaded.append(config) + return + yield # pragma: no cover - an empty async generator needs one + + monkeypatch.setattr( + "haiku.rag.client.downloads.download_models", nothing_to_download + ) + + result = runner.invoke( + cli, + ["--config", str(config_file), "--db-name", "nope", "download-models"], + ) + + assert result.exit_code == 0, result.output + assert len(downloaded) == 1 + + class TestResolvingTheDatabasePath: def test_a_path_wins_when_nothing_is_selected(self, monkeypatch): monkeypatch.setattr("haiku.rag.cli._db_name", None)