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