use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use ts_rs::TS;
use super::fingerprint::Fingerprint;
use super::verdict::Verdict;
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct UsageSnapshot {
pub input_total: Option<u64>,
pub input_no_cache: Option<u64>,
pub cache_read: Option<u64>,
pub cache_write: Option<u64>,
pub output_total: Option<u64>,
pub output_text: Option<u64>,
pub output_reasoning: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub raw: Option<Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct RequestCacheHints {
pub requested_write: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct TraceRecord {
pub provider: String,
pub model: String,
pub request_id: Option<String>,
pub session_id: Option<String>,
pub call_id: String,
#[ts(type = "number")]
pub sent_at_unix_ms: i64,
#[serde(skip)]
#[ts(skip)]
pub monotonic_sent_ms: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lcp_token_upper: Option<u64>,
#[ts(type = "number | null")]
pub ttft_ms: Option<u64>,
pub fingerprint: Fingerprint,
pub usage: UsageSnapshot,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub response_cache_headers: Option<HashMap<String, String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub request_cache_hints: Option<RequestCacheHints>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub verdict: Option<Verdict>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip)]
#[ts(skip)]
pub scope_key: u64,
}
impl TraceRecord {
#[must_use]
pub fn reported_hit_rate(&self) -> Option<f64> {
match (self.usage.cache_read, self.usage.input_total) {
(Some(r), Some(t)) if t > 0 => Some(r as f64 / t as f64),
_ => None,
}
}
#[must_use]
pub fn client_upper_bound_hit_rate(&self) -> Option<f64> {
let t = self.usage.input_total?;
if t == 0 {
return None;
}
Some(self.fingerprint.token_estimate.min(t) as f64 / t as f64)
}
}