diff --git a/CHANGELOG.md b/CHANGELOG.md index a2c3705e..4a0f8eb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ - Supports `all` argument to download/upload all datasets at once - Use `--force` flag to overwrite existing databases - Avoids lengthy database rebuild times for users running benchmarks +- **Dynamic Session Context**: Compressed conversation history for multi-turn chat + - New `SessionContext` model stores summarized conversation state instead of raw Q&A history + - Background LLM-based summarization runs after each `ask` tool call (non-blocking) + - Previous summarization tasks are cancelled when new ones start + - Research graph receives compact context (~1,000-2,000 tokens) instead of raw qa_history (potentially thousands of tokens) + - New `session_context` field on `ChatSessionState` synced via AG-UI state protocol + - Chat TUI: New context modal (`Ctrl+O`) to view current session context ### Changed @@ -28,7 +35,6 @@ - **v0.25.0 Migration Failure**: Fixed "Table 'documents' already exists" error during migration caused by held table references preventing `drop_table()` from succeeding. Added recovery logic to restore documents from staging table if a previous migration attempt failed mid-way. ## [0.26.8] - 2026-01-22 - - **Jina Reranker v3**: Added support for Jina reranking with API mode (`provider: jina`) and local inference (`provider: jina-local`, requires `[jina]` extra) - **Model Downloads**: `download-models` now pre-downloads HuggingFace models for `sentence-transformers`, `mxbai`, and `jina-local` - **Reranker Factory**: Removed unreliable `id(config)`-based caching from `get_reranker()`; factory now always instantiates fresh diff --git a/haiku_rag_slim/haiku/rag/agents/chat/agent.py b/haiku_rag_slim/haiku/rag/agents/chat/agent.py index 113fc84a..1e1fcc01 100644 --- a/haiku_rag_slim/haiku/rag/agents/chat/agent.py +++ b/haiku_rag_slim/haiku/rag/agents/chat/agent.py @@ -25,8 +25,8 @@ from haiku.rag.utils import get_model logger = logging.getLogger(__name__) -# Track background tasks to prevent garbage collection -_background_tasks: set[asyncio.Task[None]] = set() +# Track summarization tasks per session to allow cancellation +_summarization_tasks: dict[str, asyncio.Task[None]] = {} async def _update_context_background( @@ -42,6 +42,8 @@ async def _update_context_background( session_state=session_state, ) logger.debug("Session context updated") + except asyncio.CancelledError: + logger.debug("Session context summarization cancelled") except Exception: logger.exception("Failed to update session context") @@ -289,6 +291,11 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]: ] # Spawn background task to update session context + # Cancel any previous summarization for this session + session_id = ctx.deps.session_state.session_id + if session_id in _summarization_tasks: + _summarization_tasks[session_id].cancel() + task = asyncio.create_task( _update_context_background( qa_history=list(ctx.deps.session_state.qa_history), @@ -296,8 +303,8 @@ def create_chat_agent(config: AppConfig) -> Agent[ChatDeps, str]: session_state=ctx.deps.session_state, ) ) - _background_tasks.add(task) - task.add_done_callback(_background_tasks.discard) + _summarization_tasks[session_id] = task + task.add_done_callback(lambda t: _summarization_tasks.pop(session_id, None)) # Build new state with citations AND accumulated qa_history new_state = ChatSessionState(