use crate::providers::ProviderName;
use serde::{Deserialize, Serialize};
use zeph_common::memory::MemoryRoute;
use super::default_embed_timeout_secs;
fn validate_tier_similarity_threshold<'de, D>(deserializer: D) -> Result<f32, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <f32 as serde::Deserialize>::deserialize(deserializer)?;
if value.is_nan() || value.is_infinite() {
return Err(serde::de::Error::custom(
"similarity_threshold must be a finite number",
));
}
if !(0.5..=1.0).contains(&value) {
return Err(serde::de::Error::custom(
"similarity_threshold must be in [0.5, 1.0]",
));
}
Ok(value)
}
fn validate_tier_promotion_min_sessions<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <u32 as serde::Deserialize>::deserialize(deserializer)?;
if value < 2 {
return Err(serde::de::Error::custom(
"promotion_min_sessions must be >= 2",
));
}
Ok(value)
}
fn validate_tier_sweep_batch_size<'de, D>(deserializer: D) -> Result<usize, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <usize as serde::Deserialize>::deserialize(deserializer)?;
if value == 0 {
return Err(serde::de::Error::custom("sweep_batch_size must be >= 1"));
}
Ok(value)
}
fn default_tier_promotion_min_sessions() -> u32 {
3
}
fn default_tier_similarity_threshold() -> f32 {
0.92
}
fn default_tier_sweep_interval_secs() -> u64 {
3600
}
fn default_tier_sweep_batch_size() -> usize {
100
}
fn default_scene_similarity_threshold() -> f32 {
0.80
}
fn default_scene_batch_size() -> usize {
50
}
fn validate_scene_similarity_threshold<'de, D>(deserializer: D) -> Result<f32, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <f32 as serde::Deserialize>::deserialize(deserializer)?;
if value.is_nan() || value.is_infinite() {
return Err(serde::de::Error::custom(
"scene_similarity_threshold must be a finite number",
));
}
if !(0.5..=1.0).contains(&value) {
return Err(serde::de::Error::custom(
"scene_similarity_threshold must be in [0.5, 1.0]",
));
}
Ok(value)
}
fn validate_scene_batch_size<'de, D>(deserializer: D) -> Result<usize, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <usize as serde::Deserialize>::deserialize(deserializer)?;
if value == 0 {
return Err(serde::de::Error::custom("scene_batch_size must be >= 1"));
}
Ok(value)
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(default)]
pub struct TierConfig {
pub enabled: bool,
#[serde(deserialize_with = "validate_tier_promotion_min_sessions")]
pub promotion_min_sessions: u32,
#[serde(deserialize_with = "validate_tier_similarity_threshold")]
pub similarity_threshold: f32,
pub sweep_interval_secs: u64,
#[serde(deserialize_with = "validate_tier_sweep_batch_size")]
pub sweep_batch_size: usize,
pub scene_enabled: bool,
#[serde(deserialize_with = "validate_scene_similarity_threshold")]
pub scene_similarity_threshold: f32,
#[serde(deserialize_with = "validate_scene_batch_size")]
pub scene_batch_size: usize,
pub scene_provider: ProviderName,
pub scene_sweep_interval_secs: u64,
}
fn default_scene_sweep_interval_secs() -> u64 {
7200
}
impl Default for TierConfig {
fn default() -> Self {
Self {
enabled: false,
promotion_min_sessions: default_tier_promotion_min_sessions(),
similarity_threshold: default_tier_similarity_threshold(),
sweep_interval_secs: default_tier_sweep_interval_secs(),
sweep_batch_size: default_tier_sweep_batch_size(),
scene_enabled: false,
scene_similarity_threshold: default_scene_similarity_threshold(),
scene_batch_size: default_scene_batch_size(),
scene_provider: ProviderName::default(),
scene_sweep_interval_secs: default_scene_sweep_interval_secs(),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct TieredRetrievalConfig {
pub enabled: bool,
pub classifier_provider: ProviderName,
pub validator_provider: ProviderName,
pub token_budget: usize,
pub validation_enabled: bool,
pub validation_threshold: f32,
pub max_escalations: u8,
pub classifier_timeout_secs: u64,
pub validator_timeout_secs: u64,
pub similarity_weight: f64,
pub recency_weight: f64,
pub recency_half_life_days: u32,
pub tfidf_weight: f64,
pub cognitive_signal_weight: f64,
pub tier_boost_weight: f64,
pub semantic_tier_boost: f64,
#[serde(default)]
pub deep_reasoning_query_conditioned: bool,
}
impl Default for TieredRetrievalConfig {
fn default() -> Self {
Self {
enabled: false,
classifier_provider: ProviderName::default(),
validator_provider: ProviderName::default(),
token_budget: 4096,
validation_enabled: false,
validation_threshold: 0.6,
max_escalations: 1,
classifier_timeout_secs: 5,
validator_timeout_secs: 5,
similarity_weight: 1.0,
recency_weight: 0.0,
recency_half_life_days: 7,
tfidf_weight: 0.0,
cognitive_signal_weight: 0.0,
tier_boost_weight: 0.0,
semantic_tier_boost: 1.0,
deep_reasoning_query_conditioned: false,
}
}
}
fn default_retrieval_failures_low_confidence_threshold() -> f32 {
0.3
}
fn default_retrieval_failures_retention_days() -> u32 {
90
}
fn default_retrieval_failures_channel_capacity() -> usize {
256
}
fn default_retrieval_failures_batch_size() -> usize {
16
}
fn default_retrieval_failures_flush_interval_ms() -> u64 {
100
}
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum ContextFormat {
#[default]
Structured,
Plain,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct RetrievalConfig {
pub depth: u32,
pub search_prompt_template: String,
pub context_format: ContextFormat,
#[serde(default = "default_query_bias_correction")]
pub query_bias_correction: bool,
#[serde(default = "default_query_bias_profile_weight")]
pub query_bias_profile_weight: f32,
#[serde(default = "default_query_bias_centroid_ttl_secs")]
pub query_bias_centroid_ttl_secs: u64,
}
fn default_query_bias_correction() -> bool {
true
}
fn default_query_bias_profile_weight() -> f32 {
0.25
}
fn default_query_bias_centroid_ttl_secs() -> u64 {
300
}
impl Default for RetrievalConfig {
fn default() -> Self {
Self {
depth: 0,
search_prompt_template: String::new(),
context_format: ContextFormat::default(),
query_bias_correction: default_query_bias_correction(),
query_bias_profile_weight: default_query_bias_profile_weight(),
query_bias_centroid_ttl_secs: default_query_bias_centroid_ttl_secs(),
}
}
}
fn default_consolidation_confidence_threshold() -> f32 {
0.7
}
fn default_consolidation_sweep_interval_secs() -> u64 {
3600
}
fn default_consolidation_sweep_batch_size() -> usize {
50
}
fn default_consolidation_similarity_threshold() -> f32 {
0.85
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(default)]
pub struct ConsolidationConfig {
pub enabled: bool,
#[serde(default)]
pub consolidation_provider: ProviderName,
#[serde(default = "default_consolidation_confidence_threshold")]
pub confidence_threshold: f32,
#[serde(default = "default_consolidation_sweep_interval_secs")]
pub sweep_interval_secs: u64,
#[serde(default = "default_consolidation_sweep_batch_size")]
pub sweep_batch_size: usize,
#[serde(default = "default_consolidation_similarity_threshold")]
pub similarity_threshold: f32,
#[serde(default = "default_consolidation_llm_timeout_secs")]
pub llm_timeout_secs: u64,
#[serde(default = "default_embed_timeout_secs")]
pub embed_timeout_secs: u64,
}
impl Default for ConsolidationConfig {
fn default() -> Self {
Self {
enabled: false,
consolidation_provider: ProviderName::default(),
confidence_threshold: default_consolidation_confidence_threshold(),
sweep_interval_secs: default_consolidation_sweep_interval_secs(),
sweep_batch_size: default_consolidation_sweep_batch_size(),
similarity_threshold: default_consolidation_similarity_threshold(),
llm_timeout_secs: default_consolidation_llm_timeout_secs(),
embed_timeout_secs: default_embed_timeout_secs(),
}
}
}
fn default_consolidation_llm_timeout_secs() -> u64 {
30
}
fn default_admission_threshold() -> f32 {
0.40
}
fn default_admission_fast_path_margin() -> f32 {
0.15
}
fn default_rl_min_samples() -> u32 {
500
}
fn default_rl_retrain_interval_secs() -> u64 {
3600
}
#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum AdmissionStrategy {
#[default]
Heuristic,
Rl,
}
fn validate_admission_weight<'de, D>(deserializer: D) -> Result<f32, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <f32 as serde::Deserialize>::deserialize(deserializer)?;
if value < 0.0 {
return Err(serde::de::Error::custom(
"admission weight must be non-negative (>= 0.0)",
));
}
Ok(value)
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct AdmissionWeights {
#[serde(deserialize_with = "validate_admission_weight")]
pub future_utility: f32,
#[serde(deserialize_with = "validate_admission_weight")]
pub factual_confidence: f32,
#[serde(deserialize_with = "validate_admission_weight")]
pub semantic_novelty: f32,
#[serde(deserialize_with = "validate_admission_weight")]
pub temporal_recency: f32,
#[serde(deserialize_with = "validate_admission_weight")]
pub content_type_prior: f32,
#[serde(deserialize_with = "validate_admission_weight")]
pub goal_utility: f32,
}
impl Default for AdmissionWeights {
fn default() -> Self {
Self {
future_utility: 0.30,
factual_confidence: 0.15,
semantic_novelty: 0.30,
temporal_recency: 0.10,
content_type_prior: 0.15,
goal_utility: 0.0,
}
}
}
impl AdmissionWeights {
#[must_use]
pub fn normalized(&self) -> Self {
let sum = self.future_utility
+ self.factual_confidence
+ self.semantic_novelty
+ self.temporal_recency
+ self.content_type_prior
+ self.goal_utility;
if sum <= f32::EPSILON {
return Self::default();
}
Self {
future_utility: self.future_utility / sum,
factual_confidence: self.factual_confidence / sum,
semantic_novelty: self.semantic_novelty / sum,
temporal_recency: self.temporal_recency / sum,
content_type_prior: self.content_type_prior / sum,
goal_utility: self.goal_utility / sum,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct AdmissionConfig {
pub enabled: bool,
#[serde(deserialize_with = "crate::de_helpers::de_unit_closed")]
pub threshold: f32,
#[serde(deserialize_with = "crate::de_helpers::de_unit_closed")]
pub fast_path_margin: f32,
pub admission_provider: ProviderName,
pub weights: AdmissionWeights,
#[serde(default)]
pub admission_strategy: AdmissionStrategy,
#[serde(default = "default_rl_min_samples")]
pub rl_min_samples: u32,
#[serde(default = "default_rl_retrain_interval_secs")]
pub rl_retrain_interval_secs: u64,
#[serde(default)]
pub goal_conditioned_write: bool,
#[serde(default)]
pub goal_utility_provider: ProviderName,
#[serde(default = "default_goal_utility_threshold")]
pub goal_utility_threshold: f32,
#[serde(default = "default_goal_utility_weight")]
pub goal_utility_weight: f32,
}
fn default_goal_utility_threshold() -> f32 {
0.4
}
fn default_goal_utility_weight() -> f32 {
0.25
}
impl Default for AdmissionConfig {
fn default() -> Self {
Self {
enabled: false,
threshold: default_admission_threshold(),
fast_path_margin: default_admission_fast_path_margin(),
admission_provider: ProviderName::default(),
weights: AdmissionWeights::default(),
admission_strategy: AdmissionStrategy::default(),
rl_min_samples: default_rl_min_samples(),
rl_retrain_interval_secs: default_rl_retrain_interval_secs(),
goal_conditioned_write: false,
goal_utility_provider: ProviderName::default(),
goal_utility_threshold: default_goal_utility_threshold(),
goal_utility_weight: default_goal_utility_weight(),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum StoreRoutingStrategy {
#[default]
Heuristic,
Llm,
Hybrid,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct StoreRoutingConfig {
pub enabled: bool,
pub strategy: StoreRoutingStrategy,
pub routing_classifier_provider: ProviderName,
pub fallback_route: MemoryRoute,
pub confidence_threshold: f32,
}
impl Default for StoreRoutingConfig {
fn default() -> Self {
Self {
enabled: false,
strategy: StoreRoutingStrategy::Heuristic,
routing_classifier_provider: ProviderName::default(),
fallback_route: MemoryRoute::Hybrid,
confidence_threshold: 0.7,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct RetrievalFailuresConfig {
pub enabled: bool,
#[serde(default = "default_retrieval_failures_low_confidence_threshold")]
pub low_confidence_threshold: f32,
#[serde(default = "default_retrieval_failures_retention_days")]
pub retention_days: u32,
#[serde(default = "default_retrieval_failures_channel_capacity")]
pub channel_capacity: usize,
#[serde(default = "default_retrieval_failures_batch_size")]
pub batch_size: usize,
#[serde(default = "default_retrieval_failures_flush_interval_ms")]
pub flush_interval_ms: u64,
}
impl Default for RetrievalFailuresConfig {
fn default() -> Self {
Self {
enabled: false,
low_confidence_threshold: default_retrieval_failures_low_confidence_threshold(),
retention_days: default_retrieval_failures_retention_days(),
channel_capacity: default_retrieval_failures_channel_capacity(),
batch_size: default_retrieval_failures_batch_size(),
flush_interval_ms: default_retrieval_failures_flush_interval_ms(),
}
}
}