use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct SessionManagerConfig {
pub enabled: bool,
pub inference: SmInferenceConfig,
pub memory: SmMemoryConfig,
pub rounds: SmRoundsConfig,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct SmInferenceConfig {
pub provider: String,
pub sm_model: String,
pub summary_model: String,
pub model: String,
pub fallback: Vec<String>,
pub temperature: f32,
pub context_token_budget: u32,
pub compaction_model: String,
pub compressed_context_max_tokens: u32,
}
impl Default for SmInferenceConfig {
fn default() -> Self {
Self {
provider: "auto".to_string(),
sm_model: "anthropic/claude-sonnet-4-6".to_string(),
summary_model: "anthropic/claude-haiku".to_string(),
model: String::new(),
fallback: Vec::new(),
temperature: 0.3,
context_token_budget: 24_000,
compaction_model: String::new(),
compressed_context_max_tokens: 4_000,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct SmMemoryConfig {
pub palace: String,
pub recall_top_k: u32,
}
impl Default for SmMemoryConfig {
fn default() -> Self {
Self {
palace: "session-manager".to_string(),
recall_top_k: 6,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct SmRoundsConfig {
pub window: u32,
}
impl Default for SmRoundsConfig {
fn default() -> Self {
Self { window: 10 }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sm_config_full_parsed() {
let toml = r#"
enabled = true
[inference]
provider = "openrouter"
sm_model = "openrouter/anthropic/claude-sonnet-4-6"
summary_model = "openrouter/anthropic/claude-haiku"
model = "legacy/sonnet"
fallback = ["openrouter", "bedrock"]
temperature = 0.5
context_token_budget = 32000
compaction_model = "anthropic/claude-haiku"
compressed_context_max_tokens = 6000
[memory]
palace = "sm-palace"
recall_top_k = 9
[rounds]
window = 20
"#;
let cfg: SessionManagerConfig = toml::from_str(toml).expect("full SM config parses");
assert!(cfg.enabled);
assert_eq!(cfg.inference.provider, "openrouter");
assert_eq!(
cfg.inference.sm_model,
"openrouter/anthropic/claude-sonnet-4-6"
);
assert_eq!(
cfg.inference.summary_model,
"openrouter/anthropic/claude-haiku"
);
assert_eq!(cfg.inference.model, "legacy/sonnet");
assert_eq!(cfg.inference.fallback, vec!["openrouter", "bedrock"]);
assert_eq!(cfg.inference.temperature, 0.5);
assert_eq!(cfg.inference.context_token_budget, 32_000);
assert_eq!(cfg.inference.compaction_model, "anthropic/claude-haiku");
assert_eq!(cfg.inference.compressed_context_max_tokens, 6_000);
assert_eq!(cfg.memory.palace, "sm-palace");
assert_eq!(cfg.memory.recall_top_k, 9);
assert_eq!(cfg.rounds.window, 20);
}
#[test]
fn sm_config_partial_takes_defaults() {
let toml = r#"
enabled = true
[inference]
temperature = 0.7
"#;
let cfg: SessionManagerConfig = toml::from_str(toml).expect("partial SM config parses");
assert!(cfg.enabled);
assert_eq!(cfg.inference.temperature, 0.7);
assert_eq!(cfg.inference.provider, "auto");
assert_eq!(cfg.inference.context_token_budget, 24_000);
assert_eq!(cfg.memory, SmMemoryConfig::default());
assert_eq!(cfg.rounds, SmRoundsConfig::default());
}
#[test]
fn sm_config_absent_is_all_defaults() {
let cfg: SessionManagerConfig = toml::from_str("").expect("empty input parses");
assert_eq!(cfg, SessionManagerConfig::default());
}
#[test]
fn sm_config_enabled_default_false() {
assert!(!SessionManagerConfig::default().enabled);
}
#[test]
fn sm_config_default_values() {
let cfg = SessionManagerConfig::default();
assert_eq!(cfg.inference.provider, "auto");
assert_eq!(cfg.inference.sm_model, "anthropic/claude-sonnet-4-6");
assert_eq!(cfg.inference.summary_model, "anthropic/claude-haiku");
assert!(cfg.inference.model.is_empty());
assert!(cfg.inference.fallback.is_empty());
assert_eq!(cfg.inference.temperature, 0.3);
assert_eq!(cfg.inference.context_token_budget, 24_000);
assert!(cfg.inference.compaction_model.is_empty());
assert_eq!(cfg.inference.compressed_context_max_tokens, 4_000);
assert_eq!(cfg.memory.palace, "session-manager");
assert_eq!(cfg.memory.recall_top_k, 6);
assert_eq!(cfg.rounds.window, 10);
}
}