Files
Aether/crates/aether-admin/src/observability/usage.rs
T

4271 lines
150 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<String> {
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<Body> {
(
http::StatusCode::SERVICE_UNAVAILABLE,
Json(json!({ "detail": detail })),
)
.into_response()
}
pub fn admin_usage_bad_request_response(detail: impl Into<String>) -> Response<Body> {
(
http::StatusCode::BAD_REQUEST,
Json(json!({ "detail": detail.into() })),
)
.into_response()
}
fn query_param_value(query: Option<&str>, key: &str) -> Option<String> {
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<String> {
let timestamp =
chrono::DateTime::<chrono::Utc>::from_timestamp(i64::try_from(unix_secs).ok()?, 0)?;
Some(timestamp.to_rfc3339())
}
fn admin_usage_response_time_updated_at(item: &StoredRequestUsageAudit) -> Option<String> {
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<usize, String> {
match query_param_value(query, "limit") {
None => Ok(100),
Some(value) => {
let parsed = value
.parse::<usize>()
.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<usize, String> {
match query_param_value(query, "offset") {
None => Ok(0),
Some(value) => value
.parse::<usize>()
.map_err(|_| "offset must be a non-negative integer".to_string()),
}
}
pub fn admin_usage_parse_ids(query: Option<&str>) -> Option<BTreeSet<String>> {
let ids = query_param_value(query, "ids")?;
let parsed: BTreeSet<String> = 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<u32, String> {
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<usize, String> {
match query_param_value(query, "limit") {
Some(value) => {
let parsed = value
.parse::<usize>()
.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<usize, String> {
match query_param_value(query, "limit") {
Some(value) => {
let parsed = value
.parse::<usize>()
.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<String, StoredUserSummary>,
auth_user_reader_available: bool,
) -> Option<String> {
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<String, String>,
auth_api_key_reader_available: bool,
) -> Option<String> {
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<String, StoredUserSummary>,
api_key_names: &BTreeMap<String, String>,
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<String, StoredUserSummary>,
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<String, Value>) {
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<String, Value>) {
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<String, Value>) {
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<String, Value>) {
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::<String>();
// 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<String> {
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<String> {
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<Value> {
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<String> {
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<u16>,
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<String> {
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<String> {
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<String> {
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<String> {
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<String> {
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<String> {
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::<Vec<_>>()
.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<String, Value>,
key: &str,
value: Option<f64>,
) {
if let Some(value) = value {
object.insert(key.to_string(), json!(value));
}
}
fn maybe_insert_u64_field(
object: &mut serde_json::Map<String, Value>,
key: &str,
value: Option<u64>,
) {
if let Some(value) = value {
object.insert(key.to_string(), json!(value));
}
}
fn maybe_insert_bool_field(
object: &mut serde_json::Map<String, Value>,
key: &str,
value: Option<bool>,
) {
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<String, String>,
) -> Option<String> {
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<bool> {
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<bool> {
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<bool> {
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<bool> {
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<u64> {
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<String>,
provider_key_name: Option<String>,
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);
}
value["response_model"] = json!(item.provider_response_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<String, StoredUserSummary>,
api_key_names: &BTreeMap<String, String>,
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,
"response_model": item.provider_response_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<String> {
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<String, Value>,
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<Value> = 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<Value> = 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<Value> = 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<chrono::NaiveDate, (u64, u64, f64, f64)> = 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::<chrono::Utc>::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<String, Vec<StoredRequestUsageAudit>> {
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<String, Vec<StoredRequestUsageAudit>> {
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<f64> {
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<f64> {
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<f64>,
p90_interval: Option<f64>,
) -> 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<f64>,
p90_interval: Option<f64>,
) -> 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<String, Vec<Value>>,
limit: usize,
total_points: usize,
) -> BTreeMap<String, usize> {
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<String> {
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<String> {
admin_usage_id_from_path_suffix(request_path, None)
}
pub fn admin_usage_id_from_action_path(request_path: &str, action: &str) -> Option<String> {
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<Value>,
) -> Option<Value> {
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<BTreeMap<String, String>> {
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<String, String> {
BTreeMap::from([("Content-Type".to_string(), "application/json".to_string())])
}
pub fn admin_usage_build_curl_command(
url: Option<&str>,
headers: &BTreeMap<String, String>,
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<Body> {
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<Body> {
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<String, String>,
auth_api_key_reader_available: bool,
provider_key_names: &BTreeMap<String, String>,
image_progress_by_request_id: &BTreeMap<String, Value>,
state_overrides_by_request_id: &BTreeMap<String, Value>,
) -> Response<Body> {
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<String, StoredUserSummary>,
api_key_names: &BTreeMap<String, String>,
auth_user_reader_available: bool,
auth_api_key_reader_available: bool,
provider_key_names: &BTreeMap<String, String>,
total: usize,
limit: usize,
offset: usize,
) -> Response<Body> {
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<String>,
headers_json: Option<Value>,
headers: &BTreeMap<String, String>,
body: Option<&Value>,
) -> Response<Body> {
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<String, StoredUserSummary>,
api_key_names: &BTreeMap<String, String>,
auth_user_reader_available: bool,
auth_api_key_reader_available: bool,
provider_key_name: Option<&str>,
include_bodies: bool,
request_body: Option<Value>,
default_headers: &BTreeMap<String, String>,
) -> 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<String>,
request_body: Option<Value>,
url: &str,
headers: &BTreeMap<String, String>,
same_provider: bool,
same_endpoint: bool,
) -> Response<Body> {
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<i32>,
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_expose_response_model_separately_from_mapping() {
let item = StoredRequestUsageAudit {
target_model: Some("provider-mapped-model".to_string()),
request_metadata: Some(json!({
"provider_response_model": "gpt-5.1"
})),
..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["target_model"], "provider-mapped-model");
assert_eq!(payload["response_model"], "gpt-5.1");
}
}
#[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_preserves_original_headers_with_or_without_bodies() {
let item = StoredRequestUsageAudit {
request_headers: Some(json!({
"authorization": "Bearer original-client-token",
"originator": "codex-cli",
"session-id": "original-session",
"thread-id": "original-thread",
"x-codex-turn-metadata": "{\"turn_id\":\"original-turn\"}",
"x-openai-subagent": "reviewer"
})),
provider_request_headers: Some(json!({
"authorization": "Bearer original-provider-token",
"x-api-key": "original-provider-key"
})),
response_headers: Some(json!({
"set-cookie": ["session=original-upstream", "preference=original"],
"x-upstream-custom": "original-upstream-value"
})),
client_response_headers: Some(json!({
"set-cookie": "session=original-client",
"x-client-custom": "original-client-value"
})),
..sample_usage("completed", Some(200), None)
};
for include_bodies in [false, true] {
let payload = build_admin_usage_detail_payload(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
include_bodies,
None,
&BTreeMap::new(),
);
for (field, expected) in [
("request_headers", &item.request_headers),
("provider_request_headers", &item.provider_request_headers),
("response_headers", &item.response_headers),
("client_response_headers", &item.client_response_headers),
] {
assert_eq!(
&payload[field],
expected.as_ref().unwrap(),
"{field} should retain original values when include_bodies={include_bodies}"
);
}
}
}
#[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:[email protected]/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("[email protected]".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
));
}
}