merge(pr287): absorb remaining rust branch changes

Absorb the remaining changes from PR #287 into aether-rust-pioneer after resolving conflicts locally and preserving the admin fixes already landed in this branch.

Closes #287
Co-authored-by: AAEE86 <ppk0227@hotmail.com>
This commit is contained in:
fawney19
2026-04-11 03:07:26 +08:00
64 changed files with 8004 additions and 2530 deletions

View File

@@ -257,11 +257,15 @@ mod tests {
provider_name: Some("OpenAI".to_string()),
provider_website: None,
provider_type: Some("custom".to_string()),
provider_priority: Some(0),
provider_keep_priority_on_conversion: Some(false),
endpoint_api_format: Some("openai:chat".to_string()),
endpoint_api_family: Some("openai".to_string()),
endpoint_kind: Some("chat".to_string()),
provider_key_name: Some("prod".to_string()),
provider_key_auth_type: Some("api_key".to_string()),
provider_key_internal_priority: Some(10),
provider_key_global_priority_by_format: None,
provider_key_capabilities: None,
provider_key_is_active: Some(true),
}],

View File

@@ -79,6 +79,8 @@ impl UsageReadRepository for InMemoryUsageReadRepository {
.expect("usage repository lock")
.values()
.filter(|item| {
// The field is historically named `created_at_unix_ms`, but usage audit rows
// across gateway handlers, SQL repositories and tests are stored as epoch seconds.
if let Some(created_from_unix_secs) = query.created_from_unix_secs {
if item.created_at_unix_ms < created_from_unix_secs {
return false;
@@ -528,6 +530,69 @@ mod tests {
);
}
#[tokio::test]
async fn upsert_defaults_created_at_to_second_timestamp() {
let repository = InMemoryUsageReadRepository::default();
let stored = repository
.upsert(UpsertUsageRecord {
request_id: "req-upsert-ms-default".to_string(),
user_id: None,
api_key_id: None,
username: None,
api_key_name: None,
provider_name: "OpenAI".to_string(),
model: "gpt-5".to_string(),
target_model: None,
provider_id: None,
provider_endpoint_id: None,
provider_api_key_id: None,
request_type: None,
api_format: None,
api_family: None,
endpoint_kind: None,
endpoint_api_format: None,
provider_api_family: None,
provider_endpoint_kind: None,
has_format_conversion: None,
is_stream: None,
input_tokens: None,
output_tokens: None,
total_tokens: None,
cache_creation_input_tokens: None,
cache_creation_ephemeral_5m_input_tokens: None,
cache_creation_ephemeral_1h_input_tokens: None,
cache_read_input_tokens: None,
cache_creation_cost_usd: None,
cache_read_cost_usd: None,
output_price_per_1m: None,
total_cost_usd: None,
actual_total_cost_usd: None,
status_code: None,
error_message: None,
error_category: None,
response_time_ms: None,
first_byte_time_ms: None,
status: "completed".to_string(),
billing_status: "pending".to_string(),
request_headers: None,
request_body: None,
provider_request_headers: None,
provider_request_body: None,
response_headers: None,
response_body: None,
client_response_headers: None,
client_response_body: None,
request_metadata: None,
finalized_at_unix_secs: None,
created_at_unix_ms: None,
updated_at_unix_secs: 101,
})
.await
.expect("upsert should succeed");
assert_eq!(stored.created_at_unix_ms, 101);
}
#[tokio::test]
async fn summarizes_provider_usage_windows_since_timestamp() {
let repository = InMemoryUsageReadRepository::default().with_provider_usage_windows(vec![
@@ -595,4 +660,44 @@ mod tests {
assert_eq!(item.total_cost_usd, 0.24);
assert_eq!(item.last_used_at_unix_secs, Some(1_711_000_250));
}
#[tokio::test]
async fn list_usage_audits_applies_second_based_time_filters() {
let repository = InMemoryUsageReadRepository::seed(vec![
sample_usage("req-1", 1),
sample_usage("req-2", 2),
sample_usage("req-3", 3),
]);
let items = repository
.list_usage_audits(&crate::repository::usage::UsageAuditListQuery {
created_from_unix_secs: Some(2),
created_until_unix_secs: Some(3),
..Default::default()
})
.await
.expect("list should succeed");
assert_eq!(items.len(), 1);
assert_eq!(items[0].request_id, "req-2");
}
#[tokio::test]
async fn summarizes_provider_api_key_last_used_at_in_seconds() {
let repository = InMemoryUsageReadRepository::seed(vec![
sample_usage("req-1", 1_999),
sample_usage("req-2", 2_500),
]);
let summary = repository
.summarize_usage_by_provider_api_key_ids(&["provider-key-1".to_string()])
.await
.expect("summary should succeed");
let usage = summary
.get("provider-key-1")
.expect("provider key summary should exist");
assert_eq!(usage.request_count, 2);
assert_eq!(usage.last_used_at_unix_secs, Some(2_500));
}
}

View File

@@ -714,11 +714,21 @@ impl SqlxUsageReadRepository {
let request_count = row
.try_get::<i64, _>("request_count")
.map_postgres_err()?
.max(0) as u64;
.try_into()
.map_err(|_| {
DataLayerError::UnexpectedValue(
"usage.request_count aggregate is negative".to_string(),
)
})?;
let total_tokens = row
.try_get::<i64, _>("total_tokens")
.map_postgres_err()?
.max(0) as u64;
.try_into()
.map_err(|_| {
DataLayerError::UnexpectedValue(
"usage.total_tokens aggregate is negative".to_string(),
)
})?;
let total_cost_usd: f64 = row.try_get("total_cost_usd").map_postgres_err()?;
if !total_cost_usd.is_finite() {
return Err(DataLayerError::UnexpectedValue(
@@ -728,7 +738,14 @@ impl SqlxUsageReadRepository {
let last_used_at_unix_secs = row
.try_get::<Option<i64>, _>("last_used_at_unix_secs")
.map_postgres_err()?
.map(|value| value.max(0) as u64);
.map(|value| {
value.try_into().map_err(|_| {
DataLayerError::UnexpectedValue(
"usage.last_used_at_unix_secs aggregate is negative".to_string(),
)
})
})
.transpose()?;
summaries.insert(
provider_api_key_id.clone(),