Files
Aether/crates/aether-admin/src/observability/usage.rs
T
elky 579f2c7cc1 feat(security): harden gateway boundaries and usage policies
Consolidate subscription usage policy enforcement, privacy-safe persistence, and gateway security hardening into one reviewable change.

Includes bounded HTTP and execution envelopes, header and protocol guards, DNS and relay validation, authentication and secret projection hardening, secure backup/install paths, and regression coverage.
2026-09-04 03:45:52 +08:00

4189 lines
146 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);
}
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,
"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_project_end_to_end_timings_from_metadata() {
let item = StoredRequestUsageAudit {
response_time_ms: Some(626),
first_byte_time_ms: Some(120),
request_metadata: Some(json!({
"end_to_end_time_ms": 10_626,
"end_to_end_first_byte_time_ms": 10_120,
})),
..sample_usage("completed", Some(200), None)
};
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
let active = admin_usage_active_request_json(&item, None, None, None);
for payload in [&record, &active] {
assert_eq!(payload["response_time_ms"], 626);
assert_eq!(payload["first_byte_time_ms"], 120);
assert_eq!(payload["end_to_end_time_ms"], 10_626);
assert_eq!(payload["end_to_end_first_byte_time_ms"], 10_120);
}
}
#[test]
fn admin_usage_payloads_expose_websocket_transport() {
let item = StoredRequestUsageAudit {
request_metadata: Some(json!({
"websocket_mode": true,
"websocket_transport": "responses",
"usage_available": false,
"usage_pricing_available": false,
"realtime_session": {
"input_audio_tokens": 7,
"output_audio_tokens": 3,
},
})),
..sample_usage("completed", Some(200), None)
};
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
let active = admin_usage_active_request_json(&item, None, None, None);
assert_eq!(record["is_websocket"], true);
assert_eq!(active["is_websocket"], true);
assert_eq!(record["websocket_transport"], "responses");
assert_eq!(active["websocket_transport"], "responses");
assert_eq!(record["usage_available"], false);
assert_eq!(active["usage_available"], false);
assert_eq!(record["usage_pricing_available"], false);
assert_eq!(active["usage_pricing_available"], false);
assert_eq!(record["input_audio_tokens"], 7);
assert_eq!(active["input_audio_tokens"], 7);
assert_eq!(record["output_audio_tokens"], 3);
assert_eq!(active["output_audio_tokens"], 3);
assert!(admin_usage_matches_status(&item, Some("websocket")));
assert!(admin_usage_matches_status(&item, Some("ws")));
assert!(admin_usage_matches_status(&item, Some("WS")));
assert!(!admin_usage_matches_status(&item, Some("standard")));
assert!(!admin_usage_matches_status(&item, Some("stream")));
}
#[test]
fn admin_usage_detail_preserves_websocket_and_session_metadata() {
let live_session = json!({
"schema_version": "1",
"transport": "websocket",
"mode": "direct",
"state": "closed",
"client_frames": 4,
"upstream_frames": 8,
});
let realtime_session = json!({
"schema_version": "1",
"transport": "websocket",
"usage_state": "authoritative",
"input_audio_tokens": 7,
"output_audio_tokens": 3,
});
let item = StoredRequestUsageAudit {
request_type: Some("live".to_string()),
api_format: Some("codex:live".to_string()),
endpoint_api_format: Some("codex:live".to_string()),
is_stream: true,
request_metadata: Some(json!({
"websocket_mode": true,
"websocket_transport": "codex_live_direct",
"usage_available": false,
"usage_pricing_available": false,
"live_session": live_session,
"realtime_session": realtime_session,
})),
..sample_usage("completed", Some(200), None)
};
let payload = build_admin_usage_detail_payload(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
false,
None,
&BTreeMap::new(),
);
assert_eq!(payload["is_websocket"], true);
assert_eq!(payload["websocket_transport"], "codex_live_direct");
assert_eq!(payload["live_session"], live_session);
assert_eq!(payload["realtime_session"], realtime_session);
assert_eq!(payload["metadata"]["live_session"], live_session);
assert_eq!(payload["metadata"]["realtime_session"], realtime_session);
}
#[test]
fn admin_usage_safe_metadata_keeps_only_numeric_realtime_audio_counters() {
let projected = admin_usage_safe_metadata_value(&json!({
"input_audio_tokens": 99,
"refresh_token": "should-be-removed",
"realtime_session": {
"input_audio_tokens": 7,
"output_audio_tokens": 3,
"input_audio_tokens_text": "should-be-removed",
"refresh_token": 11,
"nested": {
"output_audio_tokens": 5
}
}
}));
assert!(projected.get("input_audio_tokens").is_none());
assert!(projected.get("refresh_token").is_none());
assert_eq!(projected["realtime_session"]["input_audio_tokens"], 7);
assert_eq!(projected["realtime_session"]["output_audio_tokens"], 3);
assert!(projected["realtime_session"]
.get("input_audio_tokens_text")
.is_none());
assert!(projected["realtime_session"].get("refresh_token").is_none());
assert!(projected["realtime_session"]["nested"]
.get("output_audio_tokens")
.is_none());
}
#[test]
fn admin_usage_record_infers_client_family_from_user_agent() {
let item = StoredRequestUsageAudit {
request_metadata: Some(json!({
"client_ip": "192.168.0.28",
"user_agent": "codex_vscode/0.131.0-alpha.9 (Windows 10.0.26200; x86_64)"
})),
..sample_usage("completed", Some(200), None)
};
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
let active = admin_usage_active_request_json(&item, None, None, None);
assert_eq!(record["client_family"], "codex_vscode");
assert_eq!(record["client_ip"], "192.168.0.28");
assert_eq!(active["client_family"], "codex_vscode");
}
#[test]
fn admin_usage_record_labels_openai_js_user_agent_as_sdk() {
let item = StoredRequestUsageAudit {
request_metadata: Some(json!({
"user_agent": "OpenAI/JS 6.34.0"
})),
..sample_usage("completed", Some(200), None)
};
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
assert_eq!(record["client_family"], "openai_js_sdk");
}
#[test]
fn admin_usage_record_prefers_typed_client_family() {
let item = StoredRequestUsageAudit {
client_family: Some("codex".to_string()),
request_metadata: Some(json!({
"user_agent": "OpenAI/JS 6.34.0"
})),
..sample_usage("completed", Some(200), None)
};
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
assert_eq!(record["client_family"], "codex");
}
#[test]
fn admin_usage_record_includes_requested_and_provider_reasoning_efforts() {
let item = StoredRequestUsageAudit {
request_body: Some(json!({
"reasoning": { "effort": "xhigh" }
})),
provider_request_body: Some(json!({
"reasoning": { "effort": "max" },
"service_tier": "priority"
})),
..sample_usage("completed", Some(200), None)
};
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
let active = admin_usage_active_request_json(&item, None, None, None);
assert_eq!(record["requested_reasoning_effort"], "xhigh");
assert_eq!(active["requested_reasoning_effort"], "xhigh");
assert_eq!(record["reasoning_effort"], "max");
assert_eq!(active["reasoning_effort"], "max");
assert_eq!(record["service_tier"], "priority");
assert_eq!(active["service_tier"], "priority");
assert!(record["updated_at"].is_string());
assert_eq!(record["updated_at"], active["updated_at"]);
}
#[test]
fn client_requested_stream_uses_stream_generate_content_path_over_stale_metadata_flag() {
let item = StoredRequestUsageAudit {
is_stream: true,
request_metadata: Some(json!({
"client_requested_stream": false,
"request_path": "/v1beta/models/gemini-3.1-flash-image-preview:streamGenerateContent",
"request_path_and_query": "/v1beta/models/gemini-3.1-flash-image-preview:streamGenerateContent?alt=sse"
})),
..sample_usage("completed", Some(200), None)
};
assert!(admin_usage_client_is_stream(&item));
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
assert_eq!(record["upstream_is_stream"], true);
assert_eq!(record["client_requested_stream"], true);
assert_eq!(record["client_is_stream"], true);
}
#[test]
fn client_requested_stream_falls_back_to_request_body_stream_flag() {
let item = StoredRequestUsageAudit {
is_stream: true,
request_body: Some(json!({
"model": "gpt-5.4",
"stream": false
})),
..sample_usage("completed", Some(200), None)
};
assert!(!admin_usage_client_is_stream(&item));
let active = admin_usage_active_request_json(&item, None, None, None);
assert_eq!(active["is_stream"], true);
assert_eq!(active["upstream_is_stream"], true);
assert_eq!(active["client_requested_stream"], false);
assert_eq!(active["client_is_stream"], false);
}
#[test]
fn client_requested_stream_defaults_to_non_stream_for_openai_responses_request_body_without_flag(
) {
let item = StoredRequestUsageAudit {
is_stream: true,
api_format: Some("openai:responses".to_string()),
request_body: Some(json!({
"model": "gpt-5.4",
"input": [{"role": "user", "content": "hi"}],
"store": false
})),
..sample_usage("completed", Some(200), None)
};
assert!(!admin_usage_client_is_stream(&item));
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
assert_eq!(record["is_stream"], true);
assert_eq!(record["upstream_is_stream"], true);
assert_eq!(record["client_requested_stream"], false);
assert_eq!(record["client_is_stream"], false);
}
#[test]
fn client_requested_stream_defaults_to_non_stream_for_openai_search() {
let item = StoredRequestUsageAudit {
is_stream: true,
api_format: Some("openai:search".to_string()),
request_body: Some(json!({
"id": "session-search-1",
"model": "gpt-5.6-sol",
"input": "current documentation"
})),
..sample_usage("completed", Some(200), None)
};
assert!(!admin_usage_client_is_stream(&item));
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
assert_eq!(record["client_requested_stream"], false);
assert_eq!(record["client_is_stream"], false);
}
#[test]
fn upstream_stream_prefers_request_metadata_flag() {
let item = StoredRequestUsageAudit {
is_stream: false,
request_metadata: Some(json!({
"client_requested_stream": false,
"upstream_is_stream": true
})),
..sample_usage("completed", Some(200), None)
};
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
assert_eq!(record["is_stream"], false);
assert_eq!(record["upstream_is_stream"], true);
assert_eq!(record["client_requested_stream"], false);
assert_eq!(record["client_is_stream"], false);
}
#[test]
fn stream_modes_fall_back_to_captured_response_bodies_when_request_metadata_is_missing() {
let item = StoredRequestUsageAudit {
is_stream: true,
response_body: Some(json!({
"chunks": [
{"type": "response.created"},
{"type": "response.output_text.delta", "delta": "Hello"}
],
"metadata": {
"stream": true,
"stored_chunks": 2,
"total_chunks": 2
}
})),
client_response_body: Some(json!({
"id": "resp-1",
"object": "response",
"status": "completed",
"output": []
})),
..sample_usage("completed", Some(200), None)
};
assert!(!admin_usage_client_is_stream(&item));
assert!(admin_usage_upstream_is_stream(&item));
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
assert_eq!(record["is_stream"], true);
assert_eq!(record["upstream_is_stream"], true);
assert_eq!(record["client_requested_stream"], false);
assert_eq!(record["client_is_stream"], false);
}
#[test]
fn stream_modes_fall_back_to_captured_response_headers_when_bodies_are_detached() {
let item = StoredRequestUsageAudit {
is_stream: true,
response_headers: Some(json!({
"content-type": "text/event-stream; charset=utf-8"
})),
client_response_headers: Some(json!({
"content-type": "application/json"
})),
response_body_ref: Some("usage://request/req-1/response_body".to_string()),
client_response_body_ref: Some(
"usage://request/req-1/client_response_body".to_string(),
),
..sample_usage("completed", Some(200), None)
};
assert!(!admin_usage_client_is_stream(&item));
assert!(admin_usage_upstream_is_stream(&item));
let record = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
);
assert_eq!(record["is_stream"], true);
assert_eq!(record["upstream_is_stream"], true);
assert_eq!(record["client_requested_stream"], false);
assert_eq!(record["client_is_stream"], false);
}
#[test]
fn replay_body_defaults_stream_to_client_requested_mode() {
let item = StoredRequestUsageAudit {
is_stream: true,
request_body: Some(json!({
"model": "gpt-5.4",
"input": "hello"
})),
request_metadata: Some(json!({
"client_requested_stream": false
})),
..sample_usage("completed", Some(200), None)
};
let body = admin_usage_resolve_request_capture_body(&item, None)
.expect("replay body should resolve");
assert_eq!(body["stream"], false);
}
#[test]
fn legacy_failure_signals_still_work_when_status_is_missing() {
let item = StoredRequestUsageAudit {
status: String::new(),
..sample_usage("completed", Some(429), Some("rate limited"))
};
assert!(admin_usage_is_failed(&item));
assert!(admin_usage_matches_status(&item, Some("failed")));
}
#[test]
fn redirect_status_is_not_admin_usage_success() {
let item = sample_usage("completed", Some(302), None);
assert!(!admin_usage_is_success(&item));
assert!(!admin_usage_is_failed(&item));
assert!(admin_usage_matches_status(&item, Some("error")));
assert!(admin_usage_matches_status(&item, Some("completed")));
}
#[test]
fn failed_redirect_status_counts_as_admin_usage_failed() {
let item = sample_usage(
"failed",
Some(302),
Some("execution runtime stream returned non-success status 302"),
);
assert!(admin_usage_is_failed(&item));
assert!(admin_usage_matches_status(&item, Some("failed")));
assert!(admin_usage_matches_status(&item, Some("error")));
assert!(!admin_usage_is_success(&item));
}
#[test]
fn active_status_with_failure_signal_counts_as_failed() {
let item = sample_usage("pending", Some(503), Some("upstream failed"));
assert!(admin_usage_is_failed(&item));
assert!(admin_usage_matches_status(&item, Some("failed")));
}
#[test]
fn admin_usage_fallback_flag_uses_routing_candidate_index() {
let mut item = sample_usage("completed", Some(200), None);
item.candidate_index = Some(2);
assert!(admin_usage_has_fallback(&item));
assert!(admin_usage_matches_status(&item, Some("has_fallback")));
let payload = admin_usage_record_json(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
Some("primary"),
);
assert_eq!(payload["has_fallback"], true);
}
#[test]
fn body_availability_considers_typed_reference_fields() {
let item = StoredRequestUsageAudit {
request_body_ref: Some("usage://request/req-1/request_body".to_string()),
..sample_usage("completed", Some(200), None)
};
assert!(admin_usage_has_body_value(
&item,
item.request_body.as_ref(),
UsageBodyField::RequestBody
));
}
#[test]
fn detail_payload_marks_reference_backed_bodies_as_available() {
let item = StoredRequestUsageAudit {
request_body: None,
request_body_ref: Some("usage://request/req-1/request_body".to_string()),
provider_request_body: None,
provider_request_body_ref: Some(
"usage://request/req-1/provider_request_body".to_string(),
),
response_body: None,
response_body_ref: Some("usage://request/req-1/response_body".to_string()),
client_response_body: None,
client_response_body_ref: Some(
"usage://request/req-1/client_response_body".to_string(),
),
request_metadata: Some(json!({
"request_body_ref": "usage://request/req-1/request_body",
"provider_request_body_ref": "usage://request/req-1/provider_request_body",
"response_body_ref": "usage://request/req-1/response_body",
"client_response_body_ref": "usage://request/req-1/client_response_body",
})),
..sample_usage("completed", Some(200), None)
};
let payload = build_admin_usage_detail_payload(
&item,
&BTreeMap::new(),
&BTreeMap::new(),
false,
false,
None,
false,
Some(json!({"model": "gpt-5"})),
&BTreeMap::new(),
);
assert!(payload["metadata"]["request_body_ref"].is_null());
assert!(payload["metadata"]["provider_request_body_ref"].is_null());
assert!(payload["metadata"]["response_body_ref"].is_null());
assert!(payload["metadata"]["client_response_body_ref"].is_null());
assert_eq!(payload["body_capture"]["request"]["available"], true);
assert_eq!(payload["body_capture"]["request"]["storage"], "reference");
assert_eq!(
payload["body_capture"]["request"]["body_ref"],
"usage://request/req-1/request_body"
);
assert_eq!(
payload["body_capture"]["request"]["capture_source"],
"stored_reference"
);
assert_eq!(
payload["body_capture"]["provider_request"]["body_ref"],
"usage://request/req-1/provider_request_body"
);
assert_eq!(
payload["body_capture"]["response"]["body_ref"],
"usage://request/req-1/response_body"
);
assert_eq!(
payload["body_capture"]["client_response"]["body_ref"],
"usage://request/req-1/client_response_body"
);
assert_eq!(payload["body_capture"]["response"]["available"], true);
assert_eq!(
payload["body_capture"]["client_response"]["available"],
true
);
assert_eq!(payload["has_provider_request_body"], true);
assert_eq!(payload["has_response_body"], true);
assert_eq!(payload["has_client_response_body"], true);
}
#[test]
fn detail_payload_sanitizes_historical_request_metadata() {
let item = StoredRequestUsageAudit {
request_metadata: Some(json!({
"trace_id": "internal-trace",
"authorization": "Bearer live-secret",
"nested": {
"refresh_token": "refresh-secret",
"endpoint_url": "https://user:[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
));
}
}