///|
/// Logical scheduling counters used by offline simulations. target_batches
/// is not an observed number of model API calls; TreeExperiment separately
/// records actual target_queries from its provider callbacks.
pub struct DecodeMetrics {
mut target_batches : Int
mut draft_tokens : Int
mut emitted_tokens : Int
mut accepted_tokens : Int
mut rejected_rounds : Int
}
///|
pub fn DecodeMetrics::empty() -> DecodeMetrics {
{
target_batches: 0,
draft_tokens: 0,
emitted_tokens: 0,
accepted_tokens: 0,
rejected_rounds: 0,
}
}
///|
/// Copy counters to avoid mutating historical reports through shared state.
pub fn DecodeMetrics::copy(self : DecodeMetrics) -> DecodeMetrics {
{
target_batches: self.target_batches,
draft_tokens: self.draft_tokens,
emitted_tokens: self.emitted_tokens,
accepted_tokens: self.accepted_tokens,
rejected_rounds: self.rejected_rounds,
}
}
///|
pub fn DecodeMetrics::record(
self : DecodeMetrics,
result : VerifyResult,
proposal : DraftProposal,
) -> Unit {
self.target_batches = self.target_batches + 1
self.draft_tokens = self.draft_tokens + proposal.length()
self.emitted_tokens = self.emitted_tokens + result.emitted.length()
self.accepted_tokens = self.accepted_tokens + result.accepted_count
if result.rejected_at is Some(_) {
self.rejected_rounds = self.rejected_rounds + 1
}
}
///|
pub fn DecodeMetrics::acceptance_rate(self : DecodeMetrics) -> Double {
if self.draft_tokens == 0 {
0.0
} else {
self.accepted_tokens.to_double() / self.draft_tokens.to_double()
}
}
///|
pub fn DecodeMetrics::tokens_per_target_batch(self : DecodeMetrics) -> Double {
if self.target_batches == 0 {
0.0
} else {
self.emitted_tokens.to_double() / self.target_batches.to_double()
}
}