Surface database-open errors in the TUIs
ChatApp and InspectorApp assigned self.client before __aenter__ completed, so a failed open was masked by an AttributeError from on_unmount tearing down the never-opened client.
This commit is contained in:
parent
5f4c73f89f
commit
93824cdee5
5 changed files with 38 additions and 4 deletions
|
|
@ -8,6 +8,10 @@
|
||||||
- MCP `ask_question` and `analyze` tools accept `images_base64`.
|
- MCP `ask_question` and `analyze` tools accept `images_base64`.
|
||||||
- Chat TUI: `Ctrl+I` opens an image picker; attached images insert `[Image #N]` tokens in a multi-line prompt and are sent to the model with the message.
|
- Chat TUI: `Ctrl+I` opens an image picker; attached images insert `[Image #N]` tokens in a multi-line prompt and are sent to the model with the message.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Chat and inspector TUIs report the actual database-open error instead of an `AttributeError` from teardown.
|
||||||
|
|
||||||
## [0.69.0] - 2026-07-24
|
## [0.69.0] - 2026-07-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
|
|
@ -139,12 +139,15 @@ class ChatApp(App):
|
||||||
|
|
||||||
async def on_mount(self) -> None:
|
async def on_mount(self) -> None:
|
||||||
"""Initialize the app when mounted."""
|
"""Initialize the app when mounted."""
|
||||||
self.client = HaikuRAG(
|
client = HaikuRAG(
|
||||||
db_path=self.db_path,
|
db_path=self.db_path,
|
||||||
config=self.config,
|
config=self.config,
|
||||||
read_only=self.read_only,
|
read_only=self.read_only,
|
||||||
)
|
)
|
||||||
await self.client.__aenter__()
|
# Assign only after a successful open: on_unmount must not tear down
|
||||||
|
# a client whose __aenter__ failed.
|
||||||
|
await client.__aenter__()
|
||||||
|
self.client = client
|
||||||
|
|
||||||
self._agent = Agent(
|
self._agent = Agent(
|
||||||
self._model,
|
self._model,
|
||||||
|
|
|
||||||
|
|
@ -83,12 +83,15 @@ class InspectorApp(App):
|
||||||
async def on_mount(self) -> None:
|
async def on_mount(self) -> None:
|
||||||
"""Initialize the app when mounted."""
|
"""Initialize the app when mounted."""
|
||||||
config = get_config()
|
config = get_config()
|
||||||
self.client = HaikuRAG(
|
client = HaikuRAG(
|
||||||
db_path=self.db_path,
|
db_path=self.db_path,
|
||||||
config=config,
|
config=config,
|
||||||
read_only=self.read_only,
|
read_only=self.read_only,
|
||||||
)
|
)
|
||||||
await self.client.__aenter__()
|
# Assign only after a successful open: on_unmount must not tear down
|
||||||
|
# a client whose __aenter__ failed.
|
||||||
|
await client.__aenter__()
|
||||||
|
self.client = client
|
||||||
|
|
||||||
# Load initial documents
|
# Load initial documents
|
||||||
doc_list = self.query_one(DocumentList)
|
doc_list = self.query_one(DocumentList)
|
||||||
|
|
|
||||||
|
|
@ -400,3 +400,15 @@ async def test_document_filter_cleared_when_empty(temp_db_path: Path):
|
||||||
rag_state = RAGState.model_validate(app._state[RAG_STATE_NAMESPACE])
|
rag_state = RAGState.model_validate(app._state[RAG_STATE_NAMESPACE])
|
||||||
assert rag_state.document_filter is None
|
assert rag_state.document_filter is None
|
||||||
assert app._state["rag"]["document_filter"] is None
|
assert app._state["rag"]["document_filter"] is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_chat_app_open_failure_surfaces_real_error(tmp_path: Path):
|
||||||
|
"""A failed database open must surface its own error, not an
|
||||||
|
AttributeError from tearing down a client that never opened."""
|
||||||
|
from haiku.rag.chat.app import ChatApp
|
||||||
|
|
||||||
|
app = ChatApp(db_path=tmp_path / "missing.lancedb", capabilities=[])
|
||||||
|
with pytest.raises(FileNotFoundError):
|
||||||
|
async with app.run_test():
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -223,3 +223,15 @@ async def test_context_modal_suppresses_pictures_when_vision_disabled():
|
||||||
await pilot.pause()
|
await pilot.pause()
|
||||||
modal = app.screen
|
modal = app.screen
|
||||||
assert list(modal.query(TextualImage)) == []
|
assert list(modal.query(TextualImage)) == []
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_inspector_open_failure_surfaces_real_error(tmp_path):
|
||||||
|
"""A failed database open must surface its own error, not an
|
||||||
|
AttributeError from tearing down a client that never opened."""
|
||||||
|
from haiku.rag.inspector.app import InspectorApp
|
||||||
|
|
||||||
|
app = InspectorApp(db_path=tmp_path / "missing.lancedb", read_only=True)
|
||||||
|
with pytest.raises(FileNotFoundError):
|
||||||
|
async with app.run_test():
|
||||||
|
pass
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue