use serde::{Deserialize, Serialize};
use crate::context_bundle::{
AgentRole, ContextFilters, ContextIntent, ItemValidity, RiskClass, TrustClass,
};
pub const RETRIEVAL_PLAN_SCHEMA_VERSION: u32 = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RetrievalChannel {
CanonicalFts,
CanonicalVector,
GeneratedEnrichment,
EntityGraph,
GraphExpansion,
Temporal,
Workstreams,
SessionOutcomes,
Decisions,
SupersededHistory,
GitEvidence,
BenchmarkEvidence,
FailureLessons,
Preferences,
Constraints,
}
impl RetrievalChannel {
pub const ORDERED: [RetrievalChannel; 15] = [
RetrievalChannel::CanonicalFts,
RetrievalChannel::CanonicalVector,
RetrievalChannel::GeneratedEnrichment,
RetrievalChannel::EntityGraph,
RetrievalChannel::GraphExpansion,
RetrievalChannel::Temporal,
RetrievalChannel::Workstreams,
RetrievalChannel::SessionOutcomes,
RetrievalChannel::Decisions,
RetrievalChannel::SupersededHistory,
RetrievalChannel::GitEvidence,
RetrievalChannel::BenchmarkEvidence,
RetrievalChannel::FailureLessons,
RetrievalChannel::Preferences,
RetrievalChannel::Constraints,
];
pub fn name(&self) -> &'static str {
match self {
RetrievalChannel::CanonicalFts => "canonical_fts",
RetrievalChannel::CanonicalVector => "canonical_vector",
RetrievalChannel::GeneratedEnrichment => "generated_enrichment",
RetrievalChannel::EntityGraph => "entity_graph",
RetrievalChannel::GraphExpansion => "graph_expansion",
RetrievalChannel::Temporal => "temporal",
RetrievalChannel::Workstreams => "workstreams",
RetrievalChannel::SessionOutcomes => "session_outcomes",
RetrievalChannel::Decisions => "decisions",
RetrievalChannel::SupersededHistory => "superseded_history",
RetrievalChannel::GitEvidence => "git_evidence",
RetrievalChannel::BenchmarkEvidence => "benchmark_evidence",
RetrievalChannel::FailureLessons => "failure_lessons",
RetrievalChannel::Preferences => "preferences",
RetrievalChannel::Constraints => "constraints",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ChannelDegradation {
SkipChannel,
FailClosed,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelPlan {
pub channel: RetrievalChannel,
pub enabled: bool,
pub candidate_limit: u32,
pub weight: f64,
pub required_trust: TrustClass,
pub allowed_validity: Vec<ItemValidity>,
pub max_contribution: u32,
pub timeout_ms: u32,
pub degradation: ChannelDegradation,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RerankPolicy {
pub enabled: bool,
pub candidate_pool: u32,
pub output_k: u32,
pub timeout_fallback: RerankFallback,
pub require_canonical_evidence_top1: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RerankFallback {
SkipRerank,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct TrustPolicy {
pub minimum_trust: TrustClass,
pub allow_quarantined: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FreshnessPolicy {
pub prefer_current: bool,
pub include_superseded: bool,
pub max_age_days: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AbstentionMode {
Never,
OnLowEvidence,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct AbstentionPolicy {
pub mode: AbstentionMode,
pub min_selected_items: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IntentSource {
Explicit,
KeywordFallback,
DefaultFallback,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResolvedIntent {
pub intent: ContextIntent,
pub source: IntentSource,
pub reason_code: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RetrievalPlan {
pub schema_version: u32,
pub policy_version: String,
pub intent: ContextIntent,
pub intent_source: IntentSource,
pub role: AgentRole,
pub risk: RiskClass,
pub reason_codes: Vec<String>,
pub channel_plans: Vec<ChannelPlan>,
pub filters: ContextFilters,
pub rerank_policy: RerankPolicy,
pub trust_policy: TrustPolicy,
pub freshness_policy: FreshnessPolicy,
pub token_budget: u32,
pub abstention_policy: AbstentionPolicy,
pub plan_hash: String,
}
impl RetrievalPlan {
pub fn enabled_channels(&self) -> Vec<RetrievalChannel> {
self.channel_plans
.iter()
.filter(|c| c.enabled)
.map(|c| c.channel)
.collect()
}
}