Skip to main content

synwire_agent/middleware/
summarisation.rs

1//! Summarisation middleware — compacts conversation history when thresholds are hit.
2
3use synwire_core::BoxFuture;
4use synwire_core::agents::error::AgentError;
5use synwire_core::agents::middleware::{Middleware, MiddlewareInput, MiddlewareResult};
6
7/// Configuration thresholds for triggering summarisation.
8#[derive(Debug, Clone)]
9pub struct SummarisationThresholds {
10    /// Trigger when message count exceeds this value.
11    pub max_messages: Option<usize>,
12    /// Trigger when total token count exceeds this value (approximate).
13    pub max_tokens: Option<usize>,
14    /// Trigger when context utilisation percentage exceeds this value (0.0–1.0).
15    pub max_context_utilisation: Option<f32>,
16}
17
18impl Default for SummarisationThresholds {
19    fn default() -> Self {
20        Self {
21            max_messages: Some(50),
22            max_tokens: Some(80_000),
23            max_context_utilisation: Some(0.8),
24        }
25    }
26}
27
28/// Middleware that summarises conversation history when thresholds are exceeded.
29#[derive(Debug)]
30pub struct SummarisationMiddleware {
31    thresholds: SummarisationThresholds,
32}
33
34impl SummarisationMiddleware {
35    /// Create a new summarisation middleware with custom thresholds.
36    #[must_use]
37    pub const fn new(thresholds: SummarisationThresholds) -> Self {
38        Self { thresholds }
39    }
40}
41
42impl Default for SummarisationMiddleware {
43    fn default() -> Self {
44        Self::new(SummarisationThresholds::default())
45    }
46}
47
48impl Middleware for SummarisationMiddleware {
49    fn name(&self) -> &'static str {
50        "summarisation"
51    }
52
53    fn process(
54        &self,
55        input: MiddlewareInput,
56    ) -> BoxFuture<'_, Result<MiddlewareResult, AgentError>> {
57        Box::pin(async move {
58            let should_summarise = self
59                .thresholds
60                .max_messages
61                .is_some_and(|max| input.messages.len() > max);
62
63            if should_summarise {
64                tracing::debug!(
65                    messages = input.messages.len(),
66                    "Summarisation threshold exceeded"
67                );
68                // In a real implementation, this would call the LLM to summarise.
69                // For now we emit a status update via context metadata.
70                let mut ctx = input.context.clone();
71                if let Some(obj) = ctx.as_object_mut() {
72                    let _ =
73                        obj.insert("summarisation_pending".to_string(), serde_json::json!(true));
74                }
75                return Ok(MiddlewareResult::Continue(MiddlewareInput {
76                    messages: input.messages,
77                    context: ctx,
78                }));
79            }
80
81            Ok(MiddlewareResult::Continue(input))
82        })
83    }
84}