feat(billing): 引入 model_id 精确计费查找路径,传播模型 ID 至用量事件与 report context

- UsageEventData 新增 model_id / global_model_id 字段,write.rs seed 结构体同步补充
- report_context 新增 model_id / global_model_id / global_model_name,各 payload 构建时从 candidate 传入
- event_enrichment 优先按 model_id 精确查找计费上下文,回退为按名称多轮查找(保留 NoRule 结果降级逻辑)
- BillingReadRepository 新增 find_model_context_by_model_id,memory/sql 分别实现;SQL 查询重构支持按 provider_model_name 和 mappings 匹配并按优先级排序
- pricing.rs 修复:model_tiered_pricing 为空 tiers 时回退到 default_tiered_pricing
- request_metadata 允许字段列表补充 model_id / global_model_id / global_model_name
- admin usage 路由信息输出及脱敏字段列表同步新增三个 model 相关字段
This commit is contained in:
fawney19
2026-04-24 14:40:28 +08:00
parent f3c9835759
commit 780f09c1a2
20 changed files with 665 additions and 27 deletions
@@ -75,6 +75,9 @@ fn copy_allowed_metadata_fields(source: &Map<String, Value>, target: &mut Map<St
copy_non_null_value(source, target, "billing_snapshot");
copy_non_empty_string(source, target, "billing_snapshot_schema_version");
copy_non_empty_string(source, target, "billing_snapshot_status");
copy_non_empty_string(source, target, "model_id");
copy_non_empty_string(source, target, "global_model_id");
copy_non_empty_string(source, target, "global_model_name");
copy_non_null_value(source, target, "dimensions");
copy_non_null_value(source, target, "billing_rule_snapshot");
copy_non_null_value(source, target, "scheduling_audit");
@@ -97,6 +100,9 @@ fn move_allowed_metadata_fields(mut source: Map<String, Value>, target: &mut Map
remove_non_null_value(&mut source, target, "billing_snapshot");
remove_non_empty_string(&mut source, target, "billing_snapshot_schema_version");
remove_non_empty_string(&mut source, target, "billing_snapshot_status");
remove_non_empty_string(&mut source, target, "model_id");
remove_non_empty_string(&mut source, target, "global_model_id");
remove_non_empty_string(&mut source, target, "global_model_name");
remove_non_null_value(&mut source, target, "dimensions");
remove_non_null_value(&mut source, target, "billing_rule_snapshot");
remove_non_null_value(&mut source, target, "scheduling_audit");
@@ -378,6 +384,9 @@ mod tests {
"billing_snapshot": {"status": "complete"},
"billing_snapshot_schema_version": "2.0",
"billing_snapshot_status": "complete",
"model_id": "model-1",
"global_model_id": "global-model-1",
"global_model_name": "gpt-5",
"dimensions": {"total_input_context": 10},
"rate_multiplier": 1.25,
"is_free_tier": false,
@@ -405,6 +414,9 @@ mod tests {
"billing_snapshot": {"status": "complete"},
"billing_snapshot_schema_version": "2.0",
"billing_snapshot_status": "complete",
"model_id": "model-1",
"global_model_id": "global-model-1",
"global_model_name": "gpt-5",
"dimensions": {"total_input_context": 10},
"rate_multiplier": 1.25,
"is_free_tier": false,
@@ -455,6 +467,9 @@ mod tests {
"client_requested_stream": false,
"upstream_is_stream": true,
"provider_id": "provider-1",
"model_id": "model-1",
"global_model_id": "global-model-1",
"global_model_name": "gpt-5",
"billing_snapshot": {"status": "complete"}
})
.as_object()
@@ -468,6 +483,9 @@ mod tests {
json!({
"client_requested_stream": false,
"upstream_is_stream": true,
"model_id": "model-1",
"global_model_id": "global-model-1",
"global_model_name": "gpt-5",
"billing_snapshot": {"status": "complete"}
})
);