diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a0bb5c8..bd868b6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Bump `haiku.skills>=0.17.1` and `pydantic-ai-slim>=1.100.0` (the last pre-2.0 release). Migrate off two APIs slated for removal in pydantic-ai 2.0: `Agent(tool_retries=, output_retries=)` → `Agent(retries={"tools": …, "output": …})` in the LLM-as-judge evaluator, and `Evaluator.evaluation_name` class attribute → overriding `get_default_evaluation_name()` on `CitationMRREvaluator` / `CitationMAPEvaluator`. - Drop the `item.annotations` fallback in `_picture_description_text`. Docling's `PictureItem` runs a `@model_validator(mode="after")` on load that migrates the deprecated `annotations` field into `meta.description`, so reading `meta.description.text` covers both legacy and current blobs. Tests in `test_converters.py` switched to `meta.description.text` for the same reason. +- Chat TUI now generates a stable per-launch `thread_id` (rotated on "Clear chat") instead of hardcoding `"tui"`. AGUIAdapter forwards it as the `gen_ai.conversation.id` OTel attribute, so multi-turn TUI sessions group into one Logfire conversation instead of collapsing every launch into a single bucket. - Documentation generator swapped from `mkdocs-material` to `zensical`. Drops `mkdocs` / `mkdocs-material` dev deps, replaces `mkdocs.yml` with `zensical.toml`, adds `overrides/main.html` (OG/Twitter share meta) and `docs/stylesheets/extra.css`. `build-docs` workflow now runs `uv run zensical build` and publishes via the GitHub Pages artifact actions instead of `mkdocs gh-deploy`. ### Fixed diff --git a/haiku_rag_slim/haiku/rag/chat/app.py b/haiku_rag_slim/haiku/rag/chat/app.py index ed4cd4a7..218f59f8 100644 --- a/haiku_rag_slim/haiku/rag/chat/app.py +++ b/haiku_rag_slim/haiku/rag/chat/app.py @@ -108,6 +108,10 @@ class ChatApp(App): self._is_processing = False self._current_worker: Worker[None] | None = None self._document_filter: list[str] = [] + # Stable per-launch id so multi-turn chats land in one Logfire + # conversation. AGUIAdapter reads run_input.thread_id and exports it + # as the `gen_ai.conversation.id` OTel attribute on every agent run. + self._conversation_id = str(uuid.uuid4()) def compose(self) -> "ComposeResult": """Compose the UI layout.""" @@ -207,7 +211,7 @@ class ChatApp(App): await chat_history.show_thinking() run_input = RunAgentInput( - thread_id="tui", + thread_id=self._conversation_id, run_id=str(uuid.uuid4()), messages=self._messages, state=self._state, @@ -375,6 +379,8 @@ class ChatApp(App): # Reset state if self._toolset: self._state = self._toolset.build_state_snapshot() + # Cleared chat starts a fresh Logfire conversation. + self._conversation_id = str(uuid.uuid4()) def action_focus_input(self) -> None: """Focus the input field, or cancel if processing.""" diff --git a/tests/chat/test_chat_app.py b/tests/chat/test_chat_app.py index 2853c566..e225cc54 100644 --- a/tests/chat/test_chat_app.py +++ b/tests/chat/test_chat_app.py @@ -219,7 +219,7 @@ async def test_chat_history_thinking_indicator(temp_db_path: Path): @pytest.mark.asyncio async def test_clear_chat_resets_state(temp_db_path: Path): - """Test that clearing chat resets state and messages.""" + """Test that clearing chat resets state, messages, and conversation id.""" from haiku.rag.chat.widgets.chat_history import ChatHistory app, mock_client = _make_app(temp_db_path) @@ -232,10 +232,12 @@ async def test_clear_chat_resets_state(temp_db_path: Path): await chat_history.add_message("assistant", "Hi there") assert len(chat_history.messages) == 2 + previous_conversation_id = app._conversation_id await app.action_clear_chat() await pilot.pause() assert len(chat_history.messages) == 0 + assert app._conversation_id != previous_conversation_id @pytest.mark.asyncio