use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MetricClass {
#[default]
Goal,
Guardrail,
Diagnostic,
}
impl MetricClass {
pub const fn affects_case_verdict(self) -> bool {
matches!(self, Self::Goal | Self::Guardrail)
}
pub const fn as_str(self) -> &'static str {
match self {
Self::Goal => "goal",
Self::Guardrail => "guardrail",
Self::Diagnostic => "diagnostic",
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MetricDecision {
#[default]
BlockRelease,
RollBack,
Investigate,
ObserveOnly,
}
impl MetricDecision {
pub const fn is_blocking(self) -> bool {
matches!(self, Self::BlockRelease | Self::RollBack)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct MetricSpec {
#[serde(default)]
pub class: MetricClass,
#[serde(default)]
pub decision: MetricDecision,
#[serde(default)]
pub owner: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retire_after_idle_runs: Option<u32>,
}
impl Default for MetricSpec {
fn default() -> Self {
Self {
class: MetricClass::Goal,
decision: MetricDecision::BlockRelease,
owner: String::new(),
retire_after_idle_runs: None,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JudgeCalibration {
pub labeled_samples: u64,
pub agreed_samples: u64,
}
impl JudgeCalibration {
pub fn agreement(&self) -> f64 {
if self.labeled_samples == 0 {
0.0
} else {
self.agreed_samples as f64 / self.labeled_samples as f64
}
}
}
pub const JUDGE_PROMOTION_MIN_LABELS: u64 = 50;
pub const JUDGE_PROMOTION_MIN_AGREEMENT: f64 = 0.9;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RateSlice {
pub observed: u64,
pub passed: u64,
}
impl RateSlice {
pub fn rate(&self) -> f64 {
if self.observed == 0 {
0.0
} else {
self.passed as f64 / self.observed as f64
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricSummary {
pub evaluator_id: String,
pub evaluator_version: String,
pub class: MetricClass,
#[serde(default)]
pub decision: MetricDecision,
pub observed: u64,
pub passed: u64,
pub rate: f64,
pub wilson_low: f64,
pub wilson_high: f64,
#[serde(default)]
pub cost_micros: u64,
#[serde(default)]
pub by_category: std::collections::BTreeMap<String, RateSlice>,
#[serde(default)]
pub by_variant: std::collections::BTreeMap<String, RateSlice>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ListMetricCandidatesRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub run_id: Option<String>,
#[serde(default = "default_min_occurrences")]
pub minimum_occurrences: u64,
}
fn default_min_occurrences() -> u64 {
3
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricCandidate {
pub label: String,
pub occurrences: u64,
pub failed_occurrences: u64,
pub correlation: f64,
pub recommended: bool,
pub rationale: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricCandidateReport {
pub labeled_reviews: u64,
pub failed_reviews: u64,
pub candidates: Vec<MetricCandidate>,
}
fn default_health_window() -> u32 {
100
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct MetricHealthRequest {
#[serde(default = "default_health_window")]
pub window_runs: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub evaluator_id: Option<String>,
}
impl Default for MetricHealthRequest {
fn default() -> Self {
Self {
window_runs: default_health_window(),
evaluator_id: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MetricRecommendation {
Keep,
Retire,
Investigate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricObservation {
pub evaluator_id: String,
pub evaluator_version: String,
pub class: MetricClass,
pub run_id: String,
pub observed: u64,
pub passed: u64,
pub cost_micros: u64,
pub variant_count: u64,
pub discriminated: bool,
pub created_at_ms: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricHealth {
pub evaluator_id: String,
pub evaluator_version: String,
pub class: MetricClass,
pub runs: u64,
pub observed: u64,
pub passed: u64,
pub rate: f64,
pub idle_runs: u64,
pub discrimination: f64,
pub discrimination_opportunities: u64,
pub cost_micros_per_run: u64,
pub labeled_samples: u64,
pub human_agreement: f64,
pub recommendation: MetricRecommendation,
pub rationale: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricHealthReport {
pub metrics: Vec<MetricHealth>,
}