use serde::{Deserialize, Serialize};
use ts_rs::TS;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)]
#[ts(export)]
pub enum VerdictKind {
Trusted,
SuspectOverclaim,
SuspectUnderclaim,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
#[ts(export)]
pub enum VerdictConfidence {
High,
Medium,
Low,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct Verdict {
pub kind: VerdictKind,
pub confidence: VerdictConfidence,
pub violated: Vec<String>,
pub expected_max: u64,
pub claimed: u64,
pub lcp_bytes: u64,
pub notes: Vec<String>,
}
impl Verdict {
#[must_use]
pub fn describe(&self) -> String {
format!(
"kind={:?} conf={:?} violated={:?} U={} claimed={} lcp={}B notes={:?}",
self.kind,
self.confidence,
self.violated,
self.expected_max,
self.claimed,
self.lcp_bytes,
self.notes
)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct ProviderAuditSpec {
pub family: ProviderFamily,
pub gran: Option<u64>,
pub threshold: u64,
pub ttl_ms: u64,
pub model56plus: bool,
pub shared_ceiling_mult: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
#[ts(export)]
pub enum ProviderFamily {
OpenAi,
DeepSeek,
Anthropic,
Bedrock,
Gemini,
Vllm,
Generic,
}
pub mod matrix {
use super::{ProviderAuditSpec, ProviderFamily};
const HOUR: u64 = 3_600_000;
#[must_use]
pub fn openai_56() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::OpenAi,
gran: None,
threshold: 1024,
ttl_ms: HOUR,
model56plus: true,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn openai_legacy() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::OpenAi,
gran: Some(128),
threshold: 1024,
ttl_ms: HOUR,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn azure() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::OpenAi,
gran: Some(128),
threshold: 1024,
ttl_ms: HOUR,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn deepseek() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::DeepSeek,
gran: Some(64),
threshold: 64,
ttl_ms: 48 * HOUR,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn mistral() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::Generic,
gran: Some(64),
threshold: 64,
ttl_ms: HOUR,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn anthropic() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::Anthropic,
gran: None,
threshold: 512,
ttl_ms: HOUR,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn bedrock() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::Bedrock,
gran: None,
threshold: 0,
ttl_ms: 24 * HOUR,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn gemini() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::Gemini,
gran: None,
threshold: 0,
ttl_ms: 24 * HOUR,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn vllm() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::Vllm,
gran: Some(16),
threshold: 16,
ttl_ms: u64::MAX,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn generic() -> ProviderAuditSpec {
ProviderAuditSpec {
family: ProviderFamily::Generic,
gran: Some(128),
threshold: 1024,
ttl_ms: HOUR,
model56plus: false,
shared_ceiling_mult: None,
}
}
#[must_use]
pub fn for_provider(provider: &str, model: &str) -> ProviderAuditSpec {
let p = provider.to_ascii_lowercase();
let m = model.to_ascii_lowercase();
if p.contains("deepseek") {
return deepseek();
}
if p.contains("anthropic") {
return anthropic();
}
if p.contains("azure") {
return azure();
}
if p.contains("gemini") || p.contains("vertex") {
return gemini();
}
if p.contains("bedrock") {
return bedrock();
}
if p.contains("vllm") {
return vllm();
}
if p.contains("mistral") {
return mistral();
}
if p.contains("openai") || p.contains("openrouter") {
return if m.contains("gpt-5") || m.contains("o3") || m.contains("o4") {
openai_56()
} else {
openai_legacy()
};
}
generic()
}
}
#[derive(Debug, Clone, Copy)]
pub struct LcpInput {
pub lcp_bytes: u64,
pub lcp_upper_bytes: u64,
pub same_session: bool,
pub matched_exists: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SessionStats {
pub same_session_rounds: u32,
pub prefix_stable: bool,
pub lcp_gt_1024: bool,
}
#[derive(Debug, Clone)]
pub struct JudgmentInput {
pub spec: ProviderAuditSpec,
pub strict: bool,
pub first: bool,
pub prompt_tokens: u64,
pub prompt_bytes: u64,
pub claimed: u64,
pub input_no_cache: Option<u64>,
pub input_cache_read: Option<u64>,
pub input_cache_write: Option<u64>,
pub write: Option<u64>,
pub no_cache: Option<u64>,
pub hit: Option<u64>,
pub miss: Option<u64>,
pub usage_present: bool,
pub response_cache_header_hit: bool,
pub candidate_expired: bool,
pub byte_proxy: bool,
pub route_affinity_known: bool,
pub lcp: Option<LcpInput>,
pub system_tokens: u64,
pub session_stats: Option<SessionStats>,
}
#[must_use]
pub fn quantize_down(x: u64, gran: Option<u64>) -> u64 {
match gran {
Some(g) if g > 0 => x / g * g,
_ => x,
}
}
fn tau(u: u64, gran: Option<u64>) -> u64 {
let pct = ((u as f64) * 0.05).round() as u64;
match gran {
Some(g) => pct.max(g),
None => pct,
}
}
fn is_w_level(rule: &str) -> bool {
matches!(
rule,
"R-1.1" | "R-1.1abs" | "R-1.2" | "R-1.3" | "R-1.4" | "R-1.5" | "R-1.7" | "R-1.8" | "R-3.3"
)
}
fn is_b_level(rule: &str) -> bool {
matches!(rule, "R-1.1b" | "R-1.6b" | "R-1.7b")
}
fn is_byte_proxy_rule(rule: &str) -> bool {
matches!(rule, "R-1.1" | "R-1.1b")
}
#[must_use]
pub fn judge(inp: &JudgmentInput) -> Verdict {
let mut v = Verdict {
kind: VerdictKind::Trusted,
confidence: VerdictConfidence::High,
violated: Vec::new(),
expected_max: 0,
claimed: inp.claimed,
lcp_bytes: 0,
notes: Vec::new(),
};
if !inp.usage_present {
v.kind = VerdictKind::Unknown;
v.confidence = VerdictConfidence::Medium;
v.violated.push("R-5.1".into());
v.notes
.push("usage missing (stream truncated / stripped)".into());
return v;
}
if inp.response_cache_header_hit {
v.kind = VerdictKind::Unknown;
v.confidence = VerdictConfidence::Medium;
v.violated.push("R-4.1".into());
v.notes
.push("response-cache header hit; provider cache not audited (R-4.1)".into());
return v;
}
let prompt = inp.prompt_tokens;
let claimed = inp.claimed;
if claimed > prompt {
v.violated.push("R-1.1abs".into());
}
if inp.spec.family == ProviderFamily::DeepSeek
&& let (Some(h), Some(m)) = (inp.hit, inp.miss)
&& (h as i64 + m as i64 - prompt as i64).abs() > 1
{
v.violated.push("R-1.3".into());
}
if inp.spec.family == ProviderFamily::Anthropic {
let (nc, rd, wr) = (
inp.input_no_cache,
inp.input_cache_read,
inp.input_cache_write,
);
if let (Some(nc), Some(rd), Some(wr)) = (nc, rd, wr) {
let total = inp.prompt_tokens;
if (nc as i64 + rd as i64 + wr as i64 - total as i64).abs() > 1 {
v.violated.push("R-1.4".into());
}
}
if inp.first && rd.unwrap_or(0) > 0 {
v.violated.push("R-1.4".into());
}
}
if inp.spec.family == ProviderFamily::Bedrock {
let (nc, rd, wr) = (
inp.input_no_cache,
inp.input_cache_read,
inp.input_cache_write,
);
if let (Some(nc), Some(rd), Some(wr)) = (nc, rd, wr) {
let total = inp.prompt_tokens;
if (nc as i64 + rd as i64 + wr as i64 - total as i64).abs() > 1 {
v.violated.push("R-1.5".into());
}
}
}
if prompt < inp.spec.threshold && claimed > 0 {
v.violated.push("R-3.3".into());
}
if let Some(g) = inp.spec.gran
&& claimed > 0
&& !claimed.is_multiple_of(g)
&& !inp.spec.model56plus
{
v.violated.push("R-1.6b".into());
}
if inp.spec.model56plus {
match (inp.write, inp.no_cache) {
(Some(w), Some(nc)) => {
if claimed + w + nc != prompt {
v.violated.push("R-1.7".into());
}
}
(None, _) => {
v.violated.push("R-1.7b".into());
v.notes
.push("5.6+ write side missing; downgraded to medium (R-1.7)".into());
}
_ => {}
}
}
if inp.first && claimed > 0 {
v.violated.push("R-1.2".into());
}
let mut lcp_present = false;
let mut u: u64 = 0;
let mut granularity_unknown = false;
if let Some(l) = &inp.lcp {
lcp_present = true;
v.lcp_bytes = l.lcp_bytes;
let sys_bytes = inp.system_tokens.saturating_mul(4);
let cap_bytes = if l.same_session {
l.lcp_upper_bytes
} else {
l.lcp_upper_bytes.min(sys_bytes)
};
if !l.same_session && claimed.saturating_mul(4) > sys_bytes {
v.notes
.push("claimed exceeds shared system segment (R-2.1)".into());
}
let upper_bytes = l.lcp_upper_bytes.min(cap_bytes).min(inp.prompt_bytes);
let token_upper = upper_bytes / 4;
u = quantize_down(token_upper, inp.spec.gran).min(prompt);
}
if lcp_present {
v.expected_max = u;
let t = tau(u, inp.spec.gran);
if claimed > u + t {
v.violated.push("R-1.1".into());
} else if claimed > u {
v.violated.push("R-1.1b".into());
}
} else if claimed > 0 && !inp.first {
if inp.candidate_expired {
v.violated.push("R-1.8".into());
} else {
v.notes.push(
"no block-granularity match; cannot confirm or refute (short body / sub-block growth)"
.into(),
);
granularity_unknown = true;
}
}
let mut suppressed_m = false;
if inp.spec.model56plus && claimed == 0 && v.lcp_bytes / 4 > 1024 {
v.notes
.push("5.6+ implicit breakpoint: claimed=0 legal despite large LCP (R-2.3)".into());
suppressed_m = true;
}
if !suppressed_m
&& let Some(ss) = &inp.session_stats
&& ss.same_session_rounds >= 3
&& ss.prefix_stable
&& ss.lcp_gt_1024
&& claimed == 0
{
if inp.route_affinity_known {
v.violated.push("R-2.2".into());
} else {
v.notes.push(
"stable prefix with zero claimed hits; route affinity unknown — \
likely cluster routing artifact, not a broken prefix (R-2.2 suppressed)"
.into(),
);
}
}
const DOWNGRADE_IDS: [&str; 3] = ["R-1.1", "R-1.2", "R-1.8"];
let mut has_w = v.violated.iter().any(|r| is_w_level(r));
let mut has_b = v.violated.iter().any(|r| is_b_level(r));
if !inp.strict {
let only_downgrade_w = has_w
&& v.violated
.iter()
.all(|r| !is_w_level(r) || DOWNGRADE_IDS.contains(&r.as_str()));
if only_downgrade_w {
has_w = false;
if lcp_present {
has_b = true; } else {
v.kind = VerdictKind::Unknown;
v.confidence = VerdictConfidence::Medium;
v.notes
.push("no local history source; other process may have warmed (R-5.3)".into());
return v;
}
}
}
if inp.byte_proxy {
let has_independent = v
.violated
.iter()
.any(|r| (is_w_level(r) || is_b_level(r)) && !is_byte_proxy_rule(r));
if !has_independent && (has_w || has_b) {
v.kind = VerdictKind::Unknown;
v.confidence = VerdictConfidence::Medium;
v.notes.push(
"byte-proxy overclaim (bytes/4 is not a safe token bound) — \
downgraded to Unknown (C4-2)"
.into(),
);
return v;
}
}
if inp.byte_proxy && has_w && !v.violated.iter().any(|r| r == "R-1.1abs") {
let byte_proxy_only = v
.violated
.iter()
.filter(|r| is_w_level(r))
.all(|r| r == "R-1.1");
if byte_proxy_only {
has_w = false;
has_b = true;
v.notes
.push("byte-proxy evidence caps at medium (RFC F2)".into());
}
}
if granularity_unknown && !has_w && !has_b && !v.violated.iter().any(|r| r == "R-2.2") {
v.kind = VerdictKind::Unknown;
v.confidence = VerdictConfidence::Medium;
} else if v.violated.iter().any(|r| r == "R-2.2") && !has_w {
v.kind = VerdictKind::SuspectUnderclaim;
v.confidence = VerdictConfidence::Low;
} else if has_w {
v.kind = VerdictKind::SuspectOverclaim;
v.confidence = VerdictConfidence::High;
} else if has_b {
v.kind = VerdictKind::SuspectOverclaim;
v.confidence = VerdictConfidence::Medium;
} else {
v.kind = VerdictKind::Trusted;
v.confidence = VerdictConfidence::High;
}
v
}