use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentAggregatorConfig {
pub token_budget: usize,
pub min_relevance_score: f32,
pub scoring_strategy: ScoringStrategyConfig,
pub output_format: OutputFormatConfig,
pub include_scores: bool,
pub hierarchical_min_per_level: f32,
pub deduplicate: bool,
pub dedup_threshold: f32,
}
impl Default for ContentAggregatorConfig {
fn default() -> Self {
Self {
token_budget: 4000,
min_relevance_score: 0.2,
scoring_strategy: ScoringStrategyConfig::KeywordWithBM25,
output_format: OutputFormatConfig::Markdown,
include_scores: false,
hierarchical_min_per_level: 0.1,
deduplicate: true,
dedup_threshold: 0.9,
}
}
}
impl ContentAggregatorConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_token_budget(mut self, budget: usize) -> Self {
self.token_budget = budget;
self
}
#[must_use]
pub fn with_min_relevance(mut self, score: f32) -> Self {
self.min_relevance_score = score.clamp(0.0, 1.0);
self
}
#[must_use]
pub fn with_scoring_strategy(mut self, strategy: ScoringStrategyConfig) -> Self {
self.scoring_strategy = strategy;
self
}
#[must_use]
pub fn with_output_format(mut self, format: OutputFormatConfig) -> Self {
self.output_format = format;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ScoringStrategyConfig {
KeywordOnly,
KeywordWithBM25,
Hybrid,
}
impl Default for ScoringStrategyConfig {
fn default() -> Self {
Self::KeywordWithBM25
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OutputFormatConfig {
Markdown,
Json,
Tree,
Flat,
}
impl Default for OutputFormatConfig {
fn default() -> Self {
Self::Markdown
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = ContentAggregatorConfig::default();
assert_eq!(config.token_budget, 4000);
assert_eq!(config.min_relevance_score, 0.2);
}
#[test]
fn test_config_builder() {
let config = ContentAggregatorConfig::new()
.with_token_budget(2000)
.with_min_relevance(0.5);
assert_eq!(config.token_budget, 2000);
assert_eq!(config.min_relevance_score, 0.5);
}
#[test]
fn test_min_relevance_clamped() {
let config = ContentAggregatorConfig::new().with_min_relevance(1.5);
assert_eq!(config.min_relevance_score, 1.0);
let config = ContentAggregatorConfig::new().with_min_relevance(-0.5);
assert_eq!(config.min_relevance_score, 0.0);
}
}