mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-12 14:10:19 +08:00
feat(usage): show reasoning and fast badges in usage records
- extract provider reasoning effort from request body metadata - extract priority service tier and expose it as service_tier - show reasoning level and fast badges after model names - include badges in active request updates and usage list payloads - add targeted backend and frontend coverage
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
mod types;
|
||||
|
||||
pub use types::{
|
||||
extract_provider_reasoning_effort_from_body, extract_provider_service_tier_from_body,
|
||||
parse_usage_body_ref, usage_body_ref, usage_request_metadata_client_family,
|
||||
ApiKeyLastUsedDelta, ManagementTokenCounterDelta, PendingUsageCleanupSummary,
|
||||
ProviderApiKeyWindowUsageRequest, ProxyNodeCounterDelta, StoredProviderApiKeyUsageSummary,
|
||||
@@ -27,5 +28,6 @@ pub use types::{
|
||||
UsageLeaderboardQuery, UsageMonitoringErrorCountQuery, UsageMonitoringErrorListQuery,
|
||||
UsagePerformancePercentilesQuery, UsageProviderPerformanceQuery, UsageReadRepository,
|
||||
UsageRepository, UsageSettledCostSummaryQuery, UsageTimeSeriesGranularity,
|
||||
UsageTimeSeriesQuery, UsageWriteRepository,
|
||||
UsageTimeSeriesQuery, UsageWriteRepository, PROVIDER_REASONING_EFFORT_METADATA_KEY,
|
||||
PROVIDER_SERVICE_TIER_METADATA_KEY,
|
||||
};
|
||||
|
||||
@@ -2,6 +2,48 @@ use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_json::Value;
|
||||
|
||||
pub const PROVIDER_REASONING_EFFORT_METADATA_KEY: &str = "provider_reasoning_effort";
|
||||
pub const PROVIDER_SERVICE_TIER_METADATA_KEY: &str = "provider_service_tier";
|
||||
|
||||
pub fn extract_provider_reasoning_effort_from_body(value: Option<&Value>) -> Option<String> {
|
||||
let object = value.and_then(Value::as_object)?;
|
||||
object
|
||||
.get("reasoning_effort")
|
||||
.and_then(Value::as_str)
|
||||
.or_else(|| {
|
||||
object
|
||||
.get("reasoning")
|
||||
.and_then(Value::as_object)
|
||||
.and_then(|reasoning| reasoning.get("effort"))
|
||||
.and_then(Value::as_str)
|
||||
})
|
||||
.and_then(normalize_provider_reasoning_effort)
|
||||
}
|
||||
|
||||
fn normalize_provider_reasoning_effort(value: &str) -> Option<String> {
|
||||
let normalized = value.trim().to_ascii_lowercase();
|
||||
if normalized.is_empty() || normalized.len() > 64 {
|
||||
return None;
|
||||
}
|
||||
Some(normalized)
|
||||
}
|
||||
|
||||
pub fn extract_provider_service_tier_from_body(value: Option<&Value>) -> Option<String> {
|
||||
value
|
||||
.and_then(Value::as_object)
|
||||
.and_then(|object| object.get("service_tier"))
|
||||
.and_then(Value::as_str)
|
||||
.and_then(normalize_provider_service_tier)
|
||||
}
|
||||
|
||||
fn normalize_provider_service_tier(value: &str) -> Option<String> {
|
||||
let normalized = value.trim().to_ascii_lowercase();
|
||||
if normalized.is_empty() || normalized.len() > 64 {
|
||||
return None;
|
||||
}
|
||||
Some(normalized)
|
||||
}
|
||||
|
||||
/// Joined usage read model assembled from the accounting row plus the newer audit/snapshot
|
||||
/// satellite tables.
|
||||
///
|
||||
@@ -375,6 +417,22 @@ impl StoredRequestUsageAudit {
|
||||
self.request_metadata_string("trace_id")
|
||||
}
|
||||
|
||||
pub fn provider_reasoning_effort(&self) -> Option<String> {
|
||||
self.request_metadata_string(PROVIDER_REASONING_EFFORT_METADATA_KEY)
|
||||
.and_then(normalize_provider_reasoning_effort)
|
||||
.or_else(|| {
|
||||
extract_provider_reasoning_effort_from_body(self.provider_request_body.as_ref())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn provider_service_tier(&self) -> Option<String> {
|
||||
self.request_metadata_string(PROVIDER_SERVICE_TIER_METADATA_KEY)
|
||||
.and_then(normalize_provider_service_tier)
|
||||
.or_else(|| {
|
||||
extract_provider_service_tier_from_body(self.provider_request_body.as_ref())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn body_ref(&self, field: UsageBodyField) -> Option<&str> {
|
||||
match field {
|
||||
UsageBodyField::RequestBody => self.request_body_ref.as_deref(),
|
||||
@@ -2339,6 +2397,35 @@ mod tests {
|
||||
assert_eq!(result.request_capture_source(), "stored_original");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_reasoning_effort_reads_metadata_and_provider_request_body() {
|
||||
let mut usage = sample_usage();
|
||||
usage.provider_request_body = Some(json!({
|
||||
"reasoning": { "effort": "XHigh" },
|
||||
"service_tier": "Priority"
|
||||
}));
|
||||
|
||||
assert_eq!(usage.provider_reasoning_effort().as_deref(), Some("xhigh"));
|
||||
assert_eq!(usage.provider_service_tier().as_deref(), Some("priority"));
|
||||
|
||||
usage.request_metadata = Some(json!({
|
||||
"provider_reasoning_effort": "max",
|
||||
"provider_service_tier": "standard"
|
||||
}));
|
||||
|
||||
assert_eq!(usage.provider_reasoning_effort().as_deref(), Some("max"));
|
||||
assert_eq!(usage.provider_service_tier().as_deref(), Some("standard"));
|
||||
|
||||
usage.request_metadata = None;
|
||||
usage.provider_request_body = Some(json!({
|
||||
"reasoning_effort": "High",
|
||||
"service_tier": "priority"
|
||||
}));
|
||||
|
||||
assert_eq!(usage.provider_reasoning_effort().as_deref(), Some("high"));
|
||||
assert_eq!(usage.provider_service_tier().as_deref(), Some("priority"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_body_capture_json_entry_includes_capture_source_and_body_ref() {
|
||||
let mut usage = sample_usage();
|
||||
|
||||
Reference in New Issue
Block a user