use anyhow::{Context, Result};
use once_cell::sync::OnceCell;
use prometheus::{register_int_counter_vec, IntCounterVec};
use std::sync::Arc;
static MODEL_ROUTING_METRICS: OnceCell<Arc<ModelRoutingMetrics>> = OnceCell::new();
pub fn get_model_routing_metrics() -> Option<Arc<ModelRoutingMetrics>> {
MODEL_ROUTING_METRICS.get().cloned()
}
pub fn init_model_routing_metrics() -> Result<Arc<ModelRoutingMetrics>> {
if let Some(metrics) = MODEL_ROUTING_METRICS.get() {
return Ok(metrics.clone());
}
let metrics = Arc::new(ModelRoutingMetrics::new()?);
let _ = MODEL_ROUTING_METRICS.set(metrics.clone());
Ok(metrics)
}
pub struct ModelRoutingMetrics {
model_routed: IntCounterVec,
default_upstream_used: IntCounterVec,
no_model_header: IntCounterVec,
provider_override: IntCounterVec,
}
impl ModelRoutingMetrics {
pub fn new() -> Result<Self> {
let model_routed = register_int_counter_vec!(
"zentinel_model_routing_total",
"Total number of requests routed based on model name",
&["route", "model", "upstream"]
)
.context("Failed to register model_routing metric")?;
let default_upstream_used = register_int_counter_vec!(
"zentinel_model_routing_default_total",
"Requests falling back to default upstream (no pattern matched)",
&["route"]
)
.context("Failed to register model_routing_default metric")?;
let no_model_header = register_int_counter_vec!(
"zentinel_model_routing_no_header_total",
"Requests with no model header detected",
&["route"]
)
.context("Failed to register model_routing_no_header metric")?;
let provider_override = register_int_counter_vec!(
"zentinel_model_routing_provider_override_total",
"Requests where provider was overridden by model routing",
&["route", "upstream", "provider"]
)
.context("Failed to register model_routing_provider_override metric")?;
Ok(Self {
model_routed,
default_upstream_used,
no_model_header,
provider_override,
})
}
pub fn record_model_routed(&self, route: &str, model: &str, upstream: &str) {
self.model_routed
.with_label_values(&[route, model, upstream])
.inc();
}
pub fn record_default_upstream(&self, route: &str) {
self.default_upstream_used.with_label_values(&[route]).inc();
}
pub fn record_no_model_header(&self, route: &str) {
self.no_model_header.with_label_values(&[route]).inc();
}
pub fn record_provider_override(&self, route: &str, upstream: &str, provider: &str) {
self.provider_override
.with_label_values(&[route, upstream, provider])
.inc();
}
}