mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
fix(usage): 流式终端 usage 以更完整值为准,Codex CLI 显式选择 response 解析器
- 新增 StandardizedUsage::signal_score/is_more_complete_than/choose_more_complete,流式合并与终端落库均按信号完整度择优 - OpenAI Chat/CLI 解析器支持仅 usage 的终结 chunk 与 response.completed usage - Codex provider 注入 provider_stream_event_api_format=openai:cli,解析器选择改由 report_context 显式决定 - usage_mapper 扩展嵌套 response/message/item 兼容 Claude message_start/message_delta 及 Gemini stream chunks - usage SQL upsert 在终态(completed/failed/cancelled)时按 GREATEST 写入 token/费用镜像列
This commit is contained in:
@@ -6323,18 +6323,28 @@ ORDER BY "usage".user_id ASC
|
||||
.bind(&usage.provider_endpoint_kind)
|
||||
.bind(usage.has_format_conversion)
|
||||
.bind(usage.is_stream)
|
||||
.bind(None::<i32>)
|
||||
.bind(None::<i32>)
|
||||
.bind(None::<i32>)
|
||||
.bind(None::<i32>)
|
||||
.bind(None::<i32>)
|
||||
.bind(None::<i32>)
|
||||
.bind(None::<i32>)
|
||||
.bind(None::<f64>)
|
||||
.bind(None::<f64>)
|
||||
.bind(None::<f64>)
|
||||
.bind(None::<f64>)
|
||||
.bind(usage.input_tokens.map(to_i32).transpose()?)
|
||||
.bind(usage.output_tokens.map(to_i32).transpose()?)
|
||||
.bind(usage.total_tokens.map(to_i32).transpose()?)
|
||||
.bind(usage.cache_creation_input_tokens.map(to_i32).transpose()?)
|
||||
.bind(
|
||||
usage
|
||||
.cache_creation_ephemeral_5m_input_tokens
|
||||
.map(to_i32)
|
||||
.transpose()?,
|
||||
)
|
||||
.bind(
|
||||
usage
|
||||
.cache_creation_ephemeral_1h_input_tokens
|
||||
.map(to_i32)
|
||||
.transpose()?,
|
||||
)
|
||||
.bind(usage.cache_read_input_tokens.map(to_i32).transpose()?)
|
||||
.bind(usage.cache_creation_cost_usd)
|
||||
.bind(usage.cache_read_cost_usd)
|
||||
.bind(None::<f64>)
|
||||
.bind(usage.total_cost_usd)
|
||||
.bind(usage.actual_total_cost_usd)
|
||||
.bind(usage.status_code.map(i32::from))
|
||||
.bind(&usage.error_message)
|
||||
.bind(&usage.error_category)
|
||||
|
||||
@@ -135,18 +135,18 @@ DO UPDATE SET
|
||||
provider_endpoint_kind = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.provider_endpoint_kind, "usage".provider_endpoint_kind) ELSE "usage".provider_endpoint_kind END,
|
||||
has_format_conversion = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.has_format_conversion, "usage".has_format_conversion) ELSE "usage".has_format_conversion END,
|
||||
is_stream = CASE WHEN "usage".billing_status = 'pending' THEN COALESCE(EXCLUDED.is_stream, "usage".is_stream) ELSE "usage".is_stream END,
|
||||
input_tokens = "usage".input_tokens,
|
||||
output_tokens = "usage".output_tokens,
|
||||
total_tokens = "usage".total_tokens,
|
||||
cache_creation_input_tokens = "usage".cache_creation_input_tokens,
|
||||
cache_creation_input_tokens_5m = "usage".cache_creation_input_tokens_5m,
|
||||
cache_creation_input_tokens_1h = "usage".cache_creation_input_tokens_1h,
|
||||
cache_read_input_tokens = "usage".cache_read_input_tokens,
|
||||
cache_creation_cost_usd = "usage".cache_creation_cost_usd,
|
||||
cache_read_cost_usd = "usage".cache_read_cost_usd,
|
||||
input_tokens = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".input_tokens, EXCLUDED.input_tokens) ELSE "usage".input_tokens END,
|
||||
output_tokens = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".output_tokens, EXCLUDED.output_tokens) ELSE "usage".output_tokens END,
|
||||
total_tokens = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".total_tokens, EXCLUDED.total_tokens) ELSE "usage".total_tokens END,
|
||||
cache_creation_input_tokens = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".cache_creation_input_tokens, EXCLUDED.cache_creation_input_tokens) ELSE "usage".cache_creation_input_tokens END,
|
||||
cache_creation_input_tokens_5m = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".cache_creation_input_tokens_5m, EXCLUDED.cache_creation_input_tokens_5m) ELSE "usage".cache_creation_input_tokens_5m END,
|
||||
cache_creation_input_tokens_1h = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".cache_creation_input_tokens_1h, EXCLUDED.cache_creation_input_tokens_1h) ELSE "usage".cache_creation_input_tokens_1h END,
|
||||
cache_read_input_tokens = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".cache_read_input_tokens, EXCLUDED.cache_read_input_tokens) ELSE "usage".cache_read_input_tokens END,
|
||||
cache_creation_cost_usd = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".cache_creation_cost_usd, EXCLUDED.cache_creation_cost_usd) ELSE "usage".cache_creation_cost_usd END,
|
||||
cache_read_cost_usd = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".cache_read_cost_usd, EXCLUDED.cache_read_cost_usd) ELSE "usage".cache_read_cost_usd END,
|
||||
output_price_per_1m = NULL,
|
||||
total_cost_usd = "usage".total_cost_usd,
|
||||
actual_total_cost_usd = "usage".actual_total_cost_usd,
|
||||
total_cost_usd = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".total_cost_usd, EXCLUDED.total_cost_usd) ELSE "usage".total_cost_usd END,
|
||||
actual_total_cost_usd = CASE WHEN "usage".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST("usage".actual_total_cost_usd, EXCLUDED.actual_total_cost_usd) ELSE "usage".actual_total_cost_usd END,
|
||||
status_code = CASE WHEN "usage".billing_status = 'pending' THEN CASE
|
||||
WHEN "usage".status = 'streaming' AND EXCLUDED.status = 'pending' THEN "usage".status_code
|
||||
WHEN EXCLUDED.status IN ('pending', 'streaming', 'completed', 'cancelled') AND EXCLUDED.status_code IS NULL THEN NULL
|
||||
|
||||
@@ -585,27 +585,46 @@ fn usage_sql_writes_usage_settlement_pricing_snapshots() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_does_not_update_deprecated_billing_mirror_columns() {
|
||||
fn usage_sql_updates_usage_mirror_columns_from_terminal_events_only() {
|
||||
for assignment in [
|
||||
"input_tokens = \"usage\".input_tokens",
|
||||
"output_tokens = \"usage\".output_tokens",
|
||||
"total_tokens = \"usage\".total_tokens",
|
||||
"cache_creation_input_tokens = \"usage\".cache_creation_input_tokens",
|
||||
"cache_creation_input_tokens_5m = \"usage\".cache_creation_input_tokens_5m",
|
||||
"cache_creation_input_tokens_1h = \"usage\".cache_creation_input_tokens_1h",
|
||||
"cache_read_input_tokens = \"usage\".cache_read_input_tokens",
|
||||
"cache_creation_cost_usd = \"usage\".cache_creation_cost_usd",
|
||||
"cache_read_cost_usd = \"usage\".cache_read_cost_usd",
|
||||
"total_cost_usd = \"usage\".total_cost_usd",
|
||||
"actual_total_cost_usd = \"usage\".actual_total_cost_usd",
|
||||
"input_tokens = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".input_tokens, EXCLUDED.input_tokens) ELSE \"usage\".input_tokens END",
|
||||
"output_tokens = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".output_tokens, EXCLUDED.output_tokens) ELSE \"usage\".output_tokens END",
|
||||
"total_tokens = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".total_tokens, EXCLUDED.total_tokens) ELSE \"usage\".total_tokens END",
|
||||
"cache_creation_input_tokens = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".cache_creation_input_tokens, EXCLUDED.cache_creation_input_tokens) ELSE \"usage\".cache_creation_input_tokens END",
|
||||
"cache_creation_input_tokens_5m = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".cache_creation_input_tokens_5m, EXCLUDED.cache_creation_input_tokens_5m) ELSE \"usage\".cache_creation_input_tokens_5m END",
|
||||
"cache_creation_input_tokens_1h = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".cache_creation_input_tokens_1h, EXCLUDED.cache_creation_input_tokens_1h) ELSE \"usage\".cache_creation_input_tokens_1h END",
|
||||
"cache_read_input_tokens = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".cache_read_input_tokens, EXCLUDED.cache_read_input_tokens) ELSE \"usage\".cache_read_input_tokens END",
|
||||
"cache_creation_cost_usd = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".cache_creation_cost_usd, EXCLUDED.cache_creation_cost_usd) ELSE \"usage\".cache_creation_cost_usd END",
|
||||
"cache_read_cost_usd = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".cache_read_cost_usd, EXCLUDED.cache_read_cost_usd) ELSE \"usage\".cache_read_cost_usd END",
|
||||
"total_cost_usd = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".total_cost_usd, EXCLUDED.total_cost_usd) ELSE \"usage\".total_cost_usd END",
|
||||
"actual_total_cost_usd = CASE WHEN \"usage\".billing_status = 'pending' AND EXCLUDED.status IN ('completed', 'failed', 'cancelled') THEN GREATEST(\"usage\".actual_total_cost_usd, EXCLUDED.actual_total_cost_usd) ELSE \"usage\".actual_total_cost_usd END",
|
||||
] {
|
||||
assert!(
|
||||
super::UPSERT_SQL.contains(assignment),
|
||||
"missing deprecated mirror no-op assignment: {assignment}"
|
||||
"missing terminal mirror assignment: {assignment}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_sql_binds_usage_mirror_values_for_terminal_upserts() {
|
||||
let source = include_str!("mod.rs");
|
||||
for binding in [
|
||||
".bind(usage.input_tokens.map(to_i32).transpose()?)",
|
||||
".bind(usage.output_tokens.map(to_i32).transpose()?)",
|
||||
".bind(usage.total_tokens.map(to_i32).transpose()?)",
|
||||
".bind(usage.cache_creation_input_tokens.map(to_i32).transpose()?)",
|
||||
".bind(usage.cache_read_input_tokens.map(to_i32).transpose()?)",
|
||||
".bind(usage.cache_creation_cost_usd)",
|
||||
".bind(usage.cache_read_cost_usd)",
|
||||
".bind(usage.total_cost_usd)",
|
||||
".bind(usage.actual_total_cost_usd)",
|
||||
] {
|
||||
assert!(
|
||||
source.contains(binding),
|
||||
"missing usage upsert bind: {binding}"
|
||||
);
|
||||
}
|
||||
assert!(!super::UPSERT_SQL.contains("EXCLUDED.input_tokens"));
|
||||
assert!(!super::UPSERT_SQL.contains("EXCLUDED.total_cost_usd"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user