mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30: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:
@@ -24,6 +24,7 @@ impl BillingModelPricingSnapshot {
|
||||
pub fn effective_tiered_pricing(&self) -> Option<&Value> {
|
||||
self.model_tiered_pricing
|
||||
.as_ref()
|
||||
.filter(|value| has_tiered_pricing_tiers(value))
|
||||
.or(self.default_tiered_pricing.as_ref())
|
||||
}
|
||||
|
||||
@@ -58,6 +59,67 @@ impl BillingModelPricingSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
fn has_tiered_pricing_tiers(value: &Value) -> bool {
|
||||
value
|
||||
.get("tiers")
|
||||
.and_then(Value::as_array)
|
||||
.is_some_and(|tiers| !tiers.is_empty())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::BillingModelPricingSnapshot;
|
||||
|
||||
fn snapshot(
|
||||
model_tiered_pricing: Option<serde_json::Value>,
|
||||
default_tiered_pricing: Option<serde_json::Value>,
|
||||
) -> BillingModelPricingSnapshot {
|
||||
BillingModelPricingSnapshot {
|
||||
provider_id: "provider-1".to_string(),
|
||||
provider_billing_type: None,
|
||||
provider_api_key_id: None,
|
||||
provider_api_key_rate_multipliers: None,
|
||||
provider_api_key_cache_ttl_minutes: None,
|
||||
global_model_id: "global-model-1".to_string(),
|
||||
global_model_name: "gpt-5".to_string(),
|
||||
global_model_config: None,
|
||||
default_price_per_request: None,
|
||||
default_tiered_pricing,
|
||||
model_id: Some("model-1".to_string()),
|
||||
model_provider_model_name: Some("gpt-5-upstream".to_string()),
|
||||
model_config: None,
|
||||
model_price_per_request: None,
|
||||
model_tiered_pricing,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_provider_tiered_pricing_inherits_global_default() {
|
||||
let default_pricing =
|
||||
json!({"tiers":[{"up_to":null,"input_price_per_1m":3.0,"output_price_per_1m":15.0}]});
|
||||
let pricing = snapshot(Some(json!({})), Some(default_pricing.clone()));
|
||||
|
||||
assert_eq!(pricing.effective_tiered_pricing(), Some(&default_pricing));
|
||||
|
||||
let pricing = snapshot(Some(json!({"tiers": []})), Some(default_pricing.clone()));
|
||||
|
||||
assert_eq!(pricing.effective_tiered_pricing(), Some(&default_pricing));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn populated_provider_tiered_pricing_overrides_global_default() {
|
||||
let provider_pricing =
|
||||
json!({"tiers":[{"up_to":null,"input_price_per_1m":1.0,"output_price_per_1m":2.0}]});
|
||||
let default_pricing =
|
||||
json!({"tiers":[{"up_to":null,"input_price_per_1m":3.0,"output_price_per_1m":15.0}]});
|
||||
let pricing = snapshot(Some(provider_pricing.clone()), Some(default_pricing));
|
||||
|
||||
assert_eq!(pricing.effective_tiered_pricing(), Some(&provider_pricing));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct BillingUsageInput {
|
||||
pub task_type: String,
|
||||
|
||||
Reference in New Issue
Block a user