From a99459ac93db32557f34fb7eee761bb1fd50f607 Mon Sep 17 00:00:00 2001 From: Ziyang Guo <121015044+RerankerGuo@users.noreply.github.com> Date: Wed, 14 Oct 2026 11:25:48 +0800 Subject: [PATCH] fix(reindex): return a readable summary so Studio stops showing a placeholder (#565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(reindex): return a readable summary so Studio stops showing a placeholder `ReindexStep` assigned the store's report dict straight to `response.answer`. ReMe Studio's settings panel already anticipates a string answer: typeof result.answer === "string" && result.answer ? result.answer : t("indexRebuilt") (`reme_studio/app/settings-center.tsx`), so with a dict answer that branch was unreachable and every rebuild reported the generic "index rebuilt" text. The operator never saw which scopes ran or how many entries each indexed. Render the report as one summary line — `Reindexed scope=all: bm25=7, embedding=5, tag=2` for the composite scope and `Reindexed scope=bm25: 3 indexed` for a single one. The report itself still lands in metadata, so programmatic consumers are unaffected, and Studio needs no change because its string branch now fires. `reindex` is the only answer-carrying consumer of this step; no test asserted the answer content, only metadata, so nothing else moved. * chore(reindex): tighten the renderer and deduplicate the summary test Review on #565: the helper's docstring carried the Studio background that already lives in the PR description; the intermediate per_scope dict collapses into the join generator; and the all-scope delegation test now uses distinct counts (7, 5, 2) so it alone pins both the exact summary answer and the full metadata report, replacing the overlapping summary test. * refactor(reindex): share summary return formatting --------- Co-authored-by: jinli.yl --- reme/steps/index/reindex.py | 12 +++++++++++- tests/unit/test_reindex.py | 10 ++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/reme/steps/index/reindex.py b/reme/steps/index/reindex.py index 6ba44655..82a569f3 100644 --- a/reme/steps/index/reindex.py +++ b/reme/steps/index/reindex.py @@ -4,6 +4,16 @@ from ..base_step import BaseStep from ...components import R +def _render_reindex(details: dict) -> str: + """Render the store's reindex report as one readable summary line.""" + scope = str(details.get("scope") or "all") + counts = ", ".join( + f"{name}={entry.get('indexed', 0)}" for name, entry in details.items() if isinstance(entry, dict) + ) + summary = counts or f"{details.get('indexed', 0)} indexed" + return f"Reindexed scope={scope}: {summary}" + + @R.register("reindex_step") class ReindexStep(BaseStep): """Rebuild BM25, embeddings, and/or tags without scanning workspace files.""" @@ -13,7 +23,7 @@ class ReindexStep(BaseStep): scope = str(self.context.get("scope", "all")) details = await self.file_store.reindex(scope) - self.context.response.answer = details + self.context.response.answer = _render_reindex(details) self.context.response.metadata.update(details) self.context.response.metadata["scope"] = scope return self.context.response diff --git a/tests/unit/test_reindex.py b/tests/unit/test_reindex.py index 64bd1a43..b29b9e8f 100644 --- a/tests/unit/test_reindex.py +++ b/tests/unit/test_reindex.py @@ -23,17 +23,18 @@ async def test_reindex_step_delegates_scope(scope): store.reindex.assert_awaited_once_with(scope) store.clear.assert_not_called() assert response.metadata == {"indexed": 3, "scope": scope} + assert response.answer == f"Reindexed scope={scope}: 3 indexed" @pytest.mark.asyncio async def test_reindex_step_delegates_all_once(): - """The step delegates the composite scope exactly once.""" + """The step delegates the composite scope once; the answer summarizes it.""" store = LocalFileStore(name="test_reindex_all", embedding_store="") details = { "scope": "all", - "bm25": {"scope": "bm25", "indexed": 3}, - "embedding": {"scope": "embedding", "indexed": 3}, - "tag": {"scope": "tag", "indexed": 3}, + "bm25": {"scope": "bm25", "indexed": 7}, + "embedding": {"scope": "embedding", "indexed": 5}, + "tag": {"scope": "tag", "indexed": 2}, } store.reindex = AsyncMock(return_value=details) @@ -41,6 +42,7 @@ async def test_reindex_step_delegates_all_once(): store.reindex.assert_awaited_once_with("all") assert response.metadata == details + assert response.answer == "Reindexed scope=all: bm25=7, embedding=5, tag=2" def test_reindex_job_schema_exposes_tag_scope():