use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use theway_llm_provider::{Model, Usage};
use tokio_util::sync::CancellationToken;
use super::super::session::session::SessionTreeEntry;
use super::compaction::{
CompactionSettings, CutPointResult, SummarizeError, find_cut_point, should_compact,
summarize_with_llm,
};
use crate::types::{AgentMessage, StreamFn};
#[derive(Clone)]
pub struct SummarizeRequest<'a> {
pub model: &'a Model,
pub messages: &'a [AgentMessage],
pub custom_instructions: Option<&'a str>,
pub settings: &'a CompactionSettings,
pub stream_fn: Option<&'a StreamFn>,
pub cancel: &'a CancellationToken,
}
#[derive(Clone, Debug)]
pub struct SummaryOutcome {
pub summary: String,
pub usage: Usage,
}
#[async_trait]
pub trait CompactAlgorithm: Send + Sync {
fn name(&self) -> &str;
async fn decide_compact(
&self,
context_tokens: u64,
context_window: u32,
settings: &CompactionSettings,
) -> bool {
should_compact(context_tokens, context_window, settings)
}
async fn select_cut_point(
&self,
entries: &[SessionTreeEntry],
settings: &CompactionSettings,
) -> CutPointResult {
find_cut_point(entries, settings)
}
async fn summarize_prefix(
&self,
request: &SummarizeRequest<'_>,
) -> Result<SummaryOutcome, SummarizeError> {
summarize_with_llm(request).await
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct BuiltinCompactAlgorithm;
#[async_trait]
impl CompactAlgorithm for BuiltinCompactAlgorithm {
fn name(&self) -> &str {
"builtin"
}
}
pub struct CompactAlgorithmRegistry {
custom: parking_lot::RwLock<HashMap<String, Arc<dyn CompactAlgorithm>>>,
}
impl Default for CompactAlgorithmRegistry {
fn default() -> Self {
Self::new()
}
}
impl CompactAlgorithmRegistry {
pub fn new() -> Self {
Self {
custom: parking_lot::RwLock::new(HashMap::new()),
}
}
pub fn register(&self, algorithm: Arc<dyn CompactAlgorithm>) {
if algorithm.name() != "builtin" {
self.custom
.write()
.insert(algorithm.name().to_string(), algorithm);
}
}
pub fn replace_custom(&self, algorithms: impl IntoIterator<Item = Arc<dyn CompactAlgorithm>>) {
let mut replacement = HashMap::new();
for algorithm in algorithms {
if algorithm.name() != "builtin" {
replacement.insert(algorithm.name().to_string(), algorithm);
}
}
*self.custom.write() = replacement;
}
pub fn algorithm(&self, name: &str) -> Arc<dyn CompactAlgorithm> {
if name.is_empty() || name == "builtin" {
return Arc::new(BuiltinCompactAlgorithm);
}
match self.custom.read().get(name) {
Some(a) => a.clone(),
None => {
tracing::warn!(
algorithm = name,
"unknown compaction algorithm, falling back to builtin"
);
Arc::new(BuiltinCompactAlgorithm)
}
}
}
pub fn custom_names(&self) -> Vec<String> {
let mut names: Vec<String> = self.custom.read().keys().cloned().collect();
names.sort();
names
}
}
#[cfg(test)]
tests_bridge_macro::tests_bridge!("agent/compaction/algorithm");