Files
Aether/crates/aether-data/adapters/sqlite/src/usage/tests.rs
T

784 lines
26 KiB
Rust
Raw Normal View History

use super::{SqliteUsageReadRepository, SqliteUsageWriteRepository};
use crate::run_migrations;
use aether_data_contracts::repository::usage::{
UpsertUsageRecord, UsageAuditListQuery, UsageDailyHeatmapQuery,
UsageDashboardDailyBreakdownQuery, UsageDashboardSummaryQuery, UsageProviderPerformanceQuery,
UsageReadRepository, UsageTimeSeriesGranularity, UsageWriteRepository,
};
#[tokio::test]
async fn sqlite_provider_performance_can_skip_timeline() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
SqliteUsageWriteRepository::new(pool.clone())
.upsert(sample_usage(
"provider-performance",
"completed",
"pending",
1_000,
))
.await
.expect("usage should upsert");
let reader = SqliteUsageReadRepository::new(pool);
let mut query = UsageProviderPerformanceQuery {
created_from_unix_secs: 0,
created_until_unix_secs: 2_000,
granularity: UsageTimeSeriesGranularity::Hour,
tz_offset_minutes: 0,
limit: 1,
provider_id: None,
model: None,
api_format: None,
endpoint_kind: None,
is_stream: None,
has_format_conversion: None,
slow_threshold_ms: 10_000,
include_timeline: true,
};
let with_timeline = reader
.summarize_usage_provider_performance(&query)
.await
.expect("provider performance should load");
assert_eq!(with_timeline.summary.request_count, 1);
assert_eq!(with_timeline.providers.len(), 1);
assert_eq!(with_timeline.timeline.len(), 1);
query.include_timeline = false;
let without_timeline = reader
.summarize_usage_provider_performance(&query)
.await
.expect("provider performance without timeline should load");
assert_eq!(without_timeline.summary, with_timeline.summary);
assert_eq!(without_timeline.providers, with_timeline.providers);
assert!(without_timeline.timeline.is_empty());
}
#[tokio::test]
async fn sqlite_usage_write_repository_upserts_and_rebuilds_stats() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
let repository = SqliteUsageWriteRepository::new(pool.clone());
let record = repository
.upsert(sample_usage("request-1", "completed", "pending", 1_000))
.await
.expect("usage should upsert");
assert_eq!(record.request_id, "request-1");
assert_eq!(record.api_key_id.as_deref(), Some("api-key-1"));
assert_eq!(record.total_tokens, 7);
assert_eq!(record.cache_read_input_tokens, 2);
assert_eq!(
record.request_metadata.as_ref().unwrap()["trace_id"],
"trace-1"
);
assert_eq!(
record.request_metadata.as_ref().unwrap()["upstream_is_stream"],
true
);
let upstream_is_stream: Option<i64> =
sqlx::query_scalar("SELECT upstream_is_stream FROM \"usage\" WHERE request_id = ?")
.bind("request-1")
.fetch_one(&pool)
.await
.expect("usage stream mode should load");
assert_eq!(upstream_is_stream, Some(1));
let loaded = repository
.find_by_request_id("request-1")
.await
.expect("usage should load")
.expect("usage should exist");
assert_eq!(
loaded.provider_api_key_id.as_deref(),
Some("provider-key-1")
);
let stats = sqlx::query_as::<_, (i64, i64, f64, Option<i64>)>(
"SELECT total_requests, total_tokens, total_cost_usd, last_used_at FROM api_keys WHERE id = 'api-key-1'",
)
.fetch_one(&pool)
.await
.expect("api key stats should load");
assert_eq!(stats, (1, 7, 0.5, Some(1_000)));
let provider_stats = sqlx::query_as::<_, (i64, i64, i64, i64, f64, i64, Option<i64>)>(
"SELECT request_count, success_count, error_count, total_tokens, total_cost_usd, total_response_time_ms, last_used_at FROM provider_api_keys WHERE id = 'provider-key-1'",
)
.fetch_one(&pool)
.await
.expect("provider key stats should load");
assert_eq!(provider_stats, (1, 1, 0, 7, 0.5, 42, Some(1_000)));
}
#[tokio::test]
async fn sqlite_usage_write_repository_does_not_regress_void_usage() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
let repository = SqliteUsageWriteRepository::new(pool);
repository
.upsert(sample_usage("request-1", "failed", "void", 1_000))
.await
.expect("void usage should upsert");
let existing = repository
.upsert(sample_usage("request-1", "pending", "pending", 1_001))
.await
.expect("stale usage should be ignored");
assert_eq!(existing.status, "failed");
assert_eq!(existing.billing_status, "void");
assert_eq!(existing.updated_at_unix_secs, 1_000);
}
#[tokio::test]
async fn sqlite_usage_write_repository_does_not_reopen_void_failure_from_late_streaming() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
let repository = SqliteUsageWriteRepository::new(pool);
for (request_id, status_code) in [
("request-late-active", None),
("request-late-response-start", Some(200)),
] {
let mut failed = sample_usage(request_id, "failed", "void", 1_000);
failed.status_code = Some(503);
repository
.upsert(failed)
.await
.expect("failed usage should upsert");
let mut late_streaming = sample_usage(request_id, "streaming", "pending", 1_001);
late_streaming.status_code = status_code;
late_streaming.finalized_at_unix_secs = None;
let current = repository
.upsert(late_streaming)
.await
.expect("late streaming usage should be ignored");
assert_eq!(current.status, "failed");
assert_eq!(current.billing_status, "void");
assert_eq!(current.status_code, Some(503));
assert_eq!(current.finalized_at_unix_secs, Some(1_000));
}
}
#[tokio::test]
async fn sqlite_usage_write_repository_does_not_regress_terminal_usage_from_late_streaming() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
let repository = SqliteUsageWriteRepository::new(pool);
repository
.upsert(sample_usage("request-1", "completed", "pending", 1_000))
.await
.expect("terminal usage should upsert");
let mut late_streaming = sample_usage("request-1", "streaming", "pending", 1_001);
late_streaming.input_tokens = Some(0);
late_streaming.output_tokens = Some(0);
late_streaming.total_tokens = Some(0);
late_streaming.cache_read_input_tokens = Some(0);
late_streaming.cache_read_cost_usd = Some(0.0);
late_streaming.total_cost_usd = Some(0.0);
late_streaming.actual_total_cost_usd = Some(0.0);
late_streaming.response_time_ms = Some(9_999);
late_streaming.first_byte_time_ms = Some(9_999);
late_streaming.finalized_at_unix_secs = None;
let current = repository
.upsert(late_streaming)
.await
.expect("late streaming usage should not regress terminal usage");
assert_eq!(current.status, "completed");
assert_eq!(current.billing_status, "pending");
assert_eq!(current.total_tokens, 7);
assert_eq!(current.cache_read_input_tokens, 2);
assert_eq!(current.total_cost_usd, 0.5);
assert_eq!(current.actual_total_cost_usd, 0.4);
assert_eq!(current.response_time_ms, Some(42));
assert_eq!(current.first_byte_time_ms, Some(12));
assert_eq!(current.finalized_at_unix_secs, Some(1_000));
assert_eq!(current.updated_at_unix_secs, 1_000);
}
#[tokio::test]
async fn sqlite_usage_write_repository_preserves_streaming_response_start_from_late_active() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
let repository = SqliteUsageWriteRepository::new(pool);
repository
.upsert(sample_usage(
"request-late-active",
"streaming",
"pending",
1_000,
))
.await
.expect("response-start usage should upsert");
let mut late_active = sample_usage("request-late-active", "streaming", "pending", 1_001);
late_active.status_code = None;
late_active.response_time_ms = None;
late_active.first_byte_time_ms = None;
let current = repository
.upsert(late_active)
.await
.expect("late active usage should not clear response-start fields");
assert_eq!(current.status, "streaming");
assert_eq!(current.status_code, Some(200));
assert_eq!(current.response_time_ms, Some(42));
assert_eq!(current.first_byte_time_ms, Some(12));
}
#[tokio::test]
async fn sqlite_usage_write_repository_cleans_stale_pending_requests() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
let repository = SqliteUsageWriteRepository::new(pool.clone());
repository
.upsert(sample_usage("request-recovered", "streaming", "pending", 1))
.await
.expect("streaming usage should upsert");
repository
.upsert(sample_usage("request-failed", "pending", "pending", 1))
.await
.expect("pending usage should upsert");
sqlx::query(
r#"
INSERT INTO request_candidates (
id,
request_id,
candidate_index,
retry_index,
status,
is_cached,
created_at
) VALUES
('candidate-recovered', 'request-recovered', 0, 0, 'streaming', 0, 1),
('candidate-failed', 'request-failed', 0, 0, 'pending', 0, 1)
"#,
)
.execute(&pool)
.await
.expect("request candidates should seed");
let summary = repository
.cleanup_stale_pending_requests(2, 10, 5, 1)
.await
.expect("cleanup should run");
assert_eq!(summary.recovered, 1);
assert_eq!(summary.failed, 1);
let recovered = repository
.find_by_request_id("request-recovered")
.await
.expect("recovered usage should load")
.expect("recovered usage should exist");
assert_eq!(recovered.status, "completed");
assert_eq!(recovered.status_code, Some(200));
let failed = repository
.find_by_request_id("request-failed")
.await
.expect("failed usage should load")
.expect("failed usage should exist");
assert_eq!(failed.status, "failed");
assert_eq!(failed.status_code, Some(504));
assert_eq!(failed.billing_status, "void");
assert_eq!(failed.total_cost_usd, 0.0);
assert_eq!(failed.finalized_at_unix_secs, Some(10));
let candidate_statuses = sqlx::query_as::<_, (String, String, Option<i64>)>(
r#"
SELECT request_id, status, finished_at
FROM request_candidates
ORDER BY request_id
"#,
)
.fetch_all(&pool)
.await
.expect("candidate statuses should load");
assert_eq!(
candidate_statuses,
vec![
(
"request-failed".to_string(),
"failed".to_string(),
Some(10_000)
),
(
"request-recovered".to_string(),
"success".to_string(),
Some(10_000)
),
]
);
let snapshot = sqlx::query_as::<_, (String, Option<i64>)>(
"SELECT billing_status, finalized_at FROM usage_settlement_snapshots WHERE request_id = 'request-failed'",
)
.fetch_one(&pool)
.await
.expect("void settlement snapshot should load");
assert_eq!(snapshot, ("void".to_string(), Some(10)));
}
#[tokio::test]
async fn sqlite_usage_write_repository_cleanup_uses_failed_candidate_status_when_present() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
let repository = SqliteUsageWriteRepository::new(pool.clone());
repository
.upsert(sample_usage(
"request-upstream-reset",
"pending",
"pending",
1,
))
.await
.expect("pending usage should upsert");
repository
.upsert(sample_usage("request-stuck", "pending", "pending", 1))
.await
.expect("pending usage should upsert");
// request-upstream-reset has a failed candidate carrying a concrete 502 status
// and a connection-reset message — cleanup should use them instead of 504.
// request-stuck has only a still-pending candidate, so cleanup should fall back to 504.
sqlx::query(
r#"
INSERT INTO request_candidates (
id,
request_id,
candidate_index,
retry_index,
status,
status_code,
error_message,
is_cached,
created_at,
started_at,
finished_at
) VALUES
('candidate-reset', 'request-upstream-reset', 0, 0, 'failed', 502, 'upstream connection reset by peer', 0, 1, 2, 3),
('candidate-stuck', 'request-stuck', 0, 0, 'pending', NULL, NULL, 0, 1, NULL, NULL)
"#,
)
.execute(&pool)
.await
.expect("request candidates should seed");
let summary = repository
.cleanup_stale_pending_requests(2, 10, 5, 5)
.await
.expect("cleanup should run");
assert_eq!(summary.recovered, 0);
assert_eq!(summary.failed, 2);
let reset = repository
.find_by_request_id("request-upstream-reset")
.await
.expect("upstream-reset usage should load")
.expect("upstream-reset usage should exist");
assert_eq!(reset.status, "failed");
assert_eq!(reset.status_code, Some(502));
assert_eq!(
reset.error_message.as_deref(),
Some("upstream connection reset by peer")
);
let stuck = repository
.find_by_request_id("request-stuck")
.await
.expect("stuck usage should load")
.expect("stuck usage should exist");
assert_eq!(stuck.status, "failed");
assert_eq!(stuck.status_code, Some(504));
assert!(stuck
.error_message
.as_deref()
.is_some_and(|message| message.contains("超过 5 分钟未完成")));
}
#[tokio::test]
async fn sqlite_usage_read_repository_reads_usage_contract_views() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
seed_stats_targets(&pool).await;
let writer = SqliteUsageWriteRepository::new(pool.clone());
writer
.upsert(sample_usage("request-1", "completed", "settled", 1_000))
.await
.expect("usage should upsert");
writer
.upsert(sample_usage("request-2", "failed", "void", 1_010))
.await
.expect("usage should upsert");
let reader = SqliteUsageReadRepository::new(pool);
let loaded = reader
.find_by_request_id("request-1")
.await
.expect("usage should load")
.expect("usage should exist");
assert_eq!(loaded.total_tokens, 7);
assert_eq!(loaded.billing_status, "settled");
let listed = reader
.list_usage_audits(&UsageAuditListQuery {
provider_name: Some("Provider One".to_string()),
newest_first: true,
..UsageAuditListQuery::default()
})
.await
.expect("usage list should load");
assert_eq!(listed.len(), 2);
assert_eq!(listed[0].request_id, "request-2");
let summary = reader
.summarize_dashboard_usage(&UsageDashboardSummaryQuery {
created_from_unix_secs: 999,
created_until_unix_secs: 1_020,
user_id: Some("user-1".to_string()),
})
.await
.expect("dashboard summary should load");
assert_eq!(summary.total_requests, 2);
assert_eq!(summary.error_requests, 1);
assert_eq!(summary.total_tokens, 10);
}
#[tokio::test]
async fn sqlite_usage_daily_heatmap_reads_imported_daily_aggregates() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
sqlx::query(
r#"
INSERT INTO stats_daily (
id, "date", total_requests, success_requests, error_requests,
input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
total_cost, actual_total_cost, is_complete, created_at, updated_at
) VALUES (
'daily-1', 86400, 9, 8, 1, 10, 20, 3, 4, 1.25, 1.0, 1, 1, 1
);
INSERT INTO stats_user_daily (
id, user_id, username, "date", total_requests, success_requests, error_requests,
input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
total_cost, created_at, updated_at
) VALUES (
'user-daily-1', 'user-1', 'user one', 86400, 5, 5, 0, 7, 8, 2, 1, 0.75, 1, 1
);
"#,
)
.execute(&pool)
.await
.expect("daily aggregates should seed");
let reader = SqliteUsageReadRepository::new(pool);
let admin = reader
.summarize_usage_daily_heatmap(&UsageDailyHeatmapQuery {
created_from_unix_secs: 0,
user_id: None,
admin_mode: true,
})
.await
.expect("admin heatmap should load");
assert_eq!(admin.len(), 1);
assert_eq!(admin[0].date, "1970-01-02");
assert_eq!(admin[0].requests, 9);
assert_eq!(admin[0].total_tokens, 37);
assert_eq!(admin[0].actual_total_cost_usd, 1.0);
let user = reader
.summarize_usage_daily_heatmap(&UsageDailyHeatmapQuery {
created_from_unix_secs: 0,
user_id: Some("user-1".to_string()),
admin_mode: false,
})
.await
.expect("user heatmap should load");
assert_eq!(user.len(), 1);
assert_eq!(user[0].date, "1970-01-02");
assert_eq!(user[0].requests, 5);
assert_eq!(user[0].total_tokens, 18);
assert_eq!(user[0].actual_total_cost_usd, 0.75);
}
#[tokio::test]
async fn sqlite_usage_totals_by_user_ids_reads_imported_user_daily_aggregates() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
sqlx::query(
r#"
INSERT INTO stats_user_daily (
id, user_id, username, "date", total_requests, success_requests, error_requests,
input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
total_cost, created_at, updated_at
) VALUES (
'user-daily-1', 'user-1', 'user one', 86400, 5, 5, 0, 7, 8, 2, 1, 0.75, 1, 1
);
INSERT INTO "usage" (
request_id, id, user_id, api_key_id, provider_name, model, total_tokens,
status, billing_status, created_at_unix_ms, updated_at_unix_secs
) VALUES
('raw-before-cutoff', 'usage-1', 'user-1', 'api-key-1', 'Provider One', 'model-1', 99,
'completed', 'settled', 90000, 90000),
('raw-after-cutoff', 'usage-2', 'user-1', 'api-key-1', 'Provider One', 'model-1', 7,
'completed', 'settled', 172800, 172800);
"#,
)
.execute(&pool)
.await
.expect("usage totals fixtures should seed");
let reader = SqliteUsageReadRepository::new(pool);
let totals = reader
.summarize_usage_totals_by_user_ids(&["user-1".to_string()])
.await
.expect("user totals should load");
assert_eq!(totals.len(), 1);
assert_eq!(totals[0].user_id, "user-1");
assert_eq!(totals[0].request_count, 6);
assert_eq!(totals[0].total_tokens, 25);
}
#[tokio::test]
async fn sqlite_dashboard_daily_stats_reads_imported_daily_aggregates() {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("sqlite pool should connect");
run_migrations(&pool)
.await
.expect("sqlite migrations should run");
sqlx::query(
r#"
INSERT INTO stats_daily (
id, "date", total_requests, success_requests, error_requests,
input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
total_cost, actual_total_cost, is_complete, created_at, updated_at
) VALUES (
'daily-1', 86400, 9, 8, 1, 10, 20, 3, 4, 1.25, 1.0, 1, 1, 1
);
"#,
)
.execute(&pool)
.await
.expect("daily aggregates should seed");
let reader = SqliteUsageReadRepository::new(pool);
let summary = reader
.summarize_dashboard_usage(&UsageDashboardSummaryQuery {
created_from_unix_secs: 0,
created_until_unix_secs: 172800,
user_id: None,
})
.await
.expect("dashboard summary should load");
assert_eq!(summary.total_requests, 9);
assert_eq!(summary.total_tokens, 37);
let rows = reader
.list_dashboard_daily_breakdown(&UsageDashboardDailyBreakdownQuery {
created_from_unix_secs: 0,
created_until_unix_secs: 172800,
tz_offset_minutes: 480,
user_id: None,
})
.await
.expect("dashboard daily breakdown should load");
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].date, "1970-01-02");
assert_eq!(rows[0].model, "aggregate");
assert_eq!(rows[0].requests, 9);
assert_eq!(rows[0].total_tokens, 37);
}
async fn seed_stats_targets(pool: &sqlx::SqlitePool) {
sqlx::query(
r#"
INSERT INTO users (id, auth_source, created_at, updated_at)
VALUES ('user-1', 'local', 1, 1);
INSERT INTO api_keys (id, user_id, key_hash, created_at, updated_at)
VALUES ('api-key-1', 'user-1', 'hash-1', 1, 1);
INSERT INTO providers (id, name, provider_type, created_at, updated_at)
VALUES ('provider-1', 'Provider One', 'openai', 1, 1);
INSERT INTO provider_api_keys (id, provider_id, name, created_at, updated_at)
VALUES ('provider-key-1', 'provider-1', 'Provider Key One', 1, 1);
"#,
)
.execute(pool)
.await
.expect("stats targets should seed");
}
fn sample_usage(
request_id: &str,
status: &str,
billing_status: &str,
updated_at: u64,
) -> UpsertUsageRecord {
UpsertUsageRecord {
request_id: request_id.to_string(),
user_id: Some("user-1".to_string()),
api_key_id: Some("api-key-1".to_string()),
username: Some("legacy-user".to_string()),
api_key_name: Some("legacy-key".to_string()),
provider_name: "Provider One".to_string(),
model: "model-1".to_string(),
target_model: Some("target-model".to_string()),
provider_id: Some("provider-1".to_string()),
provider_endpoint_id: Some("endpoint-1".to_string()),
provider_api_key_id: Some("provider-key-1".to_string()),
request_type: Some("chat".to_string()),
api_format: Some("openai".to_string()),
api_family: Some("chat".to_string()),
endpoint_kind: Some("chat".to_string()),
endpoint_api_format: Some("openai".to_string()),
provider_api_family: Some("chat".to_string()),
provider_endpoint_kind: Some("chat".to_string()),
has_format_conversion: Some(true),
is_stream: Some(false),
input_tokens: Some(2),
output_tokens: Some(3),
total_tokens: None,
cache_creation_input_tokens: None,
cache_creation_ephemeral_5m_input_tokens: Some(0),
cache_creation_ephemeral_1h_input_tokens: Some(0),
cache_read_input_tokens: Some(2),
cache_creation_cost_usd: Some(0.0),
cache_read_cost_usd: Some(0.1),
output_price_per_1m: Some(2.0),
total_cost_usd: Some(0.5),
actual_total_cost_usd: Some(0.4),
status_code: Some(200),
error_message: None,
error_category: None,
response_time_ms: Some(42),
first_byte_time_ms: Some(12),
status: status.to_string(),
billing_status: billing_status.to_string(),
request_headers: None,
request_body: None,
request_body_ref: None,
request_body_state: None,
provider_request_headers: None,
provider_request_body: None,
provider_request_body_ref: None,
provider_request_body_state: None,
response_headers: None,
response_body: None,
response_body_ref: None,
response_body_state: None,
client_response_headers: None,
client_response_body: None,
client_response_body_ref: None,
client_response_body_state: None,
candidate_id: Some("candidate-1".to_string()),
candidate_index: Some(1),
key_name: Some("key-one".to_string()),
planner_kind: Some("default".to_string()),
route_family: Some("chat".to_string()),
route_kind: Some("completion".to_string()),
execution_path: Some("remote".to_string()),
local_execution_runtime_miss_reason: None,
request_metadata: Some(serde_json::json!({
"trace_id": "trace-1",
"upstream_is_stream": true,
})),
finalized_at_unix_secs: Some(updated_at),
created_at_unix_ms: Some(updated_at),
updated_at_unix_secs: updated_at,
}
}