Merge pull request #352 from ggozad/fix/citations-tui

fix chat TUI citation rendering after state flattening
This commit is contained in:
Yiorgis Gozadinos 2026-04-24 13:58:38 +03:00 committed by GitHub
commit 6a289a2bf3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 43 additions and 6 deletions

View file

@ -1,6 +1,10 @@
# Changelog
## [Unreleased]
### Fixed
- **Chat TUI now renders citations again.** After the 0.42.1 flattening of skill state `citations` to `list[str]`, the TUI still indexed `citations[-1]` and iterated the resulting chunk-id string character-by-character, so no citations resolved through `citation_index` and the citation panel stayed empty. Fixed by iterating `state.citations` directly.
## [0.42.1] - 2026-04-22
### Changed

View file

@ -328,13 +328,11 @@ class ChatApp(App):
state = self._toolset.get_namespace(namespace)
if not state:
continue
citation_turns = getattr(state, "citations", [])
cited_ids = getattr(state, "citations", [])
citation_index = getattr(state, "citation_index", {})
if citation_turns:
latest_ids = citation_turns[-1]
for cid in latest_ids:
if cid in citation_index:
citations.append(citation_index[cid])
for cid in cited_ids:
if cid in citation_index:
citations.append(citation_index[cid])
if citations:
await chat_history.add_citations(citations)

View file

@ -276,6 +276,41 @@ async def test_citation_expand_collapse_with_enter(temp_db_path: Path):
assert citation_widget.collapsed is True
@pytest.mark.asyncio
async def test_show_citations_renders_from_flat_state(temp_db_path: Path):
"""Citations in state (flat list[str]) render into the chat history."""
from haiku.rag.agents.research.models import Citation
from haiku.rag.chat.app import RAG_STATE_NAMESPACE
from haiku.rag.chat.widgets.chat_history import ChatHistory, CitationWidget
app, mock_client = _make_app_with_state(temp_db_path)
with patch("haiku.rag.chat.app.HaikuRAG", return_value=mock_client):
async with app.run_test() as pilot:
rag_state = app._toolset.get_namespace(RAG_STATE_NAMESPACE)
assert isinstance(rag_state, RAGState)
citation = Citation(
index=1,
document_id="doc1",
chunk_id="chunk1",
document_uri="file:///test/doc1.pdf",
document_title="Test Document",
page_numbers=[1],
content="Cited content",
)
rag_state.citation_index["chunk1"] = citation
rag_state.citations.append("chunk1")
chat_history = app.query_one(ChatHistory)
await app._show_citations_and_programs(chat_history)
await pilot.pause()
widgets = list(chat_history.query(CitationWidget))
assert len(widgets) == 1
assert widgets[0].citation.chunk_id == "chunk1"
@pytest.mark.asyncio
async def test_document_filter_updates_rag_state(temp_db_path: Path):
"""Test that selecting document filters updates RAGState.document_filter."""