zeph_core/quality/
config.rs1use serde::{Deserialize, Serialize};
7use thiserror::Error;
8use zeph_config::providers::ProviderName;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
12#[serde(rename_all = "snake_case")]
13pub enum TriggerPolicy {
14 #[default]
16 HasRetrieval,
17 Always,
19 Manual,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct QualityConfig {
35 #[serde(default)]
37 pub self_check: bool,
38
39 #[serde(default)]
44 pub proposer_provider: ProviderName,
45
46 #[serde(default)]
51 pub checker_provider: ProviderName,
52
53 #[serde(default)]
55 pub trigger: TriggerPolicy,
56
57 #[serde(default = "default_min_evidence")]
61 pub min_evidence: f32,
62
63 #[serde(default)]
66 pub async_run: bool,
67
68 #[serde(default = "default_latency_budget_ms")]
70 pub latency_budget_ms: u64,
71
72 #[serde(default = "default_per_call_timeout_ms")]
74 pub per_call_timeout_ms: u64,
75
76 #[serde(default = "default_max_assertions")]
78 pub max_assertions: usize,
79
80 #[serde(default = "default_max_response_chars")]
82 pub max_response_chars: usize,
83
84 #[serde(default = "default_cache_disabled_for_checker")]
86 pub cache_disabled_for_checker: bool,
87
88 #[serde(default = "default_flag_marker")]
90 pub flag_marker: String,
91}
92
93fn default_min_evidence() -> f32 {
94 0.6
95}
96fn default_latency_budget_ms() -> u64 {
97 4_000
98}
99fn default_per_call_timeout_ms() -> u64 {
100 2_000
101}
102fn default_max_assertions() -> usize {
103 12
104}
105fn default_max_response_chars() -> usize {
106 8_000
107}
108fn default_cache_disabled_for_checker() -> bool {
109 true
110}
111fn default_flag_marker() -> String {
112 "[verify]".into()
113}
114
115impl Default for QualityConfig {
116 fn default() -> Self {
117 Self {
118 self_check: false,
119 proposer_provider: ProviderName::default(),
120 checker_provider: ProviderName::default(),
121 trigger: TriggerPolicy::default(),
122 min_evidence: default_min_evidence(),
123 async_run: false,
124 latency_budget_ms: default_latency_budget_ms(),
125 per_call_timeout_ms: default_per_call_timeout_ms(),
126 max_assertions: default_max_assertions(),
127 max_response_chars: default_max_response_chars(),
128 cache_disabled_for_checker: default_cache_disabled_for_checker(),
129 flag_marker: default_flag_marker(),
130 }
131 }
132}
133
134#[derive(Debug, Error)]
136pub enum QualityConfigError {
137 #[error("per_call_timeout_ms ({per_call}) × 2 must be ≤ latency_budget_ms ({budget})")]
138 TimeoutExceedsBudget { per_call: u64, budget: u64 },
139 #[error("min_evidence must be in 0.0..=1.0, got {0}")]
140 InvalidMinEvidence(f32),
141}
142
143impl QualityConfig {
144 pub fn validate(&self) -> Result<(), QualityConfigError> {
151 if 2 * self.per_call_timeout_ms > self.latency_budget_ms {
152 return Err(QualityConfigError::TimeoutExceedsBudget {
153 per_call: self.per_call_timeout_ms,
154 budget: self.latency_budget_ms,
155 });
156 }
157 if !(0.0..=1.0).contains(&self.min_evidence) {
158 return Err(QualityConfigError::InvalidMinEvidence(self.min_evidence));
159 }
160 Ok(())
161 }
162}
163
164impl From<&zeph_config::QualityConfig> for QualityConfig {
165 fn from(c: &zeph_config::QualityConfig) -> Self {
166 Self {
167 self_check: c.self_check,
168 proposer_provider: c.proposer_provider.clone(),
169 checker_provider: c.checker_provider.clone(),
170 trigger: match c.trigger {
171 zeph_config::TriggerPolicy::HasRetrieval => TriggerPolicy::HasRetrieval,
172 zeph_config::TriggerPolicy::Always => TriggerPolicy::Always,
173 zeph_config::TriggerPolicy::Manual => TriggerPolicy::Manual,
174 },
175 min_evidence: c.min_evidence,
176 async_run: c.async_run,
177 latency_budget_ms: c.latency_budget_ms,
178 per_call_timeout_ms: c.per_call_timeout_ms,
179 max_assertions: c.max_assertions,
180 max_response_chars: c.max_response_chars,
181 cache_disabled_for_checker: c.cache_disabled_for_checker,
182 flag_marker: c.flag_marker.clone(),
183 }
184 }
185}