use std::sync::Arc; use tokio::sync::Mutex; use crate::agent::AgentError; use super::session::Session; /// Run two-segment history compression synchronously. /// /// Unlike the previous background approach (tokio::spawn), this holds the /// session lock during the LLM calls (2–5 seconds). Since the agent loop /// has already finished by this point there is no response-time impact, and /// the synchronous guarantee means the next execution always starts with /// freshly compacted history. /// /// 按 topic_id 隔离:压缩只处理指定 topic 的历史,DB 替换也只影响该 topic。 pub(crate) async fn schedule_background_history_compaction( session: Arc>, chat_id: impl Into, topic_id: impl Into, ) -> Result<(), AgentError> { let chat_id = chat_id.into(); let topic_id = topic_id.into(); let mut session_guard = session.lock().await; session_guard.ensure_persistent_session(&chat_id)?; session_guard.ensure_chat_loaded(&chat_id, Some(&topic_id))?; let history = session_guard.get_or_create_history(&topic_id).clone(); let compressor = session_guard.compressor().clone(); if !compressor.should_compress(&history) { return Ok(()); } let store = session_guard.store(); let session_id = session_guard.persistent_session_id(&chat_id); let provider_config = session_guard.provider_config().clone(); tracing::info!( chat_id = %chat_id, topic_id = %topic_id, msg_count = history.len(), "Starting synchronous two-segment compression" ); // Synchronous compression — holds lock during LLM calls. // compress_two_segment guarantees the result contains no tool_calls, // so there is no risk of orphaned tool call sequences. let compressed = compressor .compress_two_segment(&history, &provider_config) .await?; // Replace only this topic's history in DB (not the entire session). // This avoids clobbering other topics' messages during compaction. store .replace_topic_history(&session_id, &topic_id, &compressed) .map_err(|e| AgentError::Other(format!("replace_topic_history error: {}", e)))?; tracing::info!( chat_id = %chat_id, topic_id = %topic_id, compressed_msg_count = compressed.len(), "Two-segment compression committed" ); session_guard.reload_topic_history(&chat_id, &topic_id)?; Ok(()) }