use crate::value::VmDictExt;
use crate::value::VmValue;
pub mod source {
pub const OLLAMA_CHAT: &str = "ollama_chat";
pub const OLLAMA_GENERATE: &str = "ollama_generate";
pub const OPENAI_USAGE: &str = "openai_usage";
pub const LLAMACPP_TIMINGS: &str = "llamacpp_timings";
pub const ANTHROPIC_USAGE: &str = "anthropic_usage";
pub const GEMINI_USAGE: &str = "gemini_usage";
pub const GEMINI_INTERACTIONS_USAGE: &str = "gemini_interactions_usage";
pub const MOCK_REPLAY: &str = "mock_replay";
pub const UNKNOWN: &str = "unknown";
}
pub(crate) fn elapsed_ms(started: std::time::Instant) -> u64 {
started.elapsed().as_millis().min(u128::from(u64::MAX)) as u64
}
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ProviderTelemetry {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub source: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub serving_base_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub serving_fingerprint: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_accounting_declared: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_total_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_load_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_prompt_eval_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_generation_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_prompt_tokens: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_uncached_prompt_tokens: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_cached_prompt_tokens: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_output_tokens: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_wall_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_first_frame_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub runtime_context_length: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub runtime_loaded_model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub runtime_memory_bytes: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub runtime_memory_vram_bytes: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub runtime_keep_alive_until: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider_cost_usd: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider_metadata: Option<serde_json::Value>,
}
impl ProviderTelemetry {
pub fn new(source: &str) -> Self {
Self {
source: source.to_string(),
..Self::default()
}
}
pub(crate) fn mock_replay(simulated_cost_usd: Option<f64>) -> Self {
Self {
source: source::MOCK_REPLAY.to_string(),
provider_metadata: simulated_cost_usd
.map(|cost_usd| serde_json::json!({"harn_simulated_cost_usd": cost_usd})),
..Self::default()
}
}
pub(crate) fn mock_replay_cost_usd(&self) -> Option<f64> {
(self.source == source::MOCK_REPLAY).then(|| {
self.provider_metadata
.as_ref()
.and_then(|metadata| metadata.get("harn_simulated_cost_usd"))
.and_then(serde_json::Value::as_f64)
.unwrap_or(0.0)
})
}
pub fn is_empty(&self) -> bool {
let Self {
source,
serving_base_url,
serving_fingerprint,
cache_accounting_declared,
server_total_ms,
server_load_ms,
server_prompt_eval_ms,
server_generation_ms,
server_prompt_tokens,
server_uncached_prompt_tokens,
server_cached_prompt_tokens,
server_output_tokens,
client_wall_ms,
client_first_frame_ms,
runtime_context_length,
runtime_loaded_model,
response_model,
runtime_memory_bytes,
runtime_memory_vram_bytes,
runtime_keep_alive_until,
request_id,
provider_cost_usd,
provider_metadata,
} = self;
source.is_empty()
&& serving_base_url.is_none()
&& serving_fingerprint.is_none()
&& cache_accounting_declared.is_none()
&& server_total_ms.is_none()
&& server_load_ms.is_none()
&& server_prompt_eval_ms.is_none()
&& server_generation_ms.is_none()
&& server_prompt_tokens.is_none()
&& server_uncached_prompt_tokens.is_none()
&& server_cached_prompt_tokens.is_none()
&& server_output_tokens.is_none()
&& client_wall_ms.is_none()
&& client_first_frame_ms.is_none()
&& runtime_context_length.is_none()
&& runtime_loaded_model.is_none()
&& response_model.is_none()
&& runtime_memory_bytes.is_none()
&& runtime_memory_vram_bytes.is_none()
&& runtime_keep_alive_until.is_none()
&& request_id.is_none()
&& provider_cost_usd.is_none()
&& provider_metadata.is_none()
}
pub fn ns_to_ms(ns: u64) -> u64 {
ns / 1_000_000
}
pub fn from_ollama_done(frame: &serde_json::Value, source: &str) -> Self {
let mut telemetry = Self::new(source);
telemetry.server_total_ms = ns_field(frame, "total_duration");
telemetry.server_load_ms = ns_field(frame, "load_duration");
telemetry.server_prompt_eval_ms = ns_field(frame, "prompt_eval_duration");
telemetry.server_generation_ms = ns_field(frame, "eval_duration");
telemetry.server_prompt_tokens = frame
.get("prompt_eval_count")
.and_then(serde_json::Value::as_i64);
telemetry.server_output_tokens =
frame.get("eval_count").and_then(serde_json::Value::as_i64);
if let Some(model) = frame.get("model").and_then(serde_json::Value::as_str) {
telemetry.runtime_loaded_model = Some(model.to_string());
}
telemetry
}
pub fn from_openai_response(response: &serde_json::Value, request_id: Option<&str>) -> Self {
let usage = response.get("usage").unwrap_or(&serde_json::Value::Null);
let mut telemetry = Self::new(source::OPENAI_USAGE);
telemetry.server_prompt_tokens = usage
.get("prompt_tokens")
.or_else(|| usage.get("input_tokens"))
.and_then(serde_json::Value::as_i64);
telemetry.server_output_tokens = usage
.get("completion_tokens")
.or_else(|| usage.get("output_tokens"))
.and_then(serde_json::Value::as_i64);
if let Some(timings) = response
.get("timings")
.filter(|value| value.is_object())
.or_else(|| usage.get("timings").filter(|value| value.is_object()))
{
telemetry.source = source::LLAMACPP_TIMINGS.to_string();
telemetry.server_prompt_eval_ms = ms_or_round(timings.get("prompt_ms"));
telemetry.server_generation_ms = ms_or_round(timings.get("predicted_ms"));
telemetry.server_uncached_prompt_tokens =
timings.get("prompt_n").and_then(serde_json::Value::as_i64);
telemetry.server_cached_prompt_tokens =
timings.get("cache_n").and_then(serde_json::Value::as_i64);
if let Some(predicted) = timings
.get("predicted_n")
.and_then(serde_json::Value::as_i64)
{
telemetry.server_output_tokens = Some(predicted);
}
let total = telemetry
.server_prompt_eval_ms
.unwrap_or(0)
.saturating_add(telemetry.server_generation_ms.unwrap_or(0));
if total > 0 {
telemetry.server_total_ms = Some(total);
}
}
if let Some(request_id) = request_id.filter(|value| !value.is_empty()) {
telemetry.request_id = Some(request_id.to_string());
}
let direct_cost_usd = usage
.get("cost")
.or_else(|| usage.get("total_cost"))
.or_else(|| usage.get("estimated_cost"))
.and_then(serde_json::Value::as_f64)
.filter(|cost| cost.is_finite() && *cost >= 0.0);
telemetry.provider_cost_usd = direct_cost_usd.or_else(|| {
const XAI_USD_TICKS_PER_DOLLAR: f64 = 10_000_000_000.0;
usage
.get("cost_in_usd_ticks")
.and_then(serde_json::Value::as_f64)
.filter(|ticks| ticks.is_finite() && *ticks >= 0.0)
.map(|ticks| ticks / XAI_USD_TICKS_PER_DOLLAR)
});
telemetry.capture_provider_metadata(response);
telemetry
}
pub fn capture_request_id(&mut self, request_id: Option<&str>) {
if self.request_id.is_none() {
self.request_id = request_id
.filter(|value| !value.is_empty())
.map(str::to_string);
}
}
pub fn from_anthropic_usage(usage: &serde_json::Value, request_id: Option<&str>) -> Self {
let mut telemetry = Self::new(source::ANTHROPIC_USAGE);
telemetry.server_prompt_tokens = usage
.get("input_tokens")
.and_then(serde_json::Value::as_i64);
telemetry.server_output_tokens = usage
.get("output_tokens")
.and_then(serde_json::Value::as_i64);
if let Some(request_id) = request_id.filter(|value| !value.is_empty()) {
telemetry.request_id = Some(request_id.to_string());
}
telemetry
}
pub fn from_gemini_usage(usage: &serde_json::Value, request_id: Option<&str>) -> Self {
let mut telemetry = Self::new(source::GEMINI_USAGE);
telemetry.server_prompt_tokens = usage
.get("promptTokenCount")
.and_then(serde_json::Value::as_i64);
telemetry.server_output_tokens = usage
.get("candidatesTokenCount")
.and_then(serde_json::Value::as_i64);
if let Some(request_id) = request_id.filter(|value| !value.is_empty()) {
telemetry.request_id = Some(request_id.to_string());
}
telemetry
}
pub fn from_gemini_interactions_usage(
usage: &serde_json::Value,
request_id: Option<&str>,
) -> Self {
let mut telemetry = Self::new(source::GEMINI_INTERACTIONS_USAGE);
telemetry.server_prompt_tokens = usage
.get("total_input_tokens")
.and_then(serde_json::Value::as_i64);
telemetry.server_output_tokens = usage
.get("total_output_tokens")
.and_then(serde_json::Value::as_i64);
if let Some(request_id) = request_id.filter(|value| !value.is_empty()) {
telemetry.request_id = Some(request_id.to_string());
}
telemetry
}
pub fn capture_provider_metadata(&mut self, response: &serde_json::Value) {
if let Some(model) = response
.get("model")
.and_then(serde_json::Value::as_str)
.filter(|value| !value.is_empty())
{
self.response_model = Some(model.to_string());
}
if let Some(fingerprint) = response
.get("system_fingerprint")
.and_then(serde_json::Value::as_str)
.filter(|value| !value.is_empty())
{
self.serving_fingerprint = Some(fingerprint.to_string());
}
if let Some(metadata) = response
.get("provider_metadata")
.filter(|value| !value.is_null())
.filter(|value| !value.as_object().is_some_and(serde_json::Map::is_empty))
{
merge_provider_metadata(&mut self.provider_metadata, metadata);
}
if let Some(receipt) = response
.get(crate::llm::managed_supply::MANAGED_SUPPLY_WIRE_KEY)
.filter(|value| !value.is_null())
{
let mut managed = serde_json::Map::new();
managed.insert(
crate::llm::managed_supply::MANAGED_SUPPLY_WIRE_KEY.to_string(),
receipt.clone(),
);
merge_provider_metadata(
&mut self.provider_metadata,
&serde_json::Value::Object(managed),
);
}
}
pub fn merge_ollama_ps(&mut self, ps: &OllamaPsModel) {
if self.runtime_loaded_model.is_none() {
self.runtime_loaded_model = ps.name.clone();
}
if self.runtime_context_length.is_none() {
self.runtime_context_length = ps.context_length;
}
if self.runtime_memory_bytes.is_none() {
self.runtime_memory_bytes = ps.size_bytes;
}
if self.runtime_memory_vram_bytes.is_none() {
self.runtime_memory_vram_bytes = ps.size_vram_bytes;
}
if self.runtime_keep_alive_until.is_none() {
self.runtime_keep_alive_until = ps.expires_at.clone();
}
}
pub fn as_vm_dict(&self) -> Option<VmValue> {
if self.is_empty() {
return None;
}
let mut dict: crate::value::DictMap = crate::value::DictMap::new();
if !self.source.is_empty() {
dict.put_str("source", self.source.as_str());
}
if let Some(ref serving_base_url) = self.serving_base_url {
dict.put_str("serving_base_url", serving_base_url.as_str());
}
if let Some(ref serving_fingerprint) = self.serving_fingerprint {
dict.put_str("serving_fingerprint", serving_fingerprint.as_str());
}
insert_opt_u64(&mut dict, "server_total_ms", self.server_total_ms);
insert_opt_u64(&mut dict, "server_load_ms", self.server_load_ms);
insert_opt_u64(
&mut dict,
"server_prompt_eval_ms",
self.server_prompt_eval_ms,
);
insert_opt_u64(&mut dict, "server_generation_ms", self.server_generation_ms);
insert_opt_i64(&mut dict, "server_prompt_tokens", self.server_prompt_tokens);
insert_opt_i64(
&mut dict,
"server_uncached_prompt_tokens",
self.server_uncached_prompt_tokens,
);
insert_opt_i64(
&mut dict,
"server_cached_prompt_tokens",
self.server_cached_prompt_tokens,
);
insert_opt_i64(&mut dict, "server_output_tokens", self.server_output_tokens);
insert_opt_u64(&mut dict, "client_wall_ms", self.client_wall_ms);
insert_opt_u64(
&mut dict,
"client_first_frame_ms",
self.client_first_frame_ms,
);
insert_opt_u64(
&mut dict,
"runtime_context_length",
self.runtime_context_length,
);
if let Some(ref model) = self.runtime_loaded_model {
dict.put_str("runtime_loaded_model", model.as_str());
}
if let Some(ref model) = self.response_model {
dict.put_str("response_model", model.as_str());
}
insert_opt_u64(&mut dict, "runtime_memory_bytes", self.runtime_memory_bytes);
insert_opt_u64(
&mut dict,
"runtime_memory_vram_bytes",
self.runtime_memory_vram_bytes,
);
if let Some(ref expires) = self.runtime_keep_alive_until {
dict.put_str("runtime_keep_alive_until", expires.as_str());
}
if let Some(ref request_id) = self.request_id {
dict.put_str("request_id", request_id.as_str());
}
if let Some(provider_cost_usd) = self.provider_cost_usd {
dict.insert(
crate::value::intern_key("provider_cost_usd"),
VmValue::Float(provider_cost_usd),
);
}
if let Some(ref provider_metadata) = self.provider_metadata {
dict.insert(
crate::value::intern_key("provider_metadata"),
crate::stdlib::json_to_vm_value(provider_metadata),
);
}
Some(VmValue::dict(dict))
}
}
fn merge_provider_metadata(target: &mut Option<serde_json::Value>, incoming: &serde_json::Value) {
let Some(incoming) = incoming.as_object() else {
return;
};
let target = target.get_or_insert_with(|| serde_json::Value::Object(Default::default()));
let Some(target) = target.as_object_mut() else {
return;
};
for (key, value) in incoming {
target.insert(key.clone(), value.clone());
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct OllamaPsModel {
pub name: Option<String>,
pub size_bytes: Option<u64>,
pub size_vram_bytes: Option<u64>,
pub expires_at: Option<String>,
pub context_length: Option<u64>,
}
impl OllamaPsModel {
pub fn from_ps_entry(entry: &serde_json::Value) -> Option<Self> {
let name = entry
.get("name")
.and_then(serde_json::Value::as_str)
.or_else(|| entry.get("model").and_then(serde_json::Value::as_str))
.map(str::to_string);
let context_length = entry
.get("context_length")
.and_then(serde_json::Value::as_u64)
.or_else(|| {
entry
.get("details")
.and_then(|details| details.get("context_length"))
.and_then(serde_json::Value::as_u64)
});
Some(Self {
name,
size_bytes: entry.get("size").and_then(serde_json::Value::as_u64),
size_vram_bytes: entry.get("size_vram").and_then(serde_json::Value::as_u64),
expires_at: entry
.get("expires_at")
.and_then(serde_json::Value::as_str)
.map(str::to_string),
context_length,
})
}
}
fn ns_field(frame: &serde_json::Value, key: &str) -> Option<u64> {
frame
.get(key)
.and_then(serde_json::Value::as_u64)
.map(ProviderTelemetry::ns_to_ms)
}
fn ms_or_round(value: Option<&serde_json::Value>) -> Option<u64> {
let value = value?;
if let Some(n) = value.as_u64() {
return Some(n);
}
value.as_f64().map(|n| n.round().max(0.0) as u64)
}
fn insert_opt_u64(dict: &mut crate::value::DictMap, key: &str, value: Option<u64>) {
if let Some(value) = value {
dict.insert(crate::value::intern_key(key), VmValue::Int(value as i64));
}
}
fn insert_opt_i64(dict: &mut crate::value::DictMap, key: &str, value: Option<i64>) {
if let Some(value) = value {
dict.insert(crate::value::intern_key(key), VmValue::Int(value));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ollama_done_frame_extracts_full_breakdown() {
let frame = serde_json::json!({
"model": "devstral-small-2:24b",
"total_duration": 7_400_000_000u64,
"load_duration": 400_000_000u64,
"prompt_eval_duration": 1_200_000_000u64,
"eval_duration": 5_800_000_000u64,
"prompt_eval_count": 1024,
"eval_count": 64
});
let telemetry = ProviderTelemetry::from_ollama_done(&frame, source::OLLAMA_CHAT);
assert_eq!(telemetry.source, source::OLLAMA_CHAT);
assert_eq!(telemetry.server_total_ms, Some(7400));
assert_eq!(telemetry.server_load_ms, Some(400));
assert_eq!(telemetry.server_prompt_eval_ms, Some(1200));
assert_eq!(telemetry.server_generation_ms, Some(5800));
assert_eq!(telemetry.server_prompt_tokens, Some(1024));
assert_eq!(telemetry.server_output_tokens, Some(64));
assert_eq!(
telemetry.runtime_loaded_model.as_deref(),
Some("devstral-small-2:24b")
);
assert!(!telemetry.is_empty());
}
#[test]
fn ollama_done_frame_leaves_missing_fields_as_none() {
let frame = serde_json::json!({
"model": "devstral-small-2:24b",
});
let telemetry = ProviderTelemetry::from_ollama_done(&frame, source::OLLAMA_CHAT);
assert_eq!(telemetry.server_total_ms, None);
assert_eq!(telemetry.server_load_ms, None);
assert_eq!(telemetry.server_prompt_eval_ms, None);
assert_eq!(telemetry.server_generation_ms, None);
assert_eq!(telemetry.server_prompt_tokens, None);
assert_eq!(telemetry.server_output_tokens, None);
}
#[test]
fn openai_usage_extracts_counts_and_provider_cost() {
let response = serde_json::json!({
"usage": {
"prompt_tokens": 200,
"completion_tokens": 50,
"cost": 0.00125
}
});
let telemetry = ProviderTelemetry::from_openai_response(&response, Some("req-abc"));
assert_eq!(telemetry.source, source::OPENAI_USAGE);
assert_eq!(telemetry.server_prompt_tokens, Some(200));
assert_eq!(telemetry.server_output_tokens, Some(50));
assert_eq!(telemetry.server_prompt_eval_ms, None);
assert_eq!(telemetry.request_id.as_deref(), Some("req-abc"));
assert_eq!(telemetry.provider_cost_usd, Some(0.00125));
}
#[test]
fn openai_usage_cost_names_keep_precedence_and_validate_estimates() {
let all_names = serde_json::json!({
"usage": {
"cost": 0.001,
"total_cost": 0.002,
"estimated_cost": 0.003
}
});
let total_and_estimate = serde_json::json!({
"usage": {
"total_cost": 0.002,
"estimated_cost": 0.003
}
});
let estimate_only = serde_json::json!({
"usage": { "estimated_cost": 0.003 }
});
assert_eq!(
ProviderTelemetry::from_openai_response(&all_names, None).provider_cost_usd,
Some(0.001)
);
assert_eq!(
ProviderTelemetry::from_openai_response(&total_and_estimate, None).provider_cost_usd,
Some(0.002)
);
assert_eq!(
ProviderTelemetry::from_openai_response(&estimate_only, None).provider_cost_usd,
Some(0.003)
);
for invalid in [serde_json::json!(-0.003), serde_json::json!("0.003")] {
let response = serde_json::json!({
"usage": { "estimated_cost": invalid }
});
assert_eq!(
ProviderTelemetry::from_openai_response(&response, None).provider_cost_usd,
None
);
}
}
#[test]
fn openai_usage_converts_xai_cost_ticks_after_direct_cost_fields() {
let ticks_only = serde_json::json!({
"usage": { "cost_in_usd_ticks": 3_546_000 }
});
let direct_and_ticks = serde_json::json!({
"usage": {
"estimated_cost": 0.0002736,
"cost_in_usd_ticks": 3_546_000
}
});
assert_eq!(
ProviderTelemetry::from_openai_response(&ticks_only, None).provider_cost_usd,
Some(0.0003546)
);
assert_eq!(
ProviderTelemetry::from_openai_response(&direct_and_ticks, None).provider_cost_usd,
Some(0.0002736)
);
assert_eq!(
ProviderTelemetry::from_openai_response(
&serde_json::json!({"usage": {"cost_in_usd_ticks": 0}}),
None,
)
.provider_cost_usd,
Some(0.0)
);
for invalid in [
serde_json::json!(-3_546_000),
serde_json::json!("3546000"),
serde_json::Value::Null,
] {
let response = serde_json::json!({
"usage": { "cost_in_usd_ticks": invalid }
});
assert_eq!(
ProviderTelemetry::from_openai_response(&response, None).provider_cost_usd,
None
);
}
}
#[test]
fn nested_llamacpp_timings_remain_a_fallback() {
let response = serde_json::json!({
"usage": {
"prompt_tokens": 220,
"completion_tokens": 17,
"timings": {
"prompt_n": 200,
"cache_n": 20,
"prompt_ms": 145.4,
"predicted_n": 17,
"predicted_ms": 89.1
}
}
});
let telemetry = ProviderTelemetry::from_openai_response(&response, None);
assert_eq!(telemetry.source, source::LLAMACPP_TIMINGS);
assert_eq!(telemetry.server_prompt_eval_ms, Some(145));
assert_eq!(telemetry.server_generation_ms, Some(89));
assert_eq!(telemetry.server_total_ms, Some(234));
assert_eq!(telemetry.server_prompt_tokens, Some(220));
assert_eq!(telemetry.server_uncached_prompt_tokens, Some(200));
assert_eq!(telemetry.server_cached_prompt_tokens, Some(20));
assert_eq!(telemetry.server_output_tokens, Some(17));
assert!(!telemetry.is_empty());
}
#[test]
fn ps_entry_pulls_context_length_from_top_level_or_details() {
let entry = serde_json::json!({
"name": "devstral-small-2:24b",
"size": 4_700_000_000u64,
"size_vram": 4_500_000_000u64,
"expires_at": "2026-05-14T10:30:00Z",
"context_length": 32768
});
let model = OllamaPsModel::from_ps_entry(&entry).expect("ps entry parses");
assert_eq!(model.context_length, Some(32768));
let entry_nested = serde_json::json!({
"name": "devstral-small-2:24b",
"details": {"context_length": 16384}
});
let nested = OllamaPsModel::from_ps_entry(&entry_nested).expect("ps entry parses");
assert_eq!(nested.context_length, Some(16384));
}
#[test]
fn merge_ollama_ps_preserves_call_level_values() {
let mut telemetry = ProviderTelemetry::new(source::OLLAMA_CHAT);
telemetry.runtime_loaded_model = Some("real-model".to_string());
let ps = OllamaPsModel {
name: Some("alias-model".to_string()),
size_bytes: Some(1),
size_vram_bytes: Some(2),
expires_at: Some("forever".to_string()),
context_length: Some(8192),
};
telemetry.merge_ollama_ps(&ps);
assert_eq!(
telemetry.runtime_loaded_model.as_deref(),
Some("real-model")
);
assert_eq!(telemetry.runtime_memory_bytes, Some(1));
assert_eq!(telemetry.runtime_memory_vram_bytes, Some(2));
assert_eq!(
telemetry.runtime_keep_alive_until.as_deref(),
Some("forever")
);
assert_eq!(telemetry.runtime_context_length, Some(8192));
}
#[test]
fn as_vm_dict_returns_none_when_empty() {
let telemetry = ProviderTelemetry::default();
assert!(telemetry.is_empty());
assert!(telemetry.as_vm_dict().is_none());
}
#[test]
fn as_vm_dict_serializes_all_present_fields() {
let telemetry = ProviderTelemetry {
source: source::OLLAMA_CHAT.to_string(),
serving_base_url: Some("https://provider.example/v1".to_string()),
server_total_ms: Some(100),
client_wall_ms: Some(120),
runtime_loaded_model: Some("qwen".to_string()),
..Default::default()
};
let value = telemetry.as_vm_dict().expect("dict present");
let dict = value.as_dict().expect("dict body");
assert_eq!(
dict.get("source").map(VmValue::display).as_deref(),
Some(source::OLLAMA_CHAT)
);
assert_eq!(
dict.get("serving_base_url")
.map(VmValue::display)
.as_deref(),
Some("https://provider.example/v1")
);
assert_eq!(
dict.get("server_total_ms").and_then(|v| match v {
VmValue::Int(n) => Some(*n),
_ => None,
}),
Some(100)
);
assert_eq!(
dict.get("client_wall_ms").and_then(|v| match v {
VmValue::Int(n) => Some(*n),
_ => None,
}),
Some(120)
);
}
const RUST_INTERNAL_TELEMETRY_FIELDS: &[&str] = &["cache_accounting_declared"];
#[test]
fn as_vm_dict_projects_every_serialized_field() {
let telemetry = ProviderTelemetry {
source: source::OLLAMA_CHAT.to_string(),
serving_base_url: Some("https://provider.example/v1".to_string()),
serving_fingerprint: Some("build-1".to_string()),
cache_accounting_declared: Some(true),
server_total_ms: Some(100),
server_load_ms: Some(1),
server_prompt_eval_ms: Some(2),
server_generation_ms: Some(3),
server_prompt_tokens: Some(4),
server_uncached_prompt_tokens: Some(2),
server_cached_prompt_tokens: Some(2),
server_output_tokens: Some(5),
client_wall_ms: Some(2_000),
client_first_frame_ms: Some(1_500),
runtime_context_length: Some(8_192),
runtime_loaded_model: Some("served-model".to_string()),
response_model: Some("response-model".to_string()),
runtime_memory_bytes: Some(6),
runtime_memory_vram_bytes: Some(7),
runtime_keep_alive_until: Some("2026-01-01T00:00:00Z".to_string()),
request_id: Some("req-1".to_string()),
provider_cost_usd: Some(0.5),
provider_metadata: Some(serde_json::json!({"tier": "standard"})),
};
let encoded = serde_json::to_value(&telemetry).expect("telemetry serializes");
let encoded = encoded.as_object().expect("telemetry is a JSON object");
let value = telemetry.as_vm_dict().expect("dict present");
let dict = value.as_dict().expect("dict body");
for key in encoded.keys() {
if RUST_INTERNAL_TELEMETRY_FIELDS.contains(&key.as_str()) {
continue;
}
assert!(
dict.get(key.as_str()).is_some(),
"{key} reaches the transcript but not the VM dict: add it to \
as_vm_dict, or to RUST_INTERNAL_TELEMETRY_FIELDS with a reason"
);
}
for exempt in RUST_INTERNAL_TELEMETRY_FIELDS {
assert!(
dict.get(*exempt).is_none(),
"{exempt} is now projected; drop it from \
RUST_INTERNAL_TELEMETRY_FIELDS"
);
}
assert_eq!(
encoded.len(),
23,
"every ProviderTelemetry field must be populated for the census to \
cover it; update this count when the struct gains a field"
);
}
#[test]
fn an_absent_first_frame_is_omitted_rather_than_zeroed() {
let telemetry = ProviderTelemetry {
source: source::OLLAMA_CHAT.to_string(),
client_wall_ms: Some(2_000),
..Default::default()
};
let value = telemetry.as_vm_dict().expect("dict present");
let dict = value.as_dict().expect("dict body");
assert!(
dict.get("client_first_frame_ms").is_none(),
"absent must not project as 0 into the VM dict"
);
let encoded = serde_json::to_value(&telemetry).expect("telemetry serializes");
assert!(
encoded.get("client_first_frame_ms").is_none(),
"absent must not serialize as 0 into the artifact"
);
}
#[test]
fn gateway_provider_metadata_is_preserved_without_schema_coupling() {
let response = serde_json::json!({
"model": "served-adapter",
"provider_metadata": {
"gateway": {
"routing": {
"resolvedProvider": "openai",
"modelAttemptCount": 1
},
"cost": "0.00003865"
}
}
});
let mut telemetry = ProviderTelemetry::default();
telemetry.capture_provider_metadata(&response);
assert_eq!(
telemetry
.provider_metadata
.as_ref()
.and_then(|metadata| metadata.pointer("/gateway/routing/resolvedProvider"))
.and_then(serde_json::Value::as_str),
Some("openai")
);
assert_eq!(telemetry.response_model.as_deref(), Some("served-adapter"));
assert!(!telemetry.is_empty());
let value = telemetry
.as_vm_dict()
.expect("metadata makes telemetry visible");
let dict = value.as_dict().expect("dict body");
assert!(dict.get("provider_metadata").is_some());
assert_eq!(
dict.get("response_model").map(VmValue::display).as_deref(),
Some("served-adapter")
);
}
#[test]
fn system_fingerprint_is_captured_and_projected() {
let response = serde_json::json!({
"model": "served-adapter",
"system_fingerprint": "b10360-48d22e295"
});
let mut telemetry = ProviderTelemetry::default();
telemetry.capture_provider_metadata(&response);
assert_eq!(
telemetry.serving_fingerprint.as_deref(),
Some("b10360-48d22e295")
);
let value = telemetry
.as_vm_dict()
.expect("fingerprint makes telemetry visible");
let dict = value.as_dict().expect("dict body");
assert_eq!(
dict.get("serving_fingerprint")
.map(VmValue::display)
.as_deref(),
Some("b10360-48d22e295")
);
}
#[test]
fn absent_system_fingerprint_stays_absent() {
let response = serde_json::json!({"model": "served-adapter"});
let mut telemetry = ProviderTelemetry::default();
telemetry.capture_provider_metadata(&response);
assert_eq!(telemetry.serving_fingerprint, None);
let value = telemetry.as_vm_dict().expect("model keeps telemetry alive");
let dict = value.as_dict().expect("dict body");
assert!(dict.get("serving_fingerprint").is_none());
}
#[test]
fn empty_system_fingerprint_does_not_overwrite_a_reported_build() {
let mut telemetry = ProviderTelemetry::default();
telemetry.capture_provider_metadata(&serde_json::json!({
"system_fingerprint": "b10360-48d22e295"
}));
telemetry.capture_provider_metadata(&serde_json::json!({
"system_fingerprint": ""
}));
assert_eq!(
telemetry.serving_fingerprint.as_deref(),
Some("b10360-48d22e295")
);
}
#[test]
fn fingerprint_alone_makes_the_envelope_non_empty() {
let telemetry = ProviderTelemetry {
serving_fingerprint: Some("b10360-48d22e295".to_string()),
..ProviderTelemetry::default()
};
assert!(!telemetry.is_empty());
assert!(telemetry.as_vm_dict().is_some());
}
}