From b221b4ac0364f8262051916a2c3aa2bbe47ca2f9 Mon Sep 17 00:00:00 2001 From: Lawrence Akka Date: Tue, 18 Aug 2026 12:20:56 +0200 Subject: [PATCH] Do not duplicate metadata items in inspector --- .../rag/inspector/widgets/detail_view.py | 29 +++++++----- tests/test_inspector.py | 47 +++++++++++++++++-- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/haiku_rag_slim/haiku/rag/inspector/widgets/detail_view.py b/haiku_rag_slim/haiku/rag/inspector/widgets/detail_view.py index 15026c8c..690db227 100644 --- a/haiku_rag_slim/haiku/rag/inspector/widgets/detail_view.py +++ b/haiku_rag_slim/haiku/rag/inspector/widgets/detail_view.py @@ -4,7 +4,7 @@ from textual.app import ComposeResult from textual.containers import VerticalScroll from textual.widgets import Markdown, Static -from haiku.rag.store.models import Chunk, Document, SearchResult +from haiku.rag.store.models import Chunk, ChunkMetadata, Document, SearchResult class ProvenanceData(Protocol): @@ -50,6 +50,21 @@ class DetailView(VerticalScroll): parts.append(f"**DocItem Refs:** `{refs_str}`") return parts + def _format_extra_metadata(self, metadata: dict) -> list[str]: + """Format raw metadata keys not already shown by `_format_provenance`. + + `ChunkMetadata`'s own fields (page_numbers, headings, labels, + doc_item_refs) are excluded here since `_format_provenance` already + renders them, with its own truncation for long ref lists. + """ + extra = { + k: v for k, v in metadata.items() if k not in ChunkMetadata.model_fields + } + if not extra: + return [] + metadata_str = "\n".join(f" - {k}: {v}" for k, v in extra.items()) + return [f"**Metadata:**\n{metadata_str}"] + async def show_document(self, document: Document) -> None: """Display document details.""" title = document.title or document.uri or "Untitled Document" @@ -92,10 +107,7 @@ class DetailView(VerticalScroll): chunk_meta = chunk.get_chunk_metadata() content_parts.extend(self._format_provenance(chunk_meta)) - - if chunk.metadata: - metadata_str = "\n".join(f" - {k}: {v}" for k, v in chunk.metadata.items()) - content_parts.append(f"**Metadata:**\n{metadata_str}") + content_parts.extend(self._format_extra_metadata(chunk.metadata)) if chunk.embedding: content_parts.append(f"**Embedding:** {len(chunk.embedding)} dimensions") @@ -124,12 +136,7 @@ class DetailView(VerticalScroll): content_parts.append(f"**Score:** {search_result.score:.4f}") content_parts.extend(self._format_provenance(search_result)) - - if search_result.chunk_meta: - metadata_str = "\n".join( - f" - {k}: {v}" for k, v in search_result.chunk_meta.items() - ) - content_parts.append(f"**Metadata:**\n{metadata_str}") + content_parts.extend(self._format_extra_metadata(search_result.chunk_meta)) if chunk.embedding: content_parts.append(f"**Embedding:** {len(chunk.embedding)} dimensions") diff --git a/tests/test_inspector.py b/tests/test_inspector.py index 7d432821..52c69c8d 100644 --- a/tests/test_inspector.py +++ b/tests/test_inspector.py @@ -167,8 +167,9 @@ async def test_document_list_tracks_has_more(): @pytest.mark.asyncio async def test_detail_view_shows_chunk_metadata(): - """show_chunk renders the raw chunk.metadata dict verbatim, not just the - typed provenance fields _format_provenance already covers.""" + """show_chunk renders metadata keys _format_provenance doesn't already + cover, but not a duplicate of the standard fields it does (headings, + page_numbers, labels, doc_item_refs).""" from textual.app import App from haiku.rag.inspector.widgets.detail_view import DetailView @@ -191,12 +192,40 @@ async def test_detail_view_shows_chunk_metadata(): source = detail_view.content_widget.source assert "**Metadata:**" in source assert "para_no: 12" in source + assert "headings:" not in source # already shown as **Section:** + + +@pytest.mark.asyncio +async def test_detail_view_omits_metadata_block_when_only_standard_fields(): + """No **Metadata:** block at all when chunk.metadata holds nothing + beyond what _format_provenance already renders.""" + from textual.app import App + + from haiku.rag.inspector.widgets.detail_view import DetailView + + chunk = Chunk( + id="chunk-1", + document_id="doc-1", + content="raw chunk text", + metadata={"headings": ["Chapter 1"], "page_numbers": [1]}, + ) + + class TestApp(App): + def compose(self): + yield DetailView(id="detail") + + app = TestApp() + async with app.run_test(): + detail_view = app.query_one(DetailView) + await detail_view.show_chunk(chunk) + source = detail_view.content_widget.source + assert "**Metadata:**" not in source @pytest.mark.asyncio async def test_detail_view_shows_search_result_chunk_meta(): - """show_search_result renders SearchResult.chunk_meta, the anchor - chunk's raw metadata carried through search/expansion.""" + """show_search_result renders SearchResult.chunk_meta's non-standard + keys, the anchor chunk's own metadata carried through search/expansion.""" from textual.app import App from haiku.rag.inspector.widgets.detail_view import DetailView @@ -206,7 +235,11 @@ async def test_detail_view_shows_search_result_chunk_meta(): content="raw chunk text", score=0.5, chunk_id="chunk-1", - chunk_meta={"para_no": "12"}, + doc_item_refs=[f"#/texts/{i}" for i in range(7)], + chunk_meta={ + "para_no": "12", + "doc_item_refs": [f"#/texts/{i}" for i in range(7)], + }, ) class TestApp(App): @@ -220,6 +253,10 @@ async def test_detail_view_shows_search_result_chunk_meta(): source = detail_view.content_widget.source assert "**Metadata:**" in source assert "para_no: 12" in source + # _format_provenance's own 5-item truncation for doc_item_refs is + # untouched by the filtered-out duplicate in chunk_meta. + assert "+2 more" in source + assert "doc_item_refs:" not in source @pytest.mark.asyncio