mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
feat: 缓存计费细分、能力匹配优化、用户模型调用计数
1. 缓存创建 tokens 区分 5min/1h TTL,支持按缓存时长差异化计费 - Usage 表新增 cache_creation_input_tokens_5m/1h 字段 - Claude handler 解析新格式 (ephemeral_5m/1h, claude_cache_creation_5/1h) - 计费规则支持 cache_ttl_pricing 覆盖 cache_creation 价格 2. 能力匹配机制优化 - COMPATIBLE 能力不再硬过滤,改为排序阶段通过 capability_miss_count 优先级处理 - cache_1h 改为 COMPATIBLE + REQUEST_PARAM(自动检测请求体中的 ttl=1h) - gemini_files 改为 EXCLUSIVE + REQUEST_PARAM(自动检测 fileData.fileUri) - 移除前端模型偏好/能力配置 UI(不再需要用户手动配置) 3. 新增用户-模型维度调用次数计数器 (UserModelUsageCount) - 原子递增,避免从 Usage 表聚合查询 - 前端模型目录和用户可用模型列表展示调用次数 4. 其他改进 - global_model_id 改为必填(NOT NULL),清理孤立模型 - 模型映射对话框支持从上游获取模型列表并分组折叠 - 端点测试不再依赖端点启用状态 - 异步任务页面对普通用户隐藏用户信息列 - Dashboard 响应式布局断点调整 (sm -> lg) - 号池管理仅展示已启用号池的提供商
This commit is contained in:
@@ -142,6 +142,76 @@ class TestDefaultBillingRuleGenerator:
|
||||
assert result.status == "complete"
|
||||
assert abs(float(result.cost) - 0.0005) < 1e-9
|
||||
|
||||
def test_default_rule_cache_ttl_pricing_overrides_cache_creation_price(self) -> None:
|
||||
global_model = GlobalModel(
|
||||
name="ttl-creation-model",
|
||||
display_name="TTL Creation Model",
|
||||
is_active=True,
|
||||
default_price_per_request=0.0,
|
||||
default_tiered_pricing={
|
||||
"tiers": [
|
||||
{
|
||||
"up_to": None,
|
||||
"input_price_per_1m": 3.0,
|
||||
"output_price_per_1m": 15.0,
|
||||
"cache_creation_price_per_1m": 3.75,
|
||||
"cache_ttl_pricing": [
|
||||
{"ttl_minutes": 5, "cache_creation_price_per_1m": 3.75},
|
||||
{"ttl_minutes": 60, "cache_creation_price_per_1m": 6.0},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
rule = DefaultBillingRuleGenerator.generate_for_model(
|
||||
global_model=global_model,
|
||||
model=None,
|
||||
task_type="chat",
|
||||
)
|
||||
|
||||
engine = FormulaEngine()
|
||||
|
||||
# TTL=5: cache_creation_price_per_1m=3.75
|
||||
# 1_000_000 * 3.75 / 1M = 3.75
|
||||
result_5m = engine.evaluate(
|
||||
expression=rule.expression,
|
||||
variables=rule.variables,
|
||||
dimensions={
|
||||
"input_tokens": 0,
|
||||
"output_tokens": 0,
|
||||
"cache_creation_tokens": 1_000_000,
|
||||
"cache_read_tokens": 0,
|
||||
"cache_ttl_minutes": 5,
|
||||
"request_count": 1,
|
||||
"total_input_context": 0,
|
||||
},
|
||||
dimension_mappings=rule.dimension_mappings,
|
||||
strict_mode=True,
|
||||
)
|
||||
assert result_5m.status == "complete"
|
||||
assert abs(float(result_5m.cost) - 3.75) < 1e-9
|
||||
|
||||
# TTL=60: cache_creation_price_per_1m=6.0
|
||||
# 1_000_000 * 6.0 / 1M = 6.0
|
||||
result_1h = engine.evaluate(
|
||||
expression=rule.expression,
|
||||
variables=rule.variables,
|
||||
dimensions={
|
||||
"input_tokens": 0,
|
||||
"output_tokens": 0,
|
||||
"cache_creation_tokens": 1_000_000,
|
||||
"cache_read_tokens": 0,
|
||||
"cache_ttl_minutes": 60,
|
||||
"request_count": 1,
|
||||
"total_input_context": 0,
|
||||
},
|
||||
dimension_mappings=rule.dimension_mappings,
|
||||
strict_mode=True,
|
||||
)
|
||||
assert result_1h.status == "complete"
|
||||
assert abs(float(result_1h.cost) - 6.0) < 1e-9
|
||||
|
||||
|
||||
class TestBillingRuleServiceDefaultFallback:
|
||||
def test_find_rule_returns_default_for_chat_when_no_db_rule(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user