mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
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:
@@ -33,6 +33,10 @@ pub struct UsageEventData {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub target_model: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub model_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub global_model_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider_endpoint_id: Option<String>,
|
||||
|
||||
@@ -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"}
|
||||
})
|
||||
);
|
||||
|
||||
@@ -74,6 +74,8 @@ pub struct LifecycleUsageSeed {
|
||||
pub provider_name: String,
|
||||
pub model: String,
|
||||
pub target_model: Option<String>,
|
||||
pub model_id: Option<String>,
|
||||
pub global_model_id: Option<String>,
|
||||
pub provider_id: Option<String>,
|
||||
pub provider_endpoint_id: Option<String>,
|
||||
pub provider_api_key_id: Option<String>,
|
||||
@@ -110,6 +112,8 @@ pub struct TerminalUsageContextSeed {
|
||||
pub provider_name: String,
|
||||
pub model: String,
|
||||
pub target_model: Option<String>,
|
||||
pub model_id: Option<String>,
|
||||
pub global_model_id: Option<String>,
|
||||
pub provider_id: Option<String>,
|
||||
pub provider_endpoint_id: Option<String>,
|
||||
pub provider_api_key_id: Option<String>,
|
||||
@@ -168,6 +172,8 @@ pub struct TerminalUsageSeed {
|
||||
pub provider_name: String,
|
||||
pub model: String,
|
||||
pub target_model: Option<String>,
|
||||
pub model_id: Option<String>,
|
||||
pub global_model_id: Option<String>,
|
||||
pub provider_id: Option<String>,
|
||||
pub provider_endpoint_id: Option<String>,
|
||||
pub provider_api_key_id: Option<String>,
|
||||
@@ -262,6 +268,8 @@ pub fn build_lifecycle_usage_seed(
|
||||
provider_name,
|
||||
model,
|
||||
target_model: context_string(context, "mapped_model"),
|
||||
model_id: context_string(context, "model_id"),
|
||||
global_model_id: context_string(context, "global_model_id"),
|
||||
provider_id: empty_to_none(
|
||||
context_string(context, "provider_id")
|
||||
.or_else(|| non_empty_str(Some(plan.provider_id.as_str()))),
|
||||
@@ -486,6 +494,8 @@ fn build_terminal_usage_event_from_seed_impl(
|
||||
provider_name,
|
||||
model,
|
||||
target_model,
|
||||
model_id,
|
||||
global_model_id,
|
||||
provider_id,
|
||||
provider_endpoint_id,
|
||||
provider_api_key_id,
|
||||
@@ -548,6 +558,8 @@ fn build_terminal_usage_event_from_seed_impl(
|
||||
provider_name,
|
||||
model,
|
||||
target_model,
|
||||
model_id,
|
||||
global_model_id,
|
||||
provider_id,
|
||||
provider_endpoint_id,
|
||||
provider_api_key_id,
|
||||
@@ -657,6 +669,8 @@ pub fn build_terminal_usage_context_seed(
|
||||
.or_else(|| non_empty_str(plan.model_name.as_deref()))
|
||||
.unwrap_or_else(|| "unknown".to_string()),
|
||||
target_model: context_string(context, "mapped_model"),
|
||||
model_id: context_string(context, "model_id"),
|
||||
global_model_id: context_string(context, "global_model_id"),
|
||||
provider_id: context_string(context, "provider_id")
|
||||
.or_else(|| non_empty_str(Some(plan.provider_id.as_str()))),
|
||||
provider_endpoint_id: context_string(context, "endpoint_id")
|
||||
@@ -813,6 +827,8 @@ pub fn build_sync_terminal_usage_seed(
|
||||
provider_name: context_seed.provider_name,
|
||||
model: context_seed.model,
|
||||
target_model: context_seed.target_model,
|
||||
model_id: context_seed.model_id,
|
||||
global_model_id: context_seed.global_model_id,
|
||||
provider_id: context_seed.provider_id,
|
||||
provider_endpoint_id: context_seed.provider_endpoint_id,
|
||||
provider_api_key_id: context_seed.provider_api_key_id,
|
||||
@@ -881,6 +897,8 @@ pub fn build_stream_terminal_usage_seed(
|
||||
provider_name: context_seed.provider_name,
|
||||
model: context_seed.model,
|
||||
target_model: context_seed.target_model,
|
||||
model_id: context_seed.model_id,
|
||||
global_model_id: context_seed.global_model_id,
|
||||
provider_id: context_seed.provider_id,
|
||||
provider_endpoint_id: context_seed.provider_endpoint_id,
|
||||
provider_api_key_id: context_seed.provider_api_key_id,
|
||||
@@ -1242,6 +1260,8 @@ fn build_usage_event_data_seed_with_detail(
|
||||
provider_name,
|
||||
model,
|
||||
target_model: context_string(context, "mapped_model"),
|
||||
model_id: context_string(context, "model_id"),
|
||||
global_model_id: context_string(context, "global_model_id"),
|
||||
provider_id: context_string(context, "provider_id")
|
||||
.or_else(|| non_empty_str(Some(plan.provider_id.as_str()))),
|
||||
provider_endpoint_id: context_string(context, "endpoint_id")
|
||||
@@ -3523,6 +3543,8 @@ mod tests {
|
||||
provider_name: "OpenAI".to_string(),
|
||||
model: "gpt-5".to_string(),
|
||||
target_model: None,
|
||||
model_id: None,
|
||||
global_model_id: None,
|
||||
provider_id: Some("provider-1".to_string()),
|
||||
provider_endpoint_id: Some("endpoint-1".to_string()),
|
||||
provider_api_key_id: Some("upstream-key-1".to_string()),
|
||||
@@ -3646,6 +3668,8 @@ mod tests {
|
||||
provider_name: "OpenAI".to_string(),
|
||||
model: "gpt-5".to_string(),
|
||||
target_model: None,
|
||||
model_id: None,
|
||||
global_model_id: None,
|
||||
provider_id: Some("provider-1".to_string()),
|
||||
provider_endpoint_id: Some("endpoint-1".to_string()),
|
||||
provider_api_key_id: Some("upstream-key-1".to_string()),
|
||||
|
||||
Reference in New Issue
Block a user