feat(billing): 引入 billing v3 settlement snapshot 及 usage_billing_facts 视图

- 新增迁移 20260424000000:为 usage_settlement_snapshots 添加 settlement_snapshot、
  billing_dimensions、billing_input/output/cache tokens、billing_total_cost_usd 等列,
  并创建 usage_billing_facts 视图(从 settlement snapshot 覆盖原始 usage token/cost 字段)
- event_enrichment:构建结构化 settlement_snapshot(含 pricing_snapshot、billing_plan_snapshot、
  resolved_dimensions、cost_breakdown 等),写入 request_metadata
- pricing:新增 pricing_source() 方法区分 provider_override / global_default / unpriced
- sql.rs:settlement snapshot 写入新列;用量汇总查询改用 usage_billing_facts 视图
- maintenance/runtime:所有统计查询的 FROM usage 替换为 FROM usage_billing_facts
- admin observability:在 settlement 响应中暴露 settlement_snapshot / billing_dimensions,
  并从 metadata 中剥离对应字段
- request_metadata:允许 settlement_snapshot / billing_dimensions 字段传播
- stream execution:在首个数据帧到达时记录 TTFB,补全流式请求的 streaming 事件
This commit is contained in:
fawney19
2026-04-24 17:52:33 +08:00
parent fb46fcd80f
commit f695238e8a
11 changed files with 1970 additions and 197 deletions

View File

@@ -299,6 +299,9 @@ fn admin_usage_strip_settlement_metadata(metadata: &mut serde_json::Map<String,
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");
@@ -353,6 +356,36 @@ fn admin_usage_body_capture_json(item: &StoredRequestUsageAudit) -> Value {
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()
@@ -2339,6 +2372,17 @@ mod tests {
"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"
}
},
"billing_snapshot": {
"resolved_variables": {
"output_price_per_1m": 11.0
@@ -2369,6 +2413,18 @@ mod tests {
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"]["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);
@@ -2381,6 +2437,9 @@ mod tests {
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());