use crate::observability::stats::{aggregate_usage_stats, parse_bounded_u32, round_to}; use aether_ai_formats::api::request_path_implies_stream_request; use aether_ai_formats::UPSTREAM_IS_STREAM_KEY; use aether_billing::{ normalize_input_tokens_for_billing, normalize_total_input_context_for_cache_hit_rate, }; use aether_data::repository::users::StoredUserSummary; use aether_data_contracts::repository::{ provider_catalog::{StoredProviderCatalogEndpoint, StoredProviderCatalogProvider}, usage::{ StoredRequestUsageAudit, StoredUsageAuditSummary, UsageBodyField, LIVE_SESSION_METADATA_KEY, REALTIME_SESSION_METADATA_KEY, }, }; use axum::{ body::Body, http, response::{IntoResponse, Response}, Json, }; use serde_json::{json, Value}; use std::collections::{BTreeMap, BTreeSet}; use url::form_urlencoded; pub const ADMIN_USAGE_DATA_UNAVAILABLE_DETAIL: &str = "Admin usage data unavailable"; fn admin_usage_safe_error_message(item: &StoredRequestUsageAudit) -> Option { if item .error_message .as_deref() .is_none_or(|value| value.trim().is_empty()) && item.status_code.is_none_or(|status| status < 400) { return None; } item.error_category .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) .or_else(|| { item.status_code .filter(|status| *status >= 400) .map(|status| format!("http_{status}")) }) .or_else(|| Some("request_failed".to_string())) } pub fn admin_usage_data_unavailable_response(detail: &'static str) -> Response { ( http::StatusCode::SERVICE_UNAVAILABLE, Json(json!({ "detail": detail })), ) .into_response() } pub fn admin_usage_bad_request_response(detail: impl Into) -> Response { ( http::StatusCode::BAD_REQUEST, Json(json!({ "detail": detail.into() })), ) .into_response() } fn query_param_value(query: Option<&str>, key: &str) -> Option { let query = query?; for (entry_key, value) in form_urlencoded::parse(query.as_bytes()) { if entry_key == key { let value = value.trim(); if !value.is_empty() { return Some(value.to_string()); } } } None } fn unix_secs_to_rfc3339(unix_secs: u64) -> Option { let timestamp = chrono::DateTime::::from_timestamp(i64::try_from(unix_secs).ok()?, 0)?; Some(timestamp.to_rfc3339()) } fn admin_usage_response_time_updated_at(item: &StoredRequestUsageAudit) -> Option { item.response_time_ms?; if matches!(item.status.as_str(), "pending" | "streaming") && item.updated_at_unix_secs <= item.created_at_unix_ms { return None; } unix_secs_to_rfc3339(item.updated_at_unix_secs) } pub fn admin_usage_parse_limit(query: Option<&str>) -> Result { match query_param_value(query, "limit") { None => Ok(100), Some(value) => { let parsed = value .parse::() .map_err(|_| "limit must be a positive integer".to_string())?; if parsed == 0 || parsed > 500 { return Err("limit must be between 1 and 500".to_string()); } Ok(parsed) } } } pub fn admin_usage_parse_offset(query: Option<&str>) -> Result { match query_param_value(query, "offset") { None => Ok(0), Some(value) => value .parse::() .map_err(|_| "offset must be a non-negative integer".to_string()), } } pub fn admin_usage_parse_ids(query: Option<&str>) -> Option> { let ids = query_param_value(query, "ids")?; let parsed: BTreeSet = ids .split(',') .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) .collect(); Some(parsed) } pub fn admin_usage_parse_recent_hours(query: Option<&str>, default: u32) -> Result { match query_param_value(query, "hours") { Some(value) => parse_bounded_u32("hours", &value, 1, 720), None => Ok(default), } } pub fn admin_usage_parse_timeline_limit(query: Option<&str>) -> Result { match query_param_value(query, "limit") { Some(value) => { let parsed = value .parse::() .map_err(|_| "limit must be an integer between 100 and 50000".to_string())?; if (100..=50_000).contains(&parsed) { Ok(parsed) } else { Err("limit must be an integer between 100 and 50000".to_string()) } } None => Ok(3_000), } } pub fn admin_usage_parse_aggregation_limit(query: Option<&str>) -> Result { match query_param_value(query, "limit") { Some(value) => { let parsed = value .parse::() .map_err(|_| "limit must be an integer between 1 and 100".to_string())?; if (1..=100).contains(&parsed) { Ok(parsed) } else { Err("limit must be an integer between 1 and 100".to_string()) } } None => Ok(20), } } pub fn admin_usage_username( item: &StoredRequestUsageAudit, users_by_id: &BTreeMap, auth_user_reader_available: bool, ) -> Option { item.user_id .as_ref() .and_then(|user_id| users_by_id.get(user_id)) .map(|value| value.username.clone()) .or_else(|| { (!auth_user_reader_available) .then(|| item.username.clone()) .flatten() }) } pub fn admin_usage_api_key_name( item: &StoredRequestUsageAudit, api_key_names: &BTreeMap, auth_api_key_reader_available: bool, ) -> Option { item.api_key_id .as_ref() .and_then(|api_key_id| api_key_names.get(api_key_id)) .cloned() .or_else(|| { (!auth_api_key_reader_available) .then(|| item.api_key_name.clone()) .flatten() }) } pub fn admin_usage_matches_search( item: &StoredRequestUsageAudit, search: Option<&str>, users_by_id: &BTreeMap, api_key_names: &BTreeMap, auth_user_reader_available: bool, auth_api_key_reader_available: bool, ) -> bool { let Some(search) = search.map(str::trim).filter(|value| !value.is_empty()) else { return true; }; let username = admin_usage_username(item, users_by_id, auth_user_reader_available); let api_key_name = admin_usage_api_key_name(item, api_key_names, auth_api_key_reader_available); let haystack = [ username.as_deref(), api_key_name.as_deref(), Some(item.model.as_str()), Some(item.provider_name.as_str()), ]; search.split_whitespace().all(|keyword| { let keyword = keyword.to_ascii_lowercase(); haystack .iter() .flatten() .any(|value| value.to_ascii_lowercase().contains(keyword.as_str())) }) } pub fn admin_usage_matches_username( item: &StoredRequestUsageAudit, username: Option<&str>, users_by_id: &BTreeMap, auth_user_reader_available: bool, ) -> bool { let Some(username) = username.map(str::trim).filter(|value| !value.is_empty()) else { return true; }; admin_usage_username(item, users_by_id, auth_user_reader_available) .as_deref() .unwrap_or_default() .to_ascii_lowercase() .contains(username.to_ascii_lowercase().as_str()) } pub fn admin_usage_matches_eq(value: &str, query: Option<&str>) -> bool { let Some(query) = query .map(str::trim) .filter(|candidate| !candidate.is_empty()) else { return true; }; value.eq_ignore_ascii_case(query) } pub fn admin_usage_matches_api_format( item: &StoredRequestUsageAudit, api_format: Option<&str>, ) -> bool { let Some(api_format) = api_format.map(str::trim).filter(|value| !value.is_empty()) else { return true; }; item.api_format .as_deref() .is_some_and(|value| value.eq_ignore_ascii_case(api_format)) } pub fn admin_usage_is_failed(item: &StoredRequestUsageAudit) -> bool { let has_failure_signal = item .status_code .is_some_and(|value| !(200..300).contains(&value)) || item .error_message .as_deref() .is_some_and(|value| !value.trim().is_empty()); let status = item.status.trim().to_ascii_lowercase(); if !status.is_empty() { return match status.as_str() { "completed" | "cancelled" => false, "pending" | "streaming" => has_failure_signal, "failed" => true, _ => false, }; } has_failure_signal } pub fn admin_usage_has_fallback(item: &StoredRequestUsageAudit) -> bool { item.has_fallback() } pub fn admin_usage_matches_status(item: &StoredRequestUsageAudit, status: Option<&str>) -> bool { let Some(status) = status .map(str::trim) .filter(|value| !value.is_empty()) .map(str::to_ascii_lowercase) else { return true; }; match status.as_str() { "stream" => item.is_stream && !item.is_websocket(), "standard" => !item.is_stream && !item.is_websocket(), "websocket" | "ws" => item.is_websocket(), "error" => { item.status_code .is_some_and(|value| !(200..300).contains(&value)) || item.error_message.is_some() } "pending" | "streaming" | "completed" | "cancelled" => item.status == status, "failed" => admin_usage_is_failed(item), "active" => matches!(item.status.as_str(), "pending" | "streaming"), "has_fallback" => admin_usage_has_fallback(item), _ => true, } } fn admin_usage_has_body_value( item: &StoredRequestUsageAudit, body: Option<&Value>, field: UsageBodyField, ) -> bool { item.body_capture_result(field, body).available } fn admin_usage_strip_body_ref_metadata(metadata: &mut serde_json::Map) { metadata.remove(UsageBodyField::RequestBody.as_ref_key()); metadata.remove(UsageBodyField::ProviderRequestBody.as_ref_key()); metadata.remove(UsageBodyField::ResponseBody.as_ref_key()); metadata.remove(UsageBodyField::ClientResponseBody.as_ref_key()); } fn admin_usage_strip_routing_metadata(metadata: &mut serde_json::Map) { metadata.remove("candidate_id"); metadata.remove("candidate_index"); metadata.remove("key_name"); metadata.remove("model_id"); metadata.remove("global_model_id"); metadata.remove("global_model_name"); metadata.remove("planner_kind"); metadata.remove("route_family"); metadata.remove("route_kind"); metadata.remove("execution_path"); metadata.remove("local_execution_runtime_miss_reason"); } fn admin_usage_strip_settlement_metadata(metadata: &mut serde_json::Map) { metadata.remove("billing_snapshot"); metadata.remove("billing_snapshot_schema_version"); metadata.remove("billing_snapshot_status"); metadata.remove("settlement_snapshot"); metadata.remove("settlement_snapshot_schema_version"); metadata.remove("billing_dimensions"); metadata.remove("rate_multiplier"); metadata.remove("is_free_tier"); metadata.remove("input_price_per_1m"); metadata.remove("output_price_per_1m"); metadata.remove("cache_creation_price_per_1m"); metadata.remove("cache_read_price_per_1m"); metadata.remove("price_per_request"); } fn admin_usage_strip_trace_metadata(metadata: &mut serde_json::Map) { metadata.remove("trace_id"); } /// Projects historical observability metadata without exposing credentials or /// URL query/fragment values. This is shared by all admin observability views /// because older rows may predate the current persistence policy. pub fn admin_usage_safe_metadata_value(value: &Value) -> Value { admin_usage_safe_metadata_value_inner(value, false) } fn admin_usage_safe_metadata_value_inner(value: &Value, in_realtime_session: bool) -> Value { match value { Value::Object(object) => Value::Object( object .iter() .filter_map(|(key, value)| { let compact = key .chars() .filter(|ch| ch.is_ascii_alphanumeric()) .map(|ch| ch.to_ascii_lowercase()) .collect::(); // The generic `token` key filter protects historical rows that may // predate the persistence projection. Realtime audio usage counters // are explicitly safe numeric metrics, but only when they occur in the // structured realtime-session object. Do not broaden this exception to // arbitrary token-shaped fields or string values. let safe_realtime_counter = in_realtime_session && matches!(compact.as_str(), "inputaudiotokens" | "outputaudiotokens") && value.as_u64().is_some(); if (compact.contains("authorization") || compact.contains("credential") || compact.contains("secret") || compact.contains("token") || compact.contains("apikey") || compact.contains("password") || matches!(compact.as_str(), "cookie" | "setcookie" | "headers")) && !safe_realtime_counter { return None; } let child_in_realtime_session = compact == "realtimesession"; Some(( key.clone(), admin_usage_safe_metadata_value_inner(value, child_in_realtime_session), )) }) .collect(), ), Value::Array(items) => Value::Array( items .iter() .map(|item| admin_usage_safe_metadata_value_inner(item, false)) .collect(), ), Value::String(value) => { if let Ok(mut url) = url::Url::parse(value.trim()) { if !url.username().is_empty() || url.password().is_some() || url.query().is_some() || url.fragment().is_some() { let _ = url.set_username(""); let _ = url.set_password(None); url.set_query(None); url.set_fragment(None); return Value::String(url.to_string()); } } Value::String(value.clone()) } other => other.clone(), } } fn admin_usage_string_field<'a>(value: &'a Value, field: &str) -> Option<&'a str> { value .get(field) .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) } fn admin_usage_error_field<'a>(body: &'a Value, field: &str) -> Option<&'a str> { body.get("error") .and_then(|error| match error { Value::Object(object) => object.get(field).and_then(Value::as_str), _ => None, }) .map(str::trim) .filter(|value| !value.is_empty()) .or_else(|| admin_usage_string_field(body, field)) } fn admin_usage_error_message_from_body(body: &Value) -> Option { body.get("error") .and_then(|error| match error { Value::Object(object) => object.get("message").and_then(Value::as_str), Value::String(message) => Some(message.as_str()), _ => None, }) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) .or_else(|| { admin_usage_string_field(body, "message") .or_else(|| admin_usage_string_field(body, "detail")) .map(ToOwned::to_owned) }) } fn admin_usage_error_type_from_body(body: &Value) -> Option { admin_usage_error_field(body, "type") .or_else(|| admin_usage_error_field(body, "status")) .or_else(|| admin_usage_error_field(body, "kind")) .map(ToOwned::to_owned) } fn admin_usage_error_code_from_body(body: &Value) -> Option { body.get("error") .and_then(|error| match error { Value::Object(object) => object.get("code").cloned(), _ => None, }) .or_else(|| body.get("code").cloned()) } fn admin_usage_header_content_type(headers: Option<&Value>) -> Option { let object = headers?.as_object()?; for (key, value) in object { if key.eq_ignore_ascii_case("content-type") { return value .as_str() .map(ToOwned::to_owned) .or_else(|| (!value.is_null()).then(|| value.to_string())); } } None } fn admin_usage_error_domain_json( source: &str, status_code: Option, headers: Option<&Value>, body: Option<&Value>, fallback_type: Option<&str>, fallback_message: Option<&str>, ) -> Value { let error_type = body .and_then(admin_usage_error_type_from_body) .or_else(|| fallback_type.map(ToOwned::to_owned)); let message = body .and_then(admin_usage_error_message_from_body) .or_else(|| { fallback_message .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) }); let code = body.and_then(admin_usage_error_code_from_body); let content_type = admin_usage_header_content_type(headers); if status_code.is_none() && error_type.is_none() && message.is_none() && code.is_none() && body.is_none() { return Value::Null; } json!({ "source": source, "status_code": status_code, "type": error_type, "message": message, "code": code, "content_type": content_type, "body": body.cloned().unwrap_or(Value::Null), }) } fn admin_usage_local_execution_client_error_message(message: &str) -> String { let without_reason = message .split_once("(原因代码:") .map(|(prefix, _)| prefix) .unwrap_or(message); let mut simplified = without_reason.trim().to_string(); if let Some(unavailable_message) = admin_usage_simplify_all_candidates_skipped_client_error_message(simplified.as_str()) { return unavailable_message; } for marker in ["。请检查", "。请确认", ". 请检查", ". 请确认"] { if let Some(index) = simplified.find(marker) { simplified.truncate(index); break; } } simplified .trim() .trim_end_matches(['。', '.', '!', '!', '?', '?']) .to_string() } fn admin_usage_simplify_all_candidates_skipped_client_error_message( message: &str, ) -> Option { if !message.contains("候选提供商") || !(message.contains("全部不可用") || message.contains("都不满足本次")) { return None; } let request_mode = admin_usage_extract_local_execution_request_mode(message)?; if let Some(model) = admin_usage_extract_candidate_supported_model(message) { return Some(format!( "没有可用提供商支持模型 {model} 的{request_mode}请求" )); } Some(format!("没有可用提供商支持本次{request_mode}请求")) } fn admin_usage_local_runtime_miss_reason_label(reason: &str) -> &'static str { match reason { "all_candidates_skipped" => "所有候选均被跳过", "candidate_list_empty" => "没有可调度候选", "local_runtime_unavailable" => "本地执行运行时不可用", "provider_transport_unavailable" => "提供商传输不可用", _ => "本地调度未命中", } } fn admin_usage_extract_local_runtime_miss_reason_summary(message: &str) -> Option { let without_reason_code = message .split_once("(原因代码:") .map(|(prefix, _)| prefix) .unwrap_or(message) .trim(); let summary = without_reason_code .rsplit_once(':') .or_else(|| without_reason_code.rsplit_once(':')) .map(|(_, suffix)| suffix.trim())?; (!summary.is_empty()).then(|| summary.to_string()) } fn admin_usage_scheduling_failure_json( item: &StoredRequestUsageAudit, client_error: &Value, ) -> Value { if item.routing_execution_path() != Some("local_execution_runtime_miss") { return Value::Null; } let reason = item .routing_local_execution_runtime_miss_reason() .unwrap_or("local_execution_runtime_miss"); let message = admin_usage_error_domain_message(client_error) .or_else(|| admin_usage_client_error_fallback_message(item)) .or_else(|| item.error_message.as_deref().map(str::to_string)); let raw_message = item .error_message .as_deref() .map(str::trim) .filter(|value| !value.is_empty()); let reason_summary = raw_message.and_then(admin_usage_extract_local_runtime_miss_reason_summary); json!({ "source": "local_execution_runtime_miss", "reason": reason, "reason_label": admin_usage_local_runtime_miss_reason_label(reason), "title": format!("本地调度失败:{}", admin_usage_local_runtime_miss_reason_label(reason)), "message": message, "reason_summary": reason_summary, "status_code": item.status_code, "no_upstream_attempt": item.candidate_id.is_none() && item.provider_api_key_id.is_none() && item.provider_request_headers.is_none() && item.provider_request_body.is_none() && item.provider_request_body_ref.is_none(), }) } fn admin_usage_extract_local_execution_request_mode(message: &str) -> Option<&str> { let rest = message.get(message.find("本次")? + "本次".len()..)?; let mode = rest.get(..rest.find("请求")?)?.trim(); (!mode.is_empty()).then_some(mode) } fn admin_usage_extract_candidate_supported_model(message: &str) -> Option<&str> { let rest = message.get(message.find("支持模型 ")? + "支持模型 ".len()..)?; let model = rest.get(..rest.find(" 的")?)?.trim(); (!model.is_empty()).then_some(model) } fn admin_usage_client_error_fallback_message(item: &StoredRequestUsageAudit) -> Option { item.error_message .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .map(|message| { if item.routing_execution_path() == Some("local_execution_runtime_miss") { admin_usage_local_execution_client_error_message(message) } else { message.to_string() } }) } fn admin_usage_error_domain_message(domain: &Value) -> Option { domain .get("message") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) } fn admin_usage_error_domain_type(domain: &Value) -> Option { domain .get("type") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) } fn admin_usage_error_domain_source(domain: &Value) -> Option { domain .get("source") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) } fn admin_usage_failure_summary_json( item: &StoredRequestUsageAudit, request_error: &Value, upstream_error: &Value, client_error: &Value, ) -> Value { let selected = [client_error, upstream_error, request_error] .into_iter() .find(|domain| !domain.is_null() && admin_usage_error_domain_message(domain).is_some()); let message = selected .and_then(admin_usage_error_domain_message) .or_else(|| { item.error_message .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) }); let Some(message) = message else { return Value::Null; }; let error_type = selected .and_then(admin_usage_error_domain_type) .or_else(|| item.error_category.clone()); let source = selected .and_then(admin_usage_error_domain_source) .unwrap_or_else(|| "usage_summary".to_string()); json!({ "source": source, "status_code": item.status_code, "type": error_type, "message": message, "category": item.error_category, }) } fn admin_usage_error_domains_json(item: &StoredRequestUsageAudit) -> Value { let has_upstream_attempt = item.candidate_id.is_some() || item.provider_api_key_id.is_some() || item.provider_request_headers.is_some() || item.provider_request_body.is_some() || item.provider_request_body_ref.is_some(); let upstream_error = if has_upstream_attempt { admin_usage_error_domain_json( "upstream_response", item.status_code, item.response_headers.as_ref(), item.response_body.as_ref(), item.error_category.as_deref(), item.error_message.as_deref(), ) } else { Value::Null }; let client_error_fallback_message = admin_usage_client_error_fallback_message(item); let client_error = admin_usage_error_domain_json( "client_response", item.status_code, item.client_response_headers.as_ref(), item.client_response_body.as_ref(), item.error_category.as_deref(), client_error_fallback_message.as_deref(), ); let request_error = Value::Null; let failure_summary = admin_usage_failure_summary_json(item, &request_error, &upstream_error, &client_error); json!({ "request_error": request_error, "upstream_error": upstream_error, "client_error": client_error, "failure_summary": failure_summary, }) } fn admin_usage_error_domain_search_text(domain: &Value) -> String { [ domain.get("type").and_then(Value::as_str), domain.get("message").and_then(Value::as_str), domain.get("code").and_then(Value::as_str), ] .into_iter() .flatten() .map(str::to_ascii_lowercase) .collect::>() .join(" ") } fn admin_usage_upstream_error_is_sensitive(domain: &Value) -> bool { let text = admin_usage_error_domain_search_text(domain); [ "insufficient_quota", "insufficient quota", "quota exhausted", "credits exhausted", "credit balance", "credit limit", "payment_required", "payment required", "account disabled", "account_deactivated", "subscription inactive", "verification required", ] .iter() .any(|pattern| text.contains(pattern)) } fn admin_usage_error_flow_json(item: &StoredRequestUsageAudit, error_domains: &Value) -> Value { let request_error = &error_domains["request_error"]; let upstream_error = &error_domains["upstream_error"]; let client_error = &error_domains["client_error"]; let failure_summary = &error_domains["failure_summary"]; if request_error.is_null() && upstream_error.is_null() && client_error.is_null() && failure_summary.is_null() { return Value::Null; } let upstream_sensitive = !upstream_error.is_null() && admin_usage_upstream_error_is_sensitive(upstream_error); let propagation = if upstream_sensitive { "suppressed" } else if !client_error.is_null() && !upstream_error.is_null() { let upstream_message = admin_usage_error_domain_message(upstream_error); let client_message = admin_usage_error_domain_message(client_error); if upstream_message.is_some() && upstream_message == client_message { "passthrough" } else { "converted" } } else if !client_error.is_null() { "local" } else if !upstream_error.is_null() { "captured" } else { "none" }; let source = if !request_error.is_null() { "request" } else if !upstream_error.is_null() { "upstream" } else if !client_error.is_null() { "gateway" } else { "summary" }; let client_response_source = if client_error.is_null() { Value::Null } else if !upstream_error.is_null() { Value::String("converted_or_sanitized_upstream".to_string()) } else { Value::String("gateway_generated".to_string()) }; json!({ "source": source, "status_code": item.status_code, "propagation": propagation, "client_response_source": client_response_source, "safe_to_expose_upstream": !upstream_sensitive, "summary_source": failure_summary.get("source").cloned().unwrap_or(Value::Null), }) } fn maybe_insert_number_field( object: &mut serde_json::Map, key: &str, value: Option, ) { if let Some(value) = value { object.insert(key.to_string(), json!(value)); } } fn maybe_insert_u64_field( object: &mut serde_json::Map, key: &str, value: Option, ) { if let Some(value) = value { object.insert(key.to_string(), json!(value)); } } fn maybe_insert_bool_field( object: &mut serde_json::Map, key: &str, value: Option, ) { if let Some(value) = value { object.insert(key.to_string(), json!(value)); } } fn admin_usage_body_capture_json(item: &StoredRequestUsageAudit) -> Value { item.body_capture_json_for_fields(&[ UsageBodyField::RequestBody, UsageBodyField::ProviderRequestBody, UsageBodyField::ResponseBody, UsageBodyField::ClientResponseBody, ]) } fn admin_usage_settlement_json(item: &StoredRequestUsageAudit) -> Value { let mut settlement = serde_json::Map::new(); if let Some(snapshot) = item .request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| metadata.get("settlement_snapshot")) .cloned() { settlement.insert("settlement_snapshot".to_string(), snapshot); } if let Some(schema_version) = item .request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| metadata.get("settlement_snapshot_schema_version")) .and_then(Value::as_str) { settlement.insert( "settlement_snapshot_schema_version".to_string(), json!(schema_version), ); } if let Some(dimensions) = item .request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| metadata.get("billing_dimensions")) .cloned() { settlement.insert("billing_dimensions".to_string(), dimensions); } if let Some(snapshot) = item .request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| metadata.get("billing_snapshot")) .cloned() { settlement.insert("billing_snapshot".to_string(), snapshot); } maybe_insert_string_field( &mut settlement, "billing_snapshot_schema_version", item.settlement_billing_snapshot_schema_version(), ); maybe_insert_string_field( &mut settlement, "billing_snapshot_status", item.settlement_billing_snapshot_status(), ); maybe_insert_number_field( &mut settlement, "rate_multiplier", item.settlement_rate_multiplier(), ); maybe_insert_bool_field( &mut settlement, "is_free_tier", item.settlement_is_free_tier(), ); maybe_insert_number_field( &mut settlement, "input_price_per_1m", item.settlement_input_price_per_1m(), ); maybe_insert_number_field( &mut settlement, "output_price_per_1m", item.settlement_output_price_per_1m(), ); maybe_insert_number_field( &mut settlement, "cache_creation_price_per_1m", item.settlement_cache_creation_price_per_1m(), ); maybe_insert_number_field( &mut settlement, "cache_read_price_per_1m", item.settlement_cache_read_price_per_1m(), ); maybe_insert_number_field( &mut settlement, "price_per_request", item.settlement_price_per_request(), ); if settlement.is_empty() { Value::Null } else { Value::Object(settlement) } } fn admin_usage_trace_json(item: &StoredRequestUsageAudit) -> Value { let mut trace = serde_json::Map::new(); if let Some(trace_id) = item.trace_id() { trace.insert("trace_id".to_string(), json!(trace_id)); } if trace.is_empty() { Value::Null } else { Value::Object(trace) } } fn admin_usage_replay_body_capture_json(item: &StoredRequestUsageAudit) -> Value { item.body_capture_json_for_fields(&[UsageBodyField::RequestBody]) } fn admin_usage_curl_body_capture_json(item: &StoredRequestUsageAudit) -> Value { let mut object = item.body_capture_json_object_for_fields(&[ UsageBodyField::RequestBody, UsageBodyField::ProviderRequestBody, ]); object.insert( "body_source".to_string(), Value::String(item.curl_body_source().to_string()), ); Value::Object(object) } pub fn admin_usage_provider_key_name( item: &StoredRequestUsageAudit, provider_key_names: &BTreeMap, ) -> Option { item.provider_api_key_id .as_ref() .and_then(|key_id| provider_key_names.get(key_id)) .cloned() .or_else(|| item.routing_key_name().map(ToOwned::to_owned)) } fn admin_usage_request_body_stream_flag(item: &StoredRequestUsageAudit) -> Option { item.request_body .as_ref() .and_then(Value::as_object) .and_then(|body| body.get("stream")) .and_then(Value::as_bool) } fn admin_usage_api_format_defaults_to_non_stream(item: &StoredRequestUsageAudit) -> bool { let api_format = item .api_format .as_deref() .or(item.endpoint_api_format.as_deref()) .map(str::trim) .filter(|value| !value.is_empty()); let Some(value) = api_format else { return false; }; aether_ai_formats::api_format_defaults_to_non_stream(value) } fn admin_usage_request_body_implies_default_non_stream(item: &StoredRequestUsageAudit) -> bool { let Some(body) = item.request_body.as_ref().and_then(Value::as_object) else { return false; }; !body.contains_key("stream") && admin_usage_api_format_defaults_to_non_stream(item) } fn admin_usage_headers_stream_flag(headers: Option<&Value>) -> Option { let object = headers.and_then(Value::as_object)?; let raw = object .iter() .find(|(key, _)| key.eq_ignore_ascii_case("content-type")) .and_then(|(_, value)| match value { Value::String(text) => Some(text.as_str()), Value::Array(values) => values.iter().find_map(Value::as_str), _ => None, })? .trim(); if raw.is_empty() { return None; } let normalized = raw.to_ascii_lowercase(); Some( normalized.contains("event-stream") || normalized.contains("eventstream") || normalized.contains("x-ndjson"), ) } fn admin_usage_body_is_sse_capture(value: Option<&Value>) -> bool { let Some(object) = value.and_then(Value::as_object) else { return false; }; object.get("chunks").and_then(Value::as_array).is_some() && object .get("metadata") .and_then(Value::as_object) .and_then(|metadata| metadata.get("stream")) .and_then(Value::as_bool) .unwrap_or(false) } fn admin_usage_infer_client_stream_from_captured_bodies( item: &StoredRequestUsageAudit, ) -> Option { let provider_stream = admin_usage_body_is_sse_capture(item.response_body.as_ref()); let client_stream = admin_usage_body_is_sse_capture(item.client_response_body.as_ref()); if client_stream { Some(true) } else if provider_stream && item.client_response_body.is_some() { Some(false) } else { None } } fn admin_usage_infer_upstream_stream_from_captured_bodies( item: &StoredRequestUsageAudit, ) -> Option { let provider_stream = admin_usage_body_is_sse_capture(item.response_body.as_ref()); let client_stream = admin_usage_body_is_sse_capture(item.client_response_body.as_ref()); if provider_stream { Some(true) } else if client_stream && item.response_body.is_some() { Some(false) } else { None } } fn admin_usage_request_path_implies_client_stream(item: &StoredRequestUsageAudit) -> bool { let Some(metadata) = item.request_metadata.as_ref().and_then(Value::as_object) else { return false; }; ["request_path", "request_path_and_query"] .into_iter() .filter_map(|field| metadata.get(field).and_then(Value::as_str)) .any(request_path_implies_stream_request) } pub fn admin_usage_client_is_stream(item: &StoredRequestUsageAudit) -> bool { admin_usage_request_path_implies_client_stream(item) .then_some(true) .or_else(|| { item.request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| metadata.get("client_requested_stream")) .and_then(Value::as_bool) }) .or_else(|| admin_usage_request_body_stream_flag(item)) .or_else(|| admin_usage_headers_stream_flag(item.client_response_headers.as_ref())) .or_else(|| admin_usage_request_body_implies_default_non_stream(item).then_some(false)) .or_else(|| admin_usage_infer_client_stream_from_captured_bodies(item)) .unwrap_or(item.is_stream) } fn admin_usage_upstream_is_stream(item: &StoredRequestUsageAudit) -> bool { item.request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| metadata.get(UPSTREAM_IS_STREAM_KEY)) .and_then(Value::as_bool) .or_else(|| admin_usage_headers_stream_flag(item.response_headers.as_ref())) .or_else(|| admin_usage_infer_upstream_stream_from_captured_bodies(item)) .unwrap_or(item.is_stream) } fn admin_usage_metadata_string<'a>( item: &'a StoredRequestUsageAudit, key: &str, ) -> Option<&'a str> { item.request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| metadata.get(key)) .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) } fn admin_usage_metadata_u64(item: &StoredRequestUsageAudit, key: &str) -> Option { item.request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| metadata.get(key)) .and_then(Value::as_u64) } fn infer_client_family_from_user_agent(user_agent: &str) -> Option<&'static str> { let normalized = user_agent.trim().to_ascii_lowercase(); if normalized.is_empty() { return None; } if normalized.starts_with("codex_vscode") { return Some("codex_vscode"); } if normalized.starts_with("codex") { return Some("codex"); } if normalized.contains("claude-code") || normalized.contains("claude_code") { return Some("claude_code"); } if normalized.contains("opencode") { return Some("opencode"); } if normalized.contains("geminicli") || normalized.contains("gemini-cli") { return Some("gemini_cli"); } if normalized.contains("qwencode") { return Some("qwen_code"); } if normalized.contains("roo-code") || normalized.contains("roocode") { return Some("roo_code"); } if normalized.contains("kilo-code") || normalized.contains("kilocode") { return Some("kilocode"); } if normalized.contains("cherrystudio") || normalized.contains("cherry-studio") { return Some("cherrystudio"); } if normalized.contains("openui-agent-manager") || normalized.contains("openui") { return Some("openui"); } if normalized.contains("cursor") { return Some("cursor"); } if normalized.contains("windsurf") { return Some("windsurf"); } if normalized.contains("continue") { return Some("continue"); } if normalized.contains("cline") { return Some("cline"); } if normalized.contains("aider") { return Some("aider"); } if normalized.contains("langchain") { return Some("langchain"); } if normalized.contains("llamaindex") || normalized.contains("llama-index") { return Some("llamaindex"); } if normalized.starts_with("openai/js") { return Some("openai_js_sdk"); } if normalized.starts_with("openai/python") { return Some("openai_python_sdk"); } if normalized.starts_with("anthropic/js") || normalized.contains("anthropic-sdk-typescript") { return Some("anthropic_js_sdk"); } if normalized.starts_with("anthropic/python") || normalized.contains("anthropic-sdk-python") { return Some("anthropic_python_sdk"); } if normalized.contains("/js ") || normalized.contains("/python ") { return Some("sdk"); } Some("unknown") } pub fn admin_usage_client_family(item: &StoredRequestUsageAudit) -> Option<&str> { item.client_family .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .or_else(|| { item.request_metadata .as_ref() .and_then(Value::as_object) .and_then(|metadata| { metadata .get("client_session_affinity") .and_then(Value::as_object) .and_then(|affinity| affinity.get("client_family")) .and_then(Value::as_str) .or_else(|| metadata.get("client_family").and_then(Value::as_str)) }) .map(str::trim) .filter(|value| !value.is_empty()) }) .or_else(|| { admin_usage_metadata_string(item, "user_agent") .and_then(infer_client_family_from_user_agent) }) } fn admin_usage_active_request_json( item: &StoredRequestUsageAudit, api_key_name: Option, provider_key_name: Option, image_progress: Option<&Value>, ) -> Value { let cache_creation_input_tokens = admin_usage_cache_creation_tokens(item); let client_is_stream = admin_usage_client_is_stream(item); let upstream_is_stream = admin_usage_upstream_is_stream(item); let mut value = json!({ "id": item.id, "status": item.status, "request_type": item.request_type, "input_tokens": item.input_tokens, "effective_input_tokens": admin_usage_effective_input_tokens(item), "output_tokens": item.output_tokens, "cache_creation_input_tokens": cache_creation_input_tokens, "cache_creation_ephemeral_5m_input_tokens": item.cache_creation_ephemeral_5m_input_tokens, "cache_creation_ephemeral_1h_input_tokens": item.cache_creation_ephemeral_1h_input_tokens, "cache_read_input_tokens": item.cache_read_input_tokens, "cost": round_to(item.total_cost_usd, 6), "actual_cost": round_to(item.actual_total_cost_usd, 6), "response_time_ms": item.response_time_ms, "first_byte_time_ms": item.first_byte_time_ms, "updated_at": unix_secs_to_rfc3339(item.updated_at_unix_secs), "response_time_updated_at": admin_usage_response_time_updated_at(item), "status_code": item.status_code, "error_message": admin_usage_safe_error_message(item), "provider": item.provider_name, "api_key_name": api_key_name, "provider_key_name": provider_key_name, "is_stream": item.is_stream, "is_websocket": item.is_websocket(), "websocket_transport": item.websocket_transport(), "usage_available": item.usage_available(), "usage_pricing_available": item.usage_pricing_available(), "input_audio_tokens": item.realtime_input_audio_tokens(), "output_audio_tokens": item.realtime_output_audio_tokens(), "upstream_is_stream": upstream_is_stream, "client_requested_stream": client_is_stream, "client_is_stream": client_is_stream, "client_family": admin_usage_client_family(item), "client_ip": admin_usage_metadata_string(item, "client_ip"), "user_agent": admin_usage_metadata_string(item, "user_agent"), "request_path": admin_usage_metadata_string(item, "request_path"), "request_path_and_query": admin_usage_metadata_string(item, "request_path_and_query"), "has_fallback": admin_usage_has_fallback(item), }); value["end_to_end_time_ms"] = json!(admin_usage_metadata_u64(item, "end_to_end_time_ms")); value["end_to_end_first_byte_time_ms"] = json!(admin_usage_metadata_u64( item, "end_to_end_first_byte_time_ms" )); if let Some(api_format) = item.api_format.as_ref() { value["api_format"] = json!(api_format); } if let Some(endpoint_api_format) = item.endpoint_api_format.as_ref() { value["endpoint_api_format"] = json!(endpoint_api_format); } value["has_format_conversion"] = json!(item.has_format_conversion); if let Some(target_model) = item.target_model.as_ref() { value["target_model"] = json!(target_model); } if let Some(reasoning_effort) = item.provider_reasoning_effort() { value["reasoning_effort"] = json!(reasoning_effort); } if let Some(requested_reasoning_effort) = item.requested_reasoning_effort() { value["requested_reasoning_effort"] = json!(requested_reasoning_effort); } if let Some(service_tier) = item.provider_service_tier() { value["service_tier"] = json!(service_tier); } if let Some(actual_service_tier) = item.provider_actual_service_tier() { value["actual_service_tier"] = json!(actual_service_tier); } if let Some(image_progress) = image_progress { value["image_progress"] = image_progress.clone(); } value } pub fn admin_usage_record_json( item: &StoredRequestUsageAudit, users_by_id: &BTreeMap, api_key_names: &BTreeMap, auth_user_reader_available: bool, auth_api_key_reader_available: bool, provider_key_name: Option<&str>, ) -> Value { let rate_multiplier = item.settlement_rate_multiplier(); let input_price_per_1m = item.settlement_input_price_per_1m(); let output_price_per_1m = item.settlement_output_price_per_1m(); let cache_creation_price_per_1m = item.settlement_cache_creation_price_per_1m(); let cache_read_price_per_1m = item.settlement_cache_read_price_per_1m(); let cache_creation_input_tokens = admin_usage_cache_creation_tokens(item); let is_free_tier = item.settlement_is_free_tier(); let user = item .user_id .as_ref() .and_then(|user_id| users_by_id.get(user_id)); let username = admin_usage_username(item, users_by_id, auth_user_reader_available) .unwrap_or_else(|| "已删除用户".to_string()); let api_key_name = admin_usage_api_key_name(item, api_key_names, auth_api_key_reader_available); let user_email = user .and_then(|value| value.email.clone()) .unwrap_or_else(|| "已删除用户".to_string()); let client_is_stream = admin_usage_client_is_stream(item); let upstream_is_stream = admin_usage_upstream_is_stream(item); let mut payload = json!({ "id": item.id, "user_id": item.user_id, "user_email": user_email, "username": username, "api_key": item.api_key_id.as_ref().map(|api_key_id| json!({ "id": api_key_id, "name": api_key_name.clone(), "display": api_key_name.clone().unwrap_or_else(|| api_key_id.clone()), })), "provider": item.provider_name, "model": item.model, "target_model": item.target_model, "input_tokens": item.input_tokens, "effective_input_tokens": admin_usage_effective_input_tokens(item), "output_tokens": item.output_tokens, "cache_creation_input_tokens": cache_creation_input_tokens, "cache_creation_ephemeral_5m_input_tokens": item.cache_creation_ephemeral_5m_input_tokens, "cache_creation_ephemeral_1h_input_tokens": item.cache_creation_ephemeral_1h_input_tokens, "cache_read_input_tokens": item.cache_read_input_tokens, "total_tokens": admin_usage_total_tokens(item), "cost": round_to(item.total_cost_usd, 6), "actual_cost": round_to(item.actual_total_cost_usd, 6), "rate_multiplier": rate_multiplier, "response_time_ms": item.response_time_ms, "first_byte_time_ms": item.first_byte_time_ms, "created_at": unix_secs_to_rfc3339(item.created_at_unix_ms), "updated_at": unix_secs_to_rfc3339(item.updated_at_unix_secs), "input_price_per_1m": input_price_per_1m, "output_price_per_1m": output_price_per_1m, "cache_creation_price_per_1m": cache_creation_price_per_1m, "cache_read_price_per_1m": cache_read_price_per_1m, "status_code": item.status_code, "error_message": admin_usage_safe_error_message(item), "status": item.status, "request_type": item.request_type, "has_fallback": admin_usage_has_fallback(item), "has_retry": false, "has_rectified": false, "is_free_tier": is_free_tier, "api_format": item.api_format, "endpoint_api_format": item.endpoint_api_format, "has_format_conversion": item.has_format_conversion, "api_key_name": api_key_name, "provider_key_name": provider_key_name, "model_version": Value::Null, }); let object = payload .as_object_mut() .expect("admin usage record payload should be an object"); object.insert( "end_to_end_time_ms".to_string(), json!(admin_usage_metadata_u64(item, "end_to_end_time_ms")), ); object.insert( "end_to_end_first_byte_time_ms".to_string(), json!(admin_usage_metadata_u64( item, "end_to_end_first_byte_time_ms" )), ); object.insert("is_websocket".to_string(), json!(item.is_websocket())); object.insert( "websocket_transport".to_string(), json!(item.websocket_transport()), ); object.insert("usage_available".to_string(), json!(item.usage_available())); object.insert( "usage_pricing_available".to_string(), json!(item.usage_pricing_available()), ); object.insert( "input_audio_tokens".to_string(), json!(item.realtime_input_audio_tokens()), ); object.insert( "output_audio_tokens".to_string(), json!(item.realtime_output_audio_tokens()), ); object.insert("is_stream".to_string(), json!(item.is_stream)); object.insert( UPSTREAM_IS_STREAM_KEY.to_string(), json!(upstream_is_stream), ); object.insert( "client_requested_stream".to_string(), json!(client_is_stream), ); object.insert("client_is_stream".to_string(), json!(client_is_stream)); maybe_insert_string_field(object, "client_family", admin_usage_client_family(item)); maybe_insert_string_field( object, "client_ip", admin_usage_metadata_string(item, "client_ip"), ); maybe_insert_string_field( object, "user_agent", admin_usage_metadata_string(item, "user_agent"), ); maybe_insert_string_field( object, "request_path", admin_usage_metadata_string(item, "request_path"), ); maybe_insert_string_field( object, "request_path_and_query", admin_usage_metadata_string(item, "request_path_and_query"), ); if let Some(reasoning_effort) = item.provider_reasoning_effort() { object.insert("reasoning_effort".to_string(), json!(reasoning_effort)); } if let Some(requested_reasoning_effort) = item.requested_reasoning_effort() { object.insert( "requested_reasoning_effort".to_string(), json!(requested_reasoning_effort), ); } if let Some(service_tier) = item.provider_service_tier() { object.insert("service_tier".to_string(), json!(service_tier)); } if let Some(actual_service_tier) = item.provider_actual_service_tier() { object.insert( "actual_service_tier".to_string(), json!(actual_service_tier), ); } payload } pub fn admin_usage_total_tokens(item: &StoredRequestUsageAudit) -> u64 { admin_usage_effective_input_tokens(item) .saturating_add(item.output_tokens) .saturating_add(admin_usage_cache_creation_tokens(item)) .saturating_add(item.cache_read_input_tokens) } pub fn admin_usage_cache_creation_tokens(item: &StoredRequestUsageAudit) -> u64 { let classified = item .cache_creation_ephemeral_5m_input_tokens .saturating_add(item.cache_creation_ephemeral_1h_input_tokens); if item.cache_creation_input_tokens == 0 && classified > 0 { classified } else { item.cache_creation_input_tokens } } pub fn admin_usage_total_input_context(item: &StoredRequestUsageAudit) -> u64 { let api_format = item .endpoint_api_format .as_deref() .or(item.api_format.as_deref()); let input_tokens = i64::try_from(item.input_tokens).unwrap_or(i64::MAX); let cache_creation_tokens = i64::try_from(admin_usage_cache_creation_tokens(item)).unwrap_or(i64::MAX); let cache_read_tokens = i64::try_from(item.cache_read_input_tokens).unwrap_or(i64::MAX); normalize_total_input_context_for_cache_hit_rate( api_format, input_tokens, cache_creation_tokens, cache_read_tokens, ) as u64 } pub fn admin_usage_effective_input_tokens(item: &StoredRequestUsageAudit) -> u64 { let api_format = item .endpoint_api_format .as_deref() .or(item.api_format.as_deref()); let input_tokens = i64::try_from(item.input_tokens).unwrap_or(i64::MAX); let cache_creation_tokens = i64::try_from(admin_usage_cache_creation_tokens(item)).unwrap_or(i64::MAX); let cache_read_tokens = i64::try_from(item.cache_read_input_tokens).unwrap_or(i64::MAX); normalize_input_tokens_for_billing( api_format, input_tokens, cache_creation_tokens, cache_read_tokens, ) as u64 } pub fn admin_usage_token_cache_hit_rate(total_input_context: u64, cache_read_tokens: u64) -> f64 { if total_input_context == 0 { 0.0 } else { round_to( cache_read_tokens as f64 / total_input_context as f64 * 100.0, 2, ) } } fn admin_usage_provider_display_name(item: &StoredRequestUsageAudit) -> Option { let provider_name = item.provider_name.trim(); if provider_name.is_empty() || matches!(provider_name, "unknown" | "pending") { None } else { Some(item.provider_name.clone()) } } fn maybe_insert_string_field( object: &mut serde_json::Map, key: &str, value: Option<&str>, ) { if let Some(value) = value { object.insert(key.to_string(), Value::String(value.to_string())); } } fn admin_usage_routing_json( item: &StoredRequestUsageAudit, provider_key_name: Option<&str>, ) -> Value { let mut routing = serde_json::Map::new(); maybe_insert_string_field(&mut routing, "candidate_id", item.routing_candidate_id()); maybe_insert_u64_field( &mut routing, "candidate_index", item.routing_candidate_index(), ); maybe_insert_string_field( &mut routing, "key_name", provider_key_name.or_else(|| item.routing_key_name()), ); maybe_insert_string_field(&mut routing, "model_id", item.routing_model_id()); maybe_insert_string_field( &mut routing, "global_model_id", item.routing_global_model_id(), ); maybe_insert_string_field( &mut routing, "global_model_name", item.routing_global_model_name(), ); maybe_insert_string_field(&mut routing, "planner_kind", item.routing_planner_kind()); maybe_insert_string_field(&mut routing, "route_family", item.routing_route_family()); maybe_insert_string_field(&mut routing, "route_kind", item.routing_route_kind()); maybe_insert_string_field( &mut routing, "execution_path", item.routing_execution_path(), ); maybe_insert_string_field( &mut routing, "local_execution_runtime_miss_reason", item.routing_local_execution_runtime_miss_reason(), ); if routing.is_empty() { Value::Null } else { Value::Object(routing) } } pub fn admin_usage_aggregation_by_model_json( usage: &[StoredRequestUsageAudit], limit: usize, ) -> Value { #[allow(clippy::type_complexity)] let mut grouped: BTreeMap< String, (u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, f64, f64), > = BTreeMap::new(); for item in usage { let key = item.model.clone(); let entry = grouped .entry(key) .or_insert((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0)); entry.0 = entry.0.saturating_add(1); entry.1 = entry.1.saturating_add(item.total_tokens); entry.2 = entry.2.saturating_add(item.input_tokens); entry.3 = entry.3.saturating_add(item.output_tokens); entry.4 = entry .4 .saturating_add(admin_usage_effective_input_tokens(item)); entry.5 = entry .5 .saturating_add(admin_usage_total_input_context(item)); entry.6 = entry .6 .saturating_add(admin_usage_cache_creation_tokens(item)); entry.7 = entry .7 .saturating_add(item.cache_creation_ephemeral_5m_input_tokens); entry.8 = entry .8 .saturating_add(item.cache_creation_ephemeral_1h_input_tokens); entry.9 = entry.9.saturating_add(item.cache_read_input_tokens); entry.10 += item.total_cost_usd; entry.11 += item.actual_total_cost_usd; } let mut items: Vec = grouped .into_iter() .map( |( model, ( request_count, total_tokens, _input_tokens, output_tokens, effective_input_tokens, total_input_context, cache_creation_tokens, cache_creation_ephemeral_5m_tokens, cache_creation_ephemeral_1h_tokens, cache_read_tokens, total_cost, actual_cost, ), )| { json!({ "model": model, "request_count": request_count, "total_tokens": total_tokens, "effective_input_tokens": effective_input_tokens, "total_input_context": total_input_context, "output_tokens": output_tokens, "total_cost": round_to(total_cost, 6), "actual_cost": round_to(actual_cost, 6), "cache_creation_tokens": cache_creation_tokens, "cache_creation_ephemeral_5m_tokens": cache_creation_ephemeral_5m_tokens, "cache_creation_ephemeral_1h_tokens": cache_creation_ephemeral_1h_tokens, "cache_read_tokens": cache_read_tokens, "cache_hit_rate": admin_usage_token_cache_hit_rate( total_input_context, cache_read_tokens, ), }) }, ) .collect(); items.sort_by(|left, right| { right["request_count"] .as_u64() .unwrap_or_default() .cmp(&left["request_count"].as_u64().unwrap_or_default()) .then_with(|| { left["model"] .as_str() .unwrap_or_default() .cmp(right["model"].as_str().unwrap_or_default()) }) }); items.truncate(limit); json!(items) } pub fn admin_usage_aggregation_by_provider_json( usage: &[StoredRequestUsageAudit], limit: usize, ) -> Value { #[allow(clippy::type_complexity)] let mut grouped: BTreeMap< String, ( String, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, f64, f64, u64, u64, ), > = BTreeMap::new(); for item in usage { let key = item .provider_id .clone() .unwrap_or_else(|| "unknown".to_string()); let provider_name = admin_usage_provider_display_name(item).unwrap_or_else(|| "Unknown".to_string()); let entry = grouped.entry(key).or_insert(( provider_name.clone(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, 0, 0, )); if entry.0 == "Unknown" && provider_name != "Unknown" { entry.0 = provider_name; } entry.1 = entry.1.saturating_add(1); entry.2 = entry.2.saturating_add(item.total_tokens); entry.3 = entry.3.saturating_add(item.input_tokens); entry.4 = entry.4.saturating_add(item.output_tokens); entry.5 = entry .5 .saturating_add(admin_usage_effective_input_tokens(item)); entry.6 = entry .6 .saturating_add(admin_usage_total_input_context(item)); entry.7 = entry .7 .saturating_add(admin_usage_cache_creation_tokens(item)); entry.8 = entry .8 .saturating_add(item.cache_creation_ephemeral_5m_input_tokens); entry.9 = entry .9 .saturating_add(item.cache_creation_ephemeral_1h_input_tokens); entry.10 = entry.10.saturating_add(item.cache_read_input_tokens); entry.11 += item.total_cost_usd; entry.12 += item.actual_total_cost_usd; entry.13 = entry .13 .saturating_add(item.response_time_ms.unwrap_or_default()); entry.14 = entry .14 .saturating_add(if admin_usage_is_success(item) { 1 } else { 0 }); } let mut items: Vec = grouped .into_iter() .map( |( provider_id, ( provider_name, request_count, total_tokens, _input_tokens, output_tokens, effective_input_tokens, total_input_context, cache_creation_tokens, cache_creation_ephemeral_5m_tokens, cache_creation_ephemeral_1h_tokens, cache_read_tokens, total_cost, actual_cost, response_time_ms_sum, success_count, ), )| { let avg_response_time_ms = if request_count == 0 { 0.0 } else { round_to(response_time_ms_sum as f64 / request_count as f64, 2) }; let error_count = request_count.saturating_sub(success_count); let success_rate = if request_count == 0 { 0.0 } else { round_to(success_count as f64 / request_count as f64 * 100.0, 2) }; json!({ "provider_id": provider_id, "provider": provider_name, "request_count": request_count, "total_tokens": total_tokens, "effective_input_tokens": effective_input_tokens, "total_input_context": total_input_context, "output_tokens": output_tokens, "total_cost": round_to(total_cost, 6), "actual_cost": round_to(actual_cost, 6), "avg_response_time_ms": avg_response_time_ms, "success_rate": success_rate, "error_count": error_count, "cache_creation_tokens": cache_creation_tokens, "cache_creation_ephemeral_5m_tokens": cache_creation_ephemeral_5m_tokens, "cache_creation_ephemeral_1h_tokens": cache_creation_ephemeral_1h_tokens, "cache_read_tokens": cache_read_tokens, "cache_hit_rate": admin_usage_token_cache_hit_rate( total_input_context, cache_read_tokens, ), }) }, ) .collect(); items.sort_by(|left, right| { right["request_count"] .as_u64() .unwrap_or_default() .cmp(&left["request_count"].as_u64().unwrap_or_default()) .then_with(|| { left["provider_id"] .as_str() .unwrap_or_default() .cmp(right["provider_id"].as_str().unwrap_or_default()) }) }); items.truncate(limit); json!(items) } pub fn admin_usage_aggregation_by_api_format_json( usage: &[StoredRequestUsageAudit], limit: usize, ) -> Value { #[allow(clippy::type_complexity)] let mut grouped: BTreeMap< String, ( u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, f64, f64, u64, ), > = BTreeMap::new(); for item in usage { let key = item .api_format .clone() .unwrap_or_else(|| "unknown".to_string()); let entry = grouped .entry(key) .or_insert((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, 0)); entry.0 = entry.0.saturating_add(1); entry.1 = entry.1.saturating_add(item.total_tokens); entry.2 = entry.2.saturating_add(item.input_tokens); entry.3 = entry.3.saturating_add(item.output_tokens); entry.4 = entry .4 .saturating_add(admin_usage_effective_input_tokens(item)); entry.5 = entry .5 .saturating_add(admin_usage_total_input_context(item)); entry.6 = entry .6 .saturating_add(admin_usage_cache_creation_tokens(item)); entry.7 = entry .7 .saturating_add(item.cache_creation_ephemeral_5m_input_tokens); entry.8 = entry .8 .saturating_add(item.cache_creation_ephemeral_1h_input_tokens); entry.9 = entry.9.saturating_add(item.cache_read_input_tokens); entry.10 += item.total_cost_usd; entry.11 += item.actual_total_cost_usd; entry.12 = entry .12 .saturating_add(item.response_time_ms.unwrap_or_default()); } let mut items: Vec = grouped .into_iter() .map( |( api_format, ( request_count, total_tokens, _input_tokens, output_tokens, effective_input_tokens, total_input_context, cache_creation_tokens, cache_creation_ephemeral_5m_tokens, cache_creation_ephemeral_1h_tokens, cache_read_tokens, total_cost, actual_cost, response_time_ms_sum, ), )| { let avg_response_time_ms = if request_count == 0 { 0.0 } else { round_to(response_time_ms_sum as f64 / request_count as f64, 2) }; json!({ "api_format": api_format, "request_count": request_count, "total_tokens": total_tokens, "effective_input_tokens": effective_input_tokens, "total_input_context": total_input_context, "output_tokens": output_tokens, "total_cost": round_to(total_cost, 6), "actual_cost": round_to(actual_cost, 6), "avg_response_time_ms": avg_response_time_ms, "cache_creation_tokens": cache_creation_tokens, "cache_creation_ephemeral_5m_tokens": cache_creation_ephemeral_5m_tokens, "cache_creation_ephemeral_1h_tokens": cache_creation_ephemeral_1h_tokens, "cache_read_tokens": cache_read_tokens, "cache_hit_rate": admin_usage_token_cache_hit_rate( total_input_context, cache_read_tokens, ), }) }, ) .collect(); items.sort_by(|left, right| { right["request_count"] .as_u64() .unwrap_or_default() .cmp(&left["request_count"].as_u64().unwrap_or_default()) .then_with(|| { left["api_format"] .as_str() .unwrap_or_default() .cmp(right["api_format"].as_str().unwrap_or_default()) }) }); items.truncate(limit); json!(items) } pub fn admin_usage_heatmap_json(usage: &[StoredRequestUsageAudit]) -> Value { let today = chrono::Utc::now().date_naive(); let start_date = today .checked_sub_signed(chrono::Duration::days(364)) .unwrap_or(today); let mut grouped: BTreeMap = BTreeMap::new(); for item in usage { let Ok(created_at_unix_ms) = i64::try_from(item.created_at_unix_ms) else { continue; }; let Some(created_at) = chrono::DateTime::::from_timestamp(created_at_unix_ms, 0) else { continue; }; let date_key = created_at.date_naive(); if date_key < start_date || date_key > today { continue; } let entry = grouped.entry(date_key).or_insert((0, 0, 0.0, 0.0)); entry.0 = entry.0.saturating_add(1); entry.1 = entry.1.saturating_add(admin_usage_total_tokens(item)); entry.2 += item.total_cost_usd; entry.3 += item.actual_total_cost_usd; } let mut max_requests = 0_u64; let mut cursor = start_date; let mut days = Vec::new(); while cursor <= today { let (requests, total_tokens, total_cost, actual_total_cost) = grouped.get(&cursor).copied().unwrap_or((0, 0, 0.0, 0.0)); max_requests = max_requests.max(requests); days.push(json!({ "date": cursor.to_string(), "requests": requests, "total_tokens": total_tokens, "total_cost": round_to(total_cost, 6), "actual_total_cost": round_to(actual_total_cost, 6), })); cursor = cursor .checked_add_signed(chrono::Duration::days(1)) .unwrap_or(today + chrono::Duration::days(1)); } json!({ "start_date": start_date.to_string(), "end_date": today.to_string(), "total_days": days.len(), "max_requests": max_requests, "days": days, }) } pub fn admin_usage_is_success(item: &StoredRequestUsageAudit) -> bool { matches!( item.status.as_str(), "completed" | "success" | "ok" | "billed" | "settled" ) && item .status_code .is_none_or(|code| (200..300).contains(&code)) } pub fn admin_usage_matches_optional_id(value: Option<&str>, expected: Option<&str>) -> bool { let Some(expected) = expected.map(str::trim).filter(|value| !value.is_empty()) else { return true; }; value.is_some_and(|candidate| candidate == expected) } pub fn admin_usage_group_completed_by_user( items: &[StoredRequestUsageAudit], ) -> BTreeMap> { let mut grouped = BTreeMap::new(); for item in items.iter().filter(|item| item.user_id.is_some()) { grouped .entry(item.user_id.clone().unwrap_or_default()) .or_insert_with(Vec::new) .push(item.clone()); } grouped } pub fn admin_usage_group_completed_by_api_key( items: &[StoredRequestUsageAudit], api_key_id: Option<&str>, ) -> BTreeMap> { let mut grouped = BTreeMap::new(); for item in items.iter().filter(|item| item.api_key_id.is_some()) { if !admin_usage_matches_optional_id(item.api_key_id.as_deref(), api_key_id) { continue; } grouped .entry(item.api_key_id.clone().unwrap_or_default()) .or_insert_with(Vec::new) .push(item.clone()); } grouped } pub fn admin_usage_collect_request_intervals_minutes( items: &[StoredRequestUsageAudit], ) -> Vec { let mut previous_created_at_unix_ms = None; let mut intervals = Vec::new(); for item in items { if let Some(previous) = previous_created_at_unix_ms { intervals.push(item.created_at_unix_ms.saturating_sub(previous) as f64 / 60.0); } previous_created_at_unix_ms = Some(item.created_at_unix_ms); } intervals } pub fn admin_usage_percentile_cont(values: &[f64], percentile: f64) -> Option { if values.is_empty() { return None; } if values.len() == 1 { return Some(values[0]); } let position = percentile.clamp(0.0, 1.0) * (values.len() - 1) as f64; let lower_index = position.floor() as usize; let upper_index = position.ceil() as usize; let lower = values[lower_index]; let upper = values[upper_index]; Some(lower + (upper - lower) * (position - lower_index as f64)) } pub fn admin_usage_calculate_recommended_ttl( p75_interval: Option, p90_interval: Option, ) -> u64 { let Some(p75_interval) = p75_interval else { return 5; }; let Some(p90_interval) = p90_interval else { return 5; }; if p90_interval <= 5.0 { 5 } else if p75_interval <= 15.0 { 15 } else if p75_interval <= 30.0 { 30 } else { 60 } } pub fn admin_usage_ttl_recommendation_reason( ttl: u64, p75_interval: Option, p90_interval: Option, ) -> String { let Some(p75_interval) = p75_interval else { return "数据不足,使用默认值".to_string(); }; let Some(p90_interval) = p90_interval else { return "数据不足,使用默认值".to_string(); }; match ttl { 5 => format!("高频用户:90% 的请求间隔在 {:.1} 分钟内", p90_interval), 15 => format!("中高频用户:75% 的请求间隔在 {:.1} 分钟内", p75_interval), 30 => format!("中频用户:75% 的请求间隔在 {:.1} 分钟内", p75_interval), _ => format!( "低频用户:75% 的请求间隔为 {:.1} 分钟,建议使用长 TTL", p75_interval ), } } pub fn admin_usage_proportional_limits( grouped: &BTreeMap>, limit: usize, total_points: usize, ) -> BTreeMap { let mut limits = BTreeMap::new(); for (group_id, items) in grouped { let computed = if total_points <= limit || total_points == 0 { items.len() } else { let scaled = ((items.len() as f64 * limit as f64) / total_points as f64).ceil() as usize; std::cmp::max(scaled, 1) }; limits.insert(group_id.clone(), computed); } limits } pub fn admin_usage_point_sort_key(left: &Value, right: &Value) -> std::cmp::Ordering { left["x"] .as_str() .unwrap_or_default() .cmp(right["x"].as_str().unwrap_or_default()) .then_with(|| { left["user_id"] .as_str() .unwrap_or_default() .cmp(right["user_id"].as_str().unwrap_or_default()) }) } fn admin_usage_id_from_path_suffix(request_path: &str, suffix: Option<&str>) -> Option { let mut value = request_path .strip_prefix("/api/admin/usage/")? .trim() .trim_matches('/') .to_string(); if let Some(suffix) = suffix { value = value.strip_suffix(suffix)?.trim_matches('/').to_string(); } if value.is_empty() || value.contains('/') { None } else { Some(value) } } pub fn admin_usage_id_from_detail_path(request_path: &str) -> Option { admin_usage_id_from_path_suffix(request_path, None) } pub fn admin_usage_id_from_action_path(request_path: &str, action: &str) -> Option { admin_usage_id_from_path_suffix(request_path, Some(action)) } fn admin_usage_curl_shell_quote(value: &str) -> String { format!("'{}'", value.replace('\'', "'\"'\"'")) } fn admin_usage_resolve_replay_mode(same_provider: bool, same_endpoint: bool) -> &'static str { if same_provider && same_endpoint { "same_endpoint_reuse" } else if same_provider { "same_provider_remap" } else { "cross_provider_remap" } } pub fn admin_usage_resolve_request_capture_body( item: &StoredRequestUsageAudit, body_override: Option, ) -> Option { let resolved_model = item.model.clone(); let client_is_stream = admin_usage_client_is_stream(item); let mut request_body = body_override.or_else(|| item.request_body.clone())?; if let Some(body) = request_body.as_object_mut() { body.entry("model".to_string()) .or_insert_with(|| json!(resolved_model)); if !body.contains_key("stream") { body.insert("stream".to_string(), json!(client_is_stream)); } if let Some(target_model) = item .target_model .as_ref() .filter(|value| !value.trim().is_empty()) { body.entry("target_model".to_string()) .or_insert_with(|| json!(target_model)); } if let Some(request_type) = item .request_type .as_ref() .filter(|value| !value.trim().is_empty()) { body.entry("request_type".to_string()) .or_insert_with(|| json!(request_type)); } if let Some(api_format) = item .api_format .as_ref() .filter(|value| !value.trim().is_empty()) { body.entry("api_format".to_string()) .or_insert_with(|| json!(api_format)); } } Some(request_body) } pub fn admin_usage_headers_from_value(value: &Value) -> Option> { let object = value.as_object()?; Some(BTreeMap::from_iter(object.iter().filter_map( |(key, value)| { if value.is_null() { return None; } let value = value .as_str() .map(ToOwned::to_owned) .unwrap_or_else(|| value.to_string()); Some((key.clone(), value)) }, ))) } pub fn admin_usage_curl_headers() -> BTreeMap { BTreeMap::from([("Content-Type".to_string(), "application/json".to_string())]) } pub fn admin_usage_build_curl_command( url: Option<&str>, headers: &BTreeMap, body: Option<&Value>, ) -> String { let mut parts = vec!["curl".to_string()]; if let Some(url) = url { parts.push(admin_usage_curl_shell_quote(url)); } parts.push("-X POST".to_string()); for (key, value) in headers { parts.push(format!( "-H {}", admin_usage_curl_shell_quote(&format!("{key}: {value}")) )); } if let Some(body) = body { parts.push(format!( "-d {}", admin_usage_curl_shell_quote(&body.to_string()) )); } parts.join(" \\\n ") } fn summarize_admin_usage_stats(usage: &[StoredRequestUsageAudit]) -> StoredUsageAuditSummary { let aggregate = aggregate_usage_stats(usage); StoredUsageAuditSummary { total_requests: aggregate.total_requests, input_tokens: usage.iter().map(|item| item.input_tokens).sum(), output_tokens: usage.iter().map(|item| item.output_tokens).sum(), recorded_total_tokens: aggregate.total_tokens, cache_creation_tokens: usage.iter().map(admin_usage_cache_creation_tokens).sum(), cache_creation_ephemeral_5m_tokens: usage .iter() .map(|item| item.cache_creation_ephemeral_5m_input_tokens) .sum(), cache_creation_ephemeral_1h_tokens: usage .iter() .map(|item| item.cache_creation_ephemeral_1h_input_tokens) .sum(), cache_read_tokens: usage.iter().map(|item| item.cache_read_input_tokens).sum(), total_cost_usd: aggregate.total_cost, actual_total_cost_usd: aggregate.actual_total_cost, cache_creation_cost_usd: usage.iter().map(|item| item.cache_creation_cost_usd).sum(), cache_read_cost_usd: usage.iter().map(|item| item.cache_read_cost_usd).sum(), total_response_time_ms: aggregate.total_response_time_ms, error_requests: aggregate.error_requests, } } pub fn build_admin_usage_summary_stats_response_from_summary( summary: &StoredUsageAuditSummary, ) -> Response { let total_tokens = summary .input_tokens .saturating_add(summary.output_tokens) .saturating_add(summary.cache_creation_tokens) .saturating_add(summary.cache_read_tokens); let avg_response_time = if summary.total_requests == 0 { 0.0 } else { round_to( summary.total_response_time_ms / summary.total_requests as f64 / 1000.0, 2, ) }; let error_rate = if summary.total_requests == 0 { 0.0 } else { round_to( (summary.error_requests as f64 / summary.total_requests as f64) * 100.0, 2, ) }; Json(json!({ "total_requests": summary.total_requests, "total_tokens": total_tokens, "total_cost": round_to(summary.total_cost_usd, 6), "total_actual_cost": round_to(summary.actual_total_cost_usd, 6), "avg_response_time": avg_response_time, "error_count": summary.error_requests, "error_rate": error_rate, "cache_stats": { "cache_creation_tokens": summary.cache_creation_tokens, "cache_creation_ephemeral_5m_tokens": summary.cache_creation_ephemeral_5m_tokens, "cache_creation_ephemeral_1h_tokens": summary.cache_creation_ephemeral_1h_tokens, "cache_read_tokens": summary.cache_read_tokens, "cache_creation_cost": round_to(summary.cache_creation_cost_usd, 6), "cache_read_cost": round_to(summary.cache_read_cost_usd, 6), } })) .into_response() } pub fn build_admin_usage_summary_stats_response( usage: &[StoredRequestUsageAudit], ) -> Response { build_admin_usage_summary_stats_response_from_summary(&summarize_admin_usage_stats(usage)) } pub fn build_admin_usage_active_requests_response( items: &[StoredRequestUsageAudit], api_key_names: &BTreeMap, auth_api_key_reader_available: bool, provider_key_names: &BTreeMap, image_progress_by_request_id: &BTreeMap, state_overrides_by_request_id: &BTreeMap, ) -> Response { let payload: Vec<_> = items .iter() .map(|item| { let provider_key_name = admin_usage_provider_key_name(item, provider_key_names); let api_key_name = admin_usage_api_key_name(item, api_key_names, auth_api_key_reader_available); let mut payload = admin_usage_active_request_json( item, api_key_name, provider_key_name, image_progress_by_request_id.get(&item.request_id), ); if let (Some(payload), Some(overrides)) = ( payload.as_object_mut(), state_overrides_by_request_id .get(&item.request_id) .and_then(Value::as_object), ) { for (key, value) in overrides { payload.insert(key.clone(), value.clone()); } } payload }) .collect(); Json(json!({ "requests": payload })).into_response() } #[allow(clippy::too_many_arguments)] pub fn build_admin_usage_records_response( items: &[StoredRequestUsageAudit], users_by_id: &BTreeMap, api_key_names: &BTreeMap, auth_user_reader_available: bool, auth_api_key_reader_available: bool, provider_key_names: &BTreeMap, total: usize, limit: usize, offset: usize, ) -> Response { let records: Vec<_> = items .iter() .map(|item| { let provider_key_name = admin_usage_provider_key_name(item, provider_key_names); admin_usage_record_json( item, users_by_id, api_key_names, auth_user_reader_available, auth_api_key_reader_available, provider_key_name.as_deref(), ) }) .collect(); Json(json!({ "records": records, "total": total, "limit": limit, "offset": offset, })) .into_response() } pub fn build_admin_usage_curl_response( item: &StoredRequestUsageAudit, url: Option, headers_json: Option, headers: &BTreeMap, body: Option<&Value>, ) -> Response { let curl = admin_usage_build_curl_command(url.as_deref(), headers, body); Json(json!({ "url": url, "method": "POST", "headers": headers_json.unwrap_or_else(|| json!(headers.clone())), "body": body.cloned().unwrap_or(Value::Null), "curl": curl, "body_capture": admin_usage_curl_body_capture_json(item), "captured_request_body_available": admin_usage_has_body_value( item, item.request_body.as_ref(), UsageBodyField::RequestBody ) || admin_usage_has_body_value( item, item.provider_request_body.as_ref(), UsageBodyField::ProviderRequestBody ), })) .into_response() } #[allow(clippy::too_many_arguments)] pub fn build_admin_usage_detail_payload( item: &StoredRequestUsageAudit, users_by_id: &BTreeMap, api_key_names: &BTreeMap, auth_user_reader_available: bool, auth_api_key_reader_available: bool, provider_key_name: Option<&str>, include_bodies: bool, request_body: Option, default_headers: &BTreeMap, ) -> Value { let mut payload = admin_usage_record_json( item, users_by_id, api_key_names, auth_user_reader_available, auth_api_key_reader_available, provider_key_name, ); let mut metadata = match item .request_metadata .as_ref() .map(admin_usage_safe_metadata_value) { Some(Value::Object(object)) => Value::Object(object), Some(value) => json!({ "request_metadata": value }), None => json!({}), }; if let Some(object) = metadata.as_object_mut() { admin_usage_strip_body_ref_metadata(object); admin_usage_strip_routing_metadata(object); admin_usage_strip_settlement_metadata(object); admin_usage_strip_trace_metadata(object); } let live_session = metadata .as_object() .and_then(|object| object.get(LIVE_SESSION_METADATA_KEY)) .cloned() .unwrap_or(Value::Null); let realtime_session = metadata .as_object() .and_then(|object| object.get(REALTIME_SESSION_METADATA_KEY)) .cloned() .unwrap_or(Value::Null); payload["user"] = match item.user_id.as_ref() { Some(user_id) => json!({ "id": user_id, "email": payload["user_email"].clone(), "username": payload["username"].clone(), }), None => Value::Null, }; payload["request_id"] = json!(item.request_id); payload["billing_status"] = json!(item.billing_status); payload["request_type"] = json!(item.request_type); payload["provider_id"] = json!(item.provider_id); payload["provider_endpoint_id"] = json!(item.provider_endpoint_id); payload["provider_api_key_id"] = json!(item.provider_api_key_id); payload["error_category"] = json!(item.error_category); payload["cache_creation_cost"] = json!(round_to(item.cache_creation_cost_usd, 6)); payload["cache_read_cost"] = json!(round_to(item.cache_read_cost_usd, 6)); payload["request_cost"] = json!(round_to(item.total_cost_usd, 6)); payload["request_headers"] = item .request_headers .clone() .unwrap_or_else(|| json!(default_headers.clone())); payload["provider_request_headers"] = item .provider_request_headers .clone() .unwrap_or_else(|| json!(default_headers.clone())); payload["response_headers"] = item.response_headers.clone().unwrap_or(Value::Null); payload["client_response_headers"] = item.client_response_headers.clone().unwrap_or(Value::Null); payload["metadata"] = metadata; // Session summaries are also first-class detail fields. Keep the original // values in `metadata` for backward compatibility while making the detail // contract independent from the generic metadata viewer. payload[LIVE_SESSION_METADATA_KEY] = live_session; payload[REALTIME_SESSION_METADATA_KEY] = realtime_session; payload["routing"] = admin_usage_routing_json(item, provider_key_name); payload["body_capture"] = admin_usage_body_capture_json(item); payload["settlement"] = admin_usage_settlement_json(item); payload["trace"] = admin_usage_trace_json(item); let error_domains = admin_usage_error_domains_json(item); let error_flow = admin_usage_error_flow_json(item, &error_domains); payload["errors"] = error_domains.clone(); payload["request_error"] = error_domains["request_error"].clone(); payload["upstream_error"] = error_domains["upstream_error"].clone(); payload["client_error"] = error_domains["client_error"].clone(); payload["failure_summary"] = error_domains["failure_summary"].clone(); payload["scheduling_failure"] = admin_usage_scheduling_failure_json(item, &error_domains["client_error"]); payload["error_flow"] = error_flow; payload["has_request_body"] = json!(admin_usage_has_body_value( item, item.request_body.as_ref(), UsageBodyField::RequestBody )); payload["has_provider_request_body"] = json!(admin_usage_has_body_value( item, item.provider_request_body.as_ref(), UsageBodyField::ProviderRequestBody )); payload["has_response_body"] = json!(admin_usage_has_body_value( item, item.response_body.as_ref(), UsageBodyField::ResponseBody )); payload["has_client_response_body"] = json!(admin_usage_has_body_value( item, item.client_response_body.as_ref(), UsageBodyField::ClientResponseBody )); payload["tiered_pricing"] = Value::Null; if include_bodies { payload["request_body"] = request_body.unwrap_or(Value::Null); payload["provider_request_body"] = item.provider_request_body.clone().unwrap_or(Value::Null); payload["response_body"] = item.response_body.clone().unwrap_or(Value::Null); payload["client_response_body"] = item.client_response_body.clone().unwrap_or(Value::Null); } else { payload["request_body"] = Value::Null; payload["provider_request_body"] = Value::Null; payload["response_body"] = Value::Null; payload["client_response_body"] = Value::Null; } payload } #[allow(clippy::too_many_arguments)] pub fn build_admin_usage_replay_plan_response( item: &StoredRequestUsageAudit, target_provider: &StoredProviderCatalogProvider, target_endpoint: &StoredProviderCatalogEndpoint, target_api_key_id: Option, request_body: Option, url: &str, headers: &BTreeMap, same_provider: bool, same_endpoint: bool, ) -> Response { let resolved_model = item.model.clone(); let mapping_source = "none"; let curl = admin_usage_build_curl_command(Some(url), headers, request_body.as_ref()); Json(json!({ "dry_run": true, "usage_id": item.id, "request_id": item.request_id, "mode": admin_usage_resolve_replay_mode(same_provider, same_endpoint), "target_provider_id": target_provider.id, "target_provider_name": target_provider.name, "target_endpoint_id": target_endpoint.id, "target_api_key_id": target_api_key_id, "target_api_format": target_endpoint.api_format, "resolved_model": resolved_model, "mapping_source": mapping_source, "method": "POST", "url": url, "request_headers": headers, "request_body": request_body.clone().unwrap_or(Value::Null), "body_capture": admin_usage_replay_body_capture_json(item), "captured_request_body_available": admin_usage_has_body_value( item, item.request_body.as_ref(), UsageBodyField::RequestBody ), "request_body_available": request_body.is_some(), "note": "Rust local replay currently exposes a dry-run plan and does not dispatch upstream", "curl": curl, })) .into_response() } #[cfg(test)] mod tests { use std::collections::BTreeMap; use serde_json::json; use super::{ admin_usage_active_request_json, admin_usage_client_is_stream, admin_usage_has_body_value, admin_usage_has_fallback, admin_usage_is_failed, admin_usage_is_success, admin_usage_matches_search, admin_usage_matches_status, admin_usage_matches_username, admin_usage_record_json, admin_usage_resolve_request_capture_body, admin_usage_safe_metadata_value, admin_usage_total_tokens, admin_usage_upstream_is_stream, build_admin_usage_detail_payload, }; use aether_data_contracts::repository::usage::{StoredRequestUsageAudit, UsageBodyField}; fn sample_usage( status: &str, status_code: Option, error_message: Option<&str>, ) -> StoredRequestUsageAudit { StoredRequestUsageAudit::new( "usage-1".to_string(), "req-1".to_string(), Some("user-1".to_string()), Some("api-key-1".to_string()), Some("alice".to_string()), Some("default".to_string()), "OpenAI".to_string(), "gpt-5".to_string(), None, Some("provider-1".to_string()), Some("endpoint-1".to_string()), Some("provider-key-1".to_string()), Some("chat".to_string()), Some("openai:chat".to_string()), Some("openai".to_string()), Some("chat".to_string()), Some("openai:chat".to_string()), Some("openai".to_string()), Some("chat".to_string()), false, false, 10, 20, 30, 0.0, 0.0, status_code, error_message.map(ToOwned::to_owned), None, Some(120), None, status.to_string(), "settled".to_string(), 100, 101, Some(102), ) .expect("usage should build") } #[test] fn explicit_completed_status_wins_over_legacy_failure_fields() { let item = sample_usage( "completed", Some(429), Some("rate limited on first attempt"), ); assert!(!admin_usage_is_failed(&item)); assert!(!admin_usage_matches_status(&item, Some("failed"))); assert!(admin_usage_matches_status(&item, Some("completed"))); } #[test] fn admin_usage_summaries_do_not_return_historical_raw_error_text() { let item = StoredRequestUsageAudit { error_category: Some("authentication_error".to_string()), ..sample_usage( "failed", Some(401), Some( "upstream said Authorization: Bearer live-secret at https://api.example?key=secret", ), ) }; let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); let active = admin_usage_active_request_json(&item, None, None, None); assert_eq!(record["error_message"], "authentication_error"); assert_eq!(active["error_message"], "authentication_error"); assert!(!record.to_string().contains("live-secret")); assert!(!active.to_string().contains("live-secret")); } #[test] fn client_requested_stream_prefers_request_metadata_flag() { let item = StoredRequestUsageAudit { is_stream: true, request_metadata: Some(json!({ "client_requested_stream": false })), ..sample_usage("completed", Some(200), None) }; assert!(!admin_usage_client_is_stream(&item)); let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["is_stream"], true); assert_eq!(record["upstream_is_stream"], true); assert_eq!(record["client_requested_stream"], false); assert_eq!(record["client_is_stream"], false); } #[test] fn admin_usage_payloads_project_end_to_end_timings_from_metadata() { let item = StoredRequestUsageAudit { response_time_ms: Some(626), first_byte_time_ms: Some(120), request_metadata: Some(json!({ "end_to_end_time_ms": 10_626, "end_to_end_first_byte_time_ms": 10_120, })), ..sample_usage("completed", Some(200), None) }; let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); let active = admin_usage_active_request_json(&item, None, None, None); for payload in [&record, &active] { assert_eq!(payload["response_time_ms"], 626); assert_eq!(payload["first_byte_time_ms"], 120); assert_eq!(payload["end_to_end_time_ms"], 10_626); assert_eq!(payload["end_to_end_first_byte_time_ms"], 10_120); } } #[test] fn admin_usage_payloads_expose_websocket_transport() { let item = StoredRequestUsageAudit { request_metadata: Some(json!({ "websocket_mode": true, "websocket_transport": "responses", "usage_available": false, "usage_pricing_available": false, "realtime_session": { "input_audio_tokens": 7, "output_audio_tokens": 3, }, })), ..sample_usage("completed", Some(200), None) }; let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); let active = admin_usage_active_request_json(&item, None, None, None); assert_eq!(record["is_websocket"], true); assert_eq!(active["is_websocket"], true); assert_eq!(record["websocket_transport"], "responses"); assert_eq!(active["websocket_transport"], "responses"); assert_eq!(record["usage_available"], false); assert_eq!(active["usage_available"], false); assert_eq!(record["usage_pricing_available"], false); assert_eq!(active["usage_pricing_available"], false); assert_eq!(record["input_audio_tokens"], 7); assert_eq!(active["input_audio_tokens"], 7); assert_eq!(record["output_audio_tokens"], 3); assert_eq!(active["output_audio_tokens"], 3); assert!(admin_usage_matches_status(&item, Some("websocket"))); assert!(admin_usage_matches_status(&item, Some("ws"))); assert!(admin_usage_matches_status(&item, Some("WS"))); assert!(!admin_usage_matches_status(&item, Some("standard"))); assert!(!admin_usage_matches_status(&item, Some("stream"))); } #[test] fn admin_usage_detail_preserves_websocket_and_session_metadata() { let live_session = json!({ "schema_version": "1", "transport": "websocket", "mode": "direct", "state": "closed", "client_frames": 4, "upstream_frames": 8, }); let realtime_session = json!({ "schema_version": "1", "transport": "websocket", "usage_state": "authoritative", "input_audio_tokens": 7, "output_audio_tokens": 3, }); let item = StoredRequestUsageAudit { request_type: Some("live".to_string()), api_format: Some("codex:live".to_string()), endpoint_api_format: Some("codex:live".to_string()), is_stream: true, request_metadata: Some(json!({ "websocket_mode": true, "websocket_transport": "codex_live_direct", "usage_available": false, "usage_pricing_available": false, "live_session": live_session, "realtime_session": realtime_session, })), ..sample_usage("completed", Some(200), None) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, false, None, &BTreeMap::new(), ); assert_eq!(payload["is_websocket"], true); assert_eq!(payload["websocket_transport"], "codex_live_direct"); assert_eq!(payload["live_session"], live_session); assert_eq!(payload["realtime_session"], realtime_session); assert_eq!(payload["metadata"]["live_session"], live_session); assert_eq!(payload["metadata"]["realtime_session"], realtime_session); } #[test] fn admin_usage_safe_metadata_keeps_only_numeric_realtime_audio_counters() { let projected = admin_usage_safe_metadata_value(&json!({ "input_audio_tokens": 99, "refresh_token": "should-be-removed", "realtime_session": { "input_audio_tokens": 7, "output_audio_tokens": 3, "input_audio_tokens_text": "should-be-removed", "refresh_token": 11, "nested": { "output_audio_tokens": 5 } } })); assert!(projected.get("input_audio_tokens").is_none()); assert!(projected.get("refresh_token").is_none()); assert_eq!(projected["realtime_session"]["input_audio_tokens"], 7); assert_eq!(projected["realtime_session"]["output_audio_tokens"], 3); assert!(projected["realtime_session"] .get("input_audio_tokens_text") .is_none()); assert!(projected["realtime_session"].get("refresh_token").is_none()); assert!(projected["realtime_session"]["nested"] .get("output_audio_tokens") .is_none()); } #[test] fn admin_usage_record_infers_client_family_from_user_agent() { let item = StoredRequestUsageAudit { request_metadata: Some(json!({ "client_ip": "192.168.0.28", "user_agent": "codex_vscode/0.131.0-alpha.9 (Windows 10.0.26200; x86_64)" })), ..sample_usage("completed", Some(200), None) }; let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); let active = admin_usage_active_request_json(&item, None, None, None); assert_eq!(record["client_family"], "codex_vscode"); assert_eq!(record["client_ip"], "192.168.0.28"); assert_eq!(active["client_family"], "codex_vscode"); } #[test] fn admin_usage_record_labels_openai_js_user_agent_as_sdk() { let item = StoredRequestUsageAudit { request_metadata: Some(json!({ "user_agent": "OpenAI/JS 6.34.0" })), ..sample_usage("completed", Some(200), None) }; let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["client_family"], "openai_js_sdk"); } #[test] fn admin_usage_record_prefers_typed_client_family() { let item = StoredRequestUsageAudit { client_family: Some("codex".to_string()), request_metadata: Some(json!({ "user_agent": "OpenAI/JS 6.34.0" })), ..sample_usage("completed", Some(200), None) }; let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["client_family"], "codex"); } #[test] fn admin_usage_record_includes_requested_and_provider_reasoning_efforts() { let item = StoredRequestUsageAudit { request_body: Some(json!({ "reasoning": { "effort": "xhigh" } })), provider_request_body: Some(json!({ "reasoning": { "effort": "max" }, "service_tier": "priority" })), ..sample_usage("completed", Some(200), None) }; let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); let active = admin_usage_active_request_json(&item, None, None, None); assert_eq!(record["requested_reasoning_effort"], "xhigh"); assert_eq!(active["requested_reasoning_effort"], "xhigh"); assert_eq!(record["reasoning_effort"], "max"); assert_eq!(active["reasoning_effort"], "max"); assert_eq!(record["service_tier"], "priority"); assert_eq!(active["service_tier"], "priority"); assert!(record["updated_at"].is_string()); assert_eq!(record["updated_at"], active["updated_at"]); } #[test] fn client_requested_stream_uses_stream_generate_content_path_over_stale_metadata_flag() { let item = StoredRequestUsageAudit { is_stream: true, request_metadata: Some(json!({ "client_requested_stream": false, "request_path": "/v1beta/models/gemini-3.1-flash-image-preview:streamGenerateContent", "request_path_and_query": "/v1beta/models/gemini-3.1-flash-image-preview:streamGenerateContent?alt=sse" })), ..sample_usage("completed", Some(200), None) }; assert!(admin_usage_client_is_stream(&item)); let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["upstream_is_stream"], true); assert_eq!(record["client_requested_stream"], true); assert_eq!(record["client_is_stream"], true); } #[test] fn client_requested_stream_falls_back_to_request_body_stream_flag() { let item = StoredRequestUsageAudit { is_stream: true, request_body: Some(json!({ "model": "gpt-5.4", "stream": false })), ..sample_usage("completed", Some(200), None) }; assert!(!admin_usage_client_is_stream(&item)); let active = admin_usage_active_request_json(&item, None, None, None); assert_eq!(active["is_stream"], true); assert_eq!(active["upstream_is_stream"], true); assert_eq!(active["client_requested_stream"], false); assert_eq!(active["client_is_stream"], false); } #[test] fn client_requested_stream_defaults_to_non_stream_for_openai_responses_request_body_without_flag( ) { let item = StoredRequestUsageAudit { is_stream: true, api_format: Some("openai:responses".to_string()), request_body: Some(json!({ "model": "gpt-5.4", "input": [{"role": "user", "content": "hi"}], "store": false })), ..sample_usage("completed", Some(200), None) }; assert!(!admin_usage_client_is_stream(&item)); let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["is_stream"], true); assert_eq!(record["upstream_is_stream"], true); assert_eq!(record["client_requested_stream"], false); assert_eq!(record["client_is_stream"], false); } #[test] fn client_requested_stream_defaults_to_non_stream_for_openai_search() { let item = StoredRequestUsageAudit { is_stream: true, api_format: Some("openai:search".to_string()), request_body: Some(json!({ "id": "session-search-1", "model": "gpt-5.6-sol", "input": "current documentation" })), ..sample_usage("completed", Some(200), None) }; assert!(!admin_usage_client_is_stream(&item)); let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["client_requested_stream"], false); assert_eq!(record["client_is_stream"], false); } #[test] fn upstream_stream_prefers_request_metadata_flag() { let item = StoredRequestUsageAudit { is_stream: false, request_metadata: Some(json!({ "client_requested_stream": false, "upstream_is_stream": true })), ..sample_usage("completed", Some(200), None) }; let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["is_stream"], false); assert_eq!(record["upstream_is_stream"], true); assert_eq!(record["client_requested_stream"], false); assert_eq!(record["client_is_stream"], false); } #[test] fn stream_modes_fall_back_to_captured_response_bodies_when_request_metadata_is_missing() { let item = StoredRequestUsageAudit { is_stream: true, response_body: Some(json!({ "chunks": [ {"type": "response.created"}, {"type": "response.output_text.delta", "delta": "Hello"} ], "metadata": { "stream": true, "stored_chunks": 2, "total_chunks": 2 } })), client_response_body: Some(json!({ "id": "resp-1", "object": "response", "status": "completed", "output": [] })), ..sample_usage("completed", Some(200), None) }; assert!(!admin_usage_client_is_stream(&item)); assert!(admin_usage_upstream_is_stream(&item)); let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["is_stream"], true); assert_eq!(record["upstream_is_stream"], true); assert_eq!(record["client_requested_stream"], false); assert_eq!(record["client_is_stream"], false); } #[test] fn stream_modes_fall_back_to_captured_response_headers_when_bodies_are_detached() { let item = StoredRequestUsageAudit { is_stream: true, response_headers: Some(json!({ "content-type": "text/event-stream; charset=utf-8" })), client_response_headers: Some(json!({ "content-type": "application/json" })), response_body_ref: Some("usage://request/req-1/response_body".to_string()), client_response_body_ref: Some( "usage://request/req-1/client_response_body".to_string(), ), ..sample_usage("completed", Some(200), None) }; assert!(!admin_usage_client_is_stream(&item)); assert!(admin_usage_upstream_is_stream(&item)); let record = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, ); assert_eq!(record["is_stream"], true); assert_eq!(record["upstream_is_stream"], true); assert_eq!(record["client_requested_stream"], false); assert_eq!(record["client_is_stream"], false); } #[test] fn replay_body_defaults_stream_to_client_requested_mode() { let item = StoredRequestUsageAudit { is_stream: true, request_body: Some(json!({ "model": "gpt-5.4", "input": "hello" })), request_metadata: Some(json!({ "client_requested_stream": false })), ..sample_usage("completed", Some(200), None) }; let body = admin_usage_resolve_request_capture_body(&item, None) .expect("replay body should resolve"); assert_eq!(body["stream"], false); } #[test] fn legacy_failure_signals_still_work_when_status_is_missing() { let item = StoredRequestUsageAudit { status: String::new(), ..sample_usage("completed", Some(429), Some("rate limited")) }; assert!(admin_usage_is_failed(&item)); assert!(admin_usage_matches_status(&item, Some("failed"))); } #[test] fn redirect_status_is_not_admin_usage_success() { let item = sample_usage("completed", Some(302), None); assert!(!admin_usage_is_success(&item)); assert!(!admin_usage_is_failed(&item)); assert!(admin_usage_matches_status(&item, Some("error"))); assert!(admin_usage_matches_status(&item, Some("completed"))); } #[test] fn failed_redirect_status_counts_as_admin_usage_failed() { let item = sample_usage( "failed", Some(302), Some("execution runtime stream returned non-success status 302"), ); assert!(admin_usage_is_failed(&item)); assert!(admin_usage_matches_status(&item, Some("failed"))); assert!(admin_usage_matches_status(&item, Some("error"))); assert!(!admin_usage_is_success(&item)); } #[test] fn active_status_with_failure_signal_counts_as_failed() { let item = sample_usage("pending", Some(503), Some("upstream failed")); assert!(admin_usage_is_failed(&item)); assert!(admin_usage_matches_status(&item, Some("failed"))); } #[test] fn admin_usage_fallback_flag_uses_routing_candidate_index() { let mut item = sample_usage("completed", Some(200), None); item.candidate_index = Some(2); assert!(admin_usage_has_fallback(&item)); assert!(admin_usage_matches_status(&item, Some("has_fallback"))); let payload = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, Some("primary"), ); assert_eq!(payload["has_fallback"], true); } #[test] fn body_availability_considers_typed_reference_fields() { let item = StoredRequestUsageAudit { request_body_ref: Some("usage://request/req-1/request_body".to_string()), ..sample_usage("completed", Some(200), None) }; assert!(admin_usage_has_body_value( &item, item.request_body.as_ref(), UsageBodyField::RequestBody )); } #[test] fn detail_payload_marks_reference_backed_bodies_as_available() { let item = StoredRequestUsageAudit { request_body: None, request_body_ref: Some("usage://request/req-1/request_body".to_string()), provider_request_body: None, provider_request_body_ref: Some( "usage://request/req-1/provider_request_body".to_string(), ), response_body: None, response_body_ref: Some("usage://request/req-1/response_body".to_string()), client_response_body: None, client_response_body_ref: Some( "usage://request/req-1/client_response_body".to_string(), ), request_metadata: Some(json!({ "request_body_ref": "usage://request/req-1/request_body", "provider_request_body_ref": "usage://request/req-1/provider_request_body", "response_body_ref": "usage://request/req-1/response_body", "client_response_body_ref": "usage://request/req-1/client_response_body", })), ..sample_usage("completed", Some(200), None) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, false, Some(json!({"model": "gpt-5"})), &BTreeMap::new(), ); assert!(payload["metadata"]["request_body_ref"].is_null()); assert!(payload["metadata"]["provider_request_body_ref"].is_null()); assert!(payload["metadata"]["response_body_ref"].is_null()); assert!(payload["metadata"]["client_response_body_ref"].is_null()); assert_eq!(payload["body_capture"]["request"]["available"], true); assert_eq!(payload["body_capture"]["request"]["storage"], "reference"); assert_eq!( payload["body_capture"]["request"]["body_ref"], "usage://request/req-1/request_body" ); assert_eq!( payload["body_capture"]["request"]["capture_source"], "stored_reference" ); assert_eq!( payload["body_capture"]["provider_request"]["body_ref"], "usage://request/req-1/provider_request_body" ); assert_eq!( payload["body_capture"]["response"]["body_ref"], "usage://request/req-1/response_body" ); assert_eq!( payload["body_capture"]["client_response"]["body_ref"], "usage://request/req-1/client_response_body" ); assert_eq!(payload["body_capture"]["response"]["available"], true); assert_eq!( payload["body_capture"]["client_response"]["available"], true ); assert_eq!(payload["has_provider_request_body"], true); assert_eq!(payload["has_response_body"], true); assert_eq!(payload["has_client_response_body"], true); } #[test] fn detail_payload_sanitizes_historical_request_metadata() { let item = StoredRequestUsageAudit { request_metadata: Some(json!({ "trace_id": "internal-trace", "authorization": "Bearer live-secret", "nested": { "refresh_token": "refresh-secret", "endpoint_url": "https://user:password@api.example.test/v1?key=secret#fragment", "safe_count": 2 } })), ..sample_usage("failed", Some(500), Some("failed")) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, false, None, &BTreeMap::new(), ); let metadata = &payload["metadata"]; assert!(metadata.get("authorization").is_none()); assert!(metadata["nested"].get("refresh_token").is_none()); assert_eq!( metadata["nested"]["endpoint_url"], "https://api.example.test/v1" ); assert_eq!(metadata["nested"]["safe_count"], 2); let encoded = metadata.to_string(); for secret in ["live-secret", "refresh-secret", "password", "key=secret"] { assert!(!encoded.contains(secret), "leaked {secret}"); } } #[test] fn detail_payload_separates_upstream_client_and_summary_errors() { let item = StoredRequestUsageAudit { error_message: Some( "execution runtime stream returned retryable status 400".to_string(), ), error_category: Some("server_error".to_string()), response_headers: Some(json!({ "content-type": "application/json" })), response_body: Some(json!({ "error": { "type": "retryable_upstream_status", "message": "execution runtime stream returned retryable status 400", "code": 400 } })), client_response_headers: Some(json!({ "content-type": "application/json" })), client_response_body: Some(json!({ "error": { "type": "http_error", "message": "local execution runtime exhausted" } })), ..sample_usage( "failed", Some(503), Some("execution runtime stream returned retryable status 400"), ) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, true, Some(json!({"model": "gpt-5.4"})), &BTreeMap::new(), ); assert_eq!(payload["upstream_error"]["source"], "upstream_response"); assert_eq!( payload["errors"]["upstream_error"], payload["upstream_error"] ); assert_eq!( payload["upstream_error"]["message"], "execution runtime stream returned retryable status 400" ); assert_eq!(payload["client_error"]["source"], "client_response"); assert_eq!( payload["client_error"]["message"], "local execution runtime exhausted" ); assert_eq!(payload["failure_summary"]["source"], "client_response"); assert_eq!( payload["failure_summary"]["message"], "local execution runtime exhausted" ); assert_eq!(payload["error_flow"]["propagation"], "converted"); assert!(payload["request_error"].is_null()); } #[test] fn detail_payload_marks_sensitive_upstream_account_errors_suppressed() { let item = StoredRequestUsageAudit { error_message: Some("credit balance exhausted".to_string()), error_category: Some("server_error".to_string()), response_body: Some(json!({ "error": { "type": "insufficient_quota", "message": "credit balance exhausted" } })), client_response_body: Some(json!({ "error": { "type": "http_error", "message": "upstream provider unavailable" } })), ..sample_usage("failed", Some(503), Some("credit balance exhausted")) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, true, Some(json!({"model": "gpt-5.4"})), &BTreeMap::new(), ); assert_eq!(payload["error_flow"]["propagation"], "suppressed"); assert_eq!(payload["error_flow"]["safe_to_expose_upstream"], false); assert_eq!(payload["upstream_error"]["type"], "insufficient_quota"); assert_eq!( payload["client_error"]["message"], "upstream provider unavailable" ); } #[test] fn detail_payload_does_not_promote_local_client_error_to_upstream_error() { let message = "没有可用提供商支持模型 gpt-5.4 的同步请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"; let client_message = "没有可用提供商支持模型 gpt-5.4 的同步请求"; let item = StoredRequestUsageAudit { provider_api_key_id: None, provider_request_headers: None, provider_request_body: None, provider_request_body_ref: None, candidate_id: None, execution_path: Some("local_execution_runtime_miss".to_string()), local_execution_runtime_miss_reason: Some("candidate_list_empty".to_string()), error_category: Some("http_error".to_string()), client_response_body: Some(json!({ "error": { "type": "http_error", "message": client_message } })), response_body: Some(json!({ "error": { "type": "http_error", "message": message } })), ..sample_usage("failed", Some(503), Some(message)) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, true, Some(json!({"model": "gpt-5.4"})), &BTreeMap::new(), ); assert!(payload["upstream_error"].is_null()); assert_eq!(payload["client_error"]["message"], client_message); assert_eq!( payload["client_response_body"]["error"]["message"], client_message ); assert_eq!(payload["error_flow"]["source"], "gateway"); assert_eq!(payload["error_flow"]["propagation"], "local"); } #[test] fn detail_payload_simplifies_local_client_error_when_client_body_is_unloaded() { let message = "没有可用提供商支持模型 gpt-5.4 的流式请求。请检查模型映射、端点启用状态和 API Key 权限(原因代码: candidate_list_empty)"; let item = StoredRequestUsageAudit { provider_api_key_id: None, provider_request_headers: None, provider_request_body: None, provider_request_body_ref: None, candidate_id: None, execution_path: Some("local_execution_runtime_miss".to_string()), local_execution_runtime_miss_reason: Some("candidate_list_empty".to_string()), error_category: Some("http_error".to_string()), client_response_headers: Some(json!({"content-type": "application/json"})), client_response_body: None, client_response_body_ref: Some( "usage://request/req-1/client_response_body".to_string(), ), response_body: None, ..sample_usage("failed", Some(503), Some(message)) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, false, Some(json!({"model": "gpt-5.4", "stream": true})), &BTreeMap::new(), ); assert!(payload["client_error"]["message"].is_null()); assert!(payload["failure_summary"].is_null()); assert_eq!( payload["scheduling_failure"]["title"], "本地调度失败:没有可调度候选" ); assert_eq!( payload["scheduling_failure"]["reason"], "candidate_list_empty" ); assert!(payload["scheduling_failure"]["reason_summary"].is_null()); assert!(payload["scheduling_failure"]["message"].is_null()); assert_eq!(payload["scheduling_failure"]["no_upstream_attempt"], true); } #[test] fn detail_payload_simplifies_all_candidates_skipped_when_client_body_is_unloaded() { let message = "找到 1 个支持模型 gpt-5.4 的候选提供商,但本次流式请求全部不可用:provider_quota_blocked 2 次(原因代码: all_candidates_skipped)"; let item = StoredRequestUsageAudit { provider_api_key_id: None, provider_request_headers: None, provider_request_body: None, provider_request_body_ref: None, candidate_id: None, execution_path: Some("local_execution_runtime_miss".to_string()), local_execution_runtime_miss_reason: Some("all_candidates_skipped".to_string()), error_category: Some("http_error".to_string()), client_response_headers: Some(json!({"content-type": "application/json"})), client_response_body: None, client_response_body_ref: Some( "usage://request/req-1/client_response_body".to_string(), ), response_body: None, ..sample_usage("failed", Some(503), Some(message)) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, false, Some(json!({"model": "gpt-5.4", "stream": true})), &BTreeMap::new(), ); assert!(payload["client_error"]["message"].is_null()); assert!(payload["failure_summary"].is_null()); assert_eq!( payload["scheduling_failure"]["title"], "本地调度失败:所有候选均被跳过" ); assert_eq!( payload["scheduling_failure"]["reason"], "all_candidates_skipped" ); assert!(payload["scheduling_failure"]["reason_summary"].is_null()); assert!(payload["scheduling_failure"]["message"].is_null()); assert_eq!(payload["scheduling_failure"]["no_upstream_attempt"], true); } #[test] fn detail_payload_preserves_legacy_body_capture_metadata_keys() { let item = StoredRequestUsageAudit { request_metadata: Some(json!({ "request_preview_source": "stored_original", "original_request_body_available": true, "original_response_body_available": true, "custom_key": "keep-me" })), ..sample_usage("completed", Some(200), None) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, false, Some(json!({"model": "gpt-5"})), &BTreeMap::new(), ); assert_eq!( payload["metadata"]["request_preview_source"], "stored_original" ); assert_eq!(payload["metadata"]["original_request_body_available"], true); assert_eq!( payload["metadata"]["original_response_body_available"], true ); assert_eq!(payload["metadata"]["custom_key"], "keep-me"); } #[test] fn admin_usage_record_prefers_typed_pricing_metadata_with_billing_snapshot_fallback() { let item = StoredRequestUsageAudit { output_price_per_1m: Some(15.0), request_metadata: Some(json!({ "rate_multiplier": 0.5, "is_free_tier": false, "input_price_per_1m": 3.0, "cache_creation_price_per_1m": 3.75, "billing_snapshot": { "resolved_variables": { "cache_read_price_per_1m": 0.30, "output_price_per_1m": 15.0 } } })), ..sample_usage("completed", Some(200), None) }; let payload = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, Some("primary"), ); assert_eq!(payload["rate_multiplier"], 0.5); assert_eq!(payload["is_free_tier"], false); assert_eq!(payload["input_price_per_1m"], 3.0); assert_eq!(payload["output_price_per_1m"], 15.0); assert_eq!(payload["cache_creation_price_per_1m"], 3.75); assert_eq!(payload["cache_read_price_per_1m"], 0.30); } #[test] fn admin_usage_record_rehydrates_cache_creation_total_from_classified_fields() { let item = StoredRequestUsageAudit { cache_creation_input_tokens: 0, cache_creation_ephemeral_5m_input_tokens: 12, cache_creation_ephemeral_1h_input_tokens: 8, ..sample_usage("completed", Some(200), None) }; let payload = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, Some("primary"), ); assert_eq!(payload["cache_creation_input_tokens"], 20); assert_eq!(payload["cache_creation_ephemeral_5m_input_tokens"], 12); assert_eq!(payload["cache_creation_ephemeral_1h_input_tokens"], 8); assert_eq!(payload["total_tokens"], 40); } #[test] fn admin_usage_total_tokens_uses_effective_input_for_cached_openai_usage() { let item = StoredRequestUsageAudit { input_tokens: 100, output_tokens: 20, total_tokens: 999, cache_creation_input_tokens: 0, cache_creation_ephemeral_5m_input_tokens: 12, cache_creation_ephemeral_1h_input_tokens: 8, cache_read_input_tokens: 80, ..sample_usage("completed", Some(200), None) }; assert_eq!(admin_usage_total_tokens(&item), 120); } #[test] fn detail_payload_exposes_typed_settlement_section() { let item = StoredRequestUsageAudit { output_price_per_1m: Some(15.0), request_metadata: Some(json!({ "trace_id": "trace-detail", "billing_snapshot_schema_version": "v2", "billing_snapshot_status": "resolved", "rate_multiplier": 0.5, "is_free_tier": false, "input_price_per_1m": 3.0, "output_price_per_1m": 9.0, "cache_creation_price_per_1m": 3.75, "cache_read_price_per_1m": 0.30, "price_per_request": 0.02, "settlement_snapshot_schema_version": "3.0", "billing_dimensions": { "input_tokens": 35, "total_input_context": 42 }, "settlement_snapshot": { "schema_version": "3.0", "pricing_snapshot": { "pricing_source": "provider_override", "tiered_pricing_source": "provider_override", "billing_processing_tier": "fast", "processing_tier_price_multiplier": 2.5, "tiered_pricing": { "tiers": [{ "up_to": null, "input_price_per_1m": 7.5, "output_price_per_1m": 37.5 }] } } }, "billing_snapshot": { "resolved_variables": { "output_price_per_1m": 11.0 } } })), ..sample_usage("completed", Some(200), None) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, None, false, Some(json!({"model": "gpt-5"})), &BTreeMap::new(), ); assert_eq!(payload["trace"]["trace_id"], "trace-detail"); assert_eq!( payload["settlement"]["billing_snapshot_schema_version"], "v2" ); assert_eq!( payload["settlement"]["billing_snapshot"]["resolved_variables"]["output_price_per_1m"], 11.0 ); assert_eq!( payload["settlement"]["settlement_snapshot_schema_version"], "3.0" ); assert_eq!( payload["settlement"]["settlement_snapshot"]["pricing_snapshot"]["pricing_source"], "provider_override" ); assert_eq!( payload["settlement"]["settlement_snapshot"]["pricing_snapshot"] ["billing_processing_tier"], "fast" ); assert_eq!( payload["settlement"]["settlement_snapshot"]["pricing_snapshot"] ["processing_tier_price_multiplier"], 2.5 ); assert_eq!( payload["settlement"]["settlement_snapshot"]["pricing_snapshot"]["tiered_pricing"] ["tiers"][0]["input_price_per_1m"], 7.5 ); assert_eq!( payload["settlement"]["billing_dimensions"]["input_tokens"], 35 ); assert_eq!(payload["settlement"]["billing_snapshot_status"], "resolved"); assert_eq!(payload["settlement"]["rate_multiplier"], 0.5); assert_eq!(payload["settlement"]["is_free_tier"], false); assert_eq!(payload["settlement"]["input_price_per_1m"], 3.0); assert_eq!(payload["settlement"]["output_price_per_1m"], 9.0); assert_eq!(payload["settlement"]["cache_creation_price_per_1m"], 3.75); assert_eq!(payload["settlement"]["cache_read_price_per_1m"], 0.30); assert_eq!(payload["settlement"]["price_per_request"], 0.02); assert!(payload["metadata"]["trace_id"].is_null()); assert!(payload["metadata"]["billing_snapshot"].is_null()); assert!(payload["metadata"]["billing_snapshot_schema_version"].is_null()); assert!(payload["metadata"]["billing_snapshot_status"].is_null()); assert!(payload["metadata"]["settlement_snapshot"].is_null()); assert!(payload["metadata"]["settlement_snapshot_schema_version"].is_null()); assert!(payload["metadata"]["billing_dimensions"].is_null()); assert!(payload["metadata"]["rate_multiplier"].is_null()); assert!(payload["metadata"]["is_free_tier"].is_null()); assert!(payload["metadata"]["input_price_per_1m"].is_null()); assert!(payload["metadata"]["output_price_per_1m"].is_null()); assert!(payload["metadata"]["cache_creation_price_per_1m"].is_null()); assert!(payload["metadata"]["cache_read_price_per_1m"].is_null()); assert!(payload["metadata"]["price_per_request"].is_null()); } #[test] fn admin_usage_provider_key_name_falls_back_to_typed_routing_metadata() { let item = StoredRequestUsageAudit { request_metadata: Some(json!({ "key_name": "upstream-primary" })), ..sample_usage("completed", Some(200), None) }; assert_eq!( super::admin_usage_provider_key_name(&item, &BTreeMap::new()), Some("upstream-primary".to_string()) ); } #[test] fn detail_payload_exposes_typed_routing_section() { let item = StoredRequestUsageAudit { request_metadata: Some(json!({ "trace_id": "trace-routing-detail", "model_id": "model-1", "global_model_id": "global-model-1", "global_model_name": "gpt-5" })), candidate_id: Some("cand-1".to_string()), candidate_index: Some(2), key_name: Some("upstream-primary".to_string()), planner_kind: Some("claude_cli_sync".to_string()), route_family: Some("claude".to_string()), route_kind: Some("cli".to_string()), execution_path: Some("local_execution_runtime_miss".to_string()), local_execution_runtime_miss_reason: Some("all_candidates_skipped".to_string()), ..sample_usage("completed", Some(200), None) }; let payload = build_admin_usage_detail_payload( &item, &BTreeMap::new(), &BTreeMap::new(), false, false, Some("resolved-primary"), false, Some(json!({"model": "gpt-5"})), &BTreeMap::new(), ); assert_eq!(payload["routing"]["candidate_id"], "cand-1"); assert_eq!(payload["routing"]["candidate_index"], 2); assert_eq!(payload["routing"]["key_name"], "resolved-primary"); assert_eq!(payload["routing"]["model_id"], "model-1"); assert_eq!(payload["routing"]["global_model_id"], "global-model-1"); assert_eq!(payload["routing"]["global_model_name"], "gpt-5"); assert_eq!(payload["routing"]["planner_kind"], "claude_cli_sync"); assert_eq!(payload["routing"]["route_family"], "claude"); assert_eq!(payload["routing"]["route_kind"], "cli"); assert_eq!( payload["routing"]["execution_path"], "local_execution_runtime_miss" ); assert_eq!( payload["routing"]["local_execution_runtime_miss_reason"], "all_candidates_skipped" ); assert!(payload["metadata"]["candidate_id"].is_null()); assert!(payload["metadata"]["candidate_index"].is_null()); assert!(payload["metadata"]["key_name"].is_null()); assert!(payload["metadata"]["model_id"].is_null()); assert!(payload["metadata"]["global_model_id"].is_null()); assert!(payload["metadata"]["global_model_name"].is_null()); assert!(payload["metadata"]["planner_kind"].is_null()); assert!(payload["metadata"]["trace_id"].is_null()); assert!(payload["metadata"]["route_family"].is_null()); assert!(payload["metadata"]["route_kind"].is_null()); assert!(payload["metadata"]["execution_path"].is_null()); assert!(payload["metadata"]["local_execution_runtime_miss_reason"].is_null()); } #[test] fn admin_usage_record_prefers_joined_user_and_api_key_names_over_legacy_columns() { let item = StoredRequestUsageAudit { username: Some("legacy-alice".to_string()), api_key_name: Some("legacy-default".to_string()), ..sample_usage("completed", Some(200), None) }; let users_by_id = BTreeMap::from([( "user-1".to_string(), aether_data::repository::users::StoredUserSummary::new( "user-1".to_string(), "fresh-alice".to_string(), Some("fresh-alice@example.com".to_string()), "user".to_string(), true, false, ) .expect("user summary should build"), )]); let api_key_names = BTreeMap::from([("api-key-1".to_string(), "fresh-default".to_string())]); let payload = admin_usage_record_json( &item, &users_by_id, &api_key_names, true, true, Some("primary"), ); assert_eq!(payload["username"], "fresh-alice"); assert_eq!(payload["api_key"]["name"], "fresh-default"); assert_eq!(payload["api_key"]["display"], "fresh-default"); assert_eq!(payload["api_key_name"], "fresh-default"); assert!(admin_usage_matches_search( &item, Some("fresh-default"), &users_by_id, &api_key_names, true, true )); assert!(admin_usage_matches_username( &item, Some("fresh-alice"), &users_by_id, true )); } #[test] fn admin_usage_record_does_not_fallback_to_legacy_username_when_reader_is_available() { let item = StoredRequestUsageAudit { username: Some("legacy-alice".to_string()), ..sample_usage("completed", Some(200), None) }; let payload = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), true, false, Some("primary"), ); assert_eq!(payload["username"], "已删除用户"); assert!(!admin_usage_matches_search( &item, Some("legacy-alice"), &BTreeMap::new(), &BTreeMap::new(), true, false )); assert!(!admin_usage_matches_username( &item, Some("legacy-alice"), &BTreeMap::new(), true )); } #[test] fn admin_usage_record_does_not_fallback_to_legacy_api_key_name_when_reader_is_available() { let item = StoredRequestUsageAudit { api_key_name: Some("legacy-default".to_string()), ..sample_usage("completed", Some(200), None) }; let payload = admin_usage_record_json( &item, &BTreeMap::new(), &BTreeMap::new(), false, true, Some("primary"), ); assert_eq!(payload["api_key"]["id"], "api-key-1"); assert!(payload["api_key"]["name"].is_null()); assert_eq!(payload["api_key"]["display"], "api-key-1"); assert!(payload["api_key_name"].is_null()); assert!(!admin_usage_matches_search( &item, Some("legacy-default"), &BTreeMap::new(), &BTreeMap::new(), false, true )); } }