实现侧: - 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。 - 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。 - 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。 - 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。 - 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。 - 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。 - 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。 - 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。 用例侧: - 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存; - 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
135 lines
5.4 KiB
Python
135 lines
5.4 KiB
Python
"""网关计价的线性公式、缓存口径与费率文件载入合同。"""
|
||
|
||
from decimal import Decimal
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from muse.任务运行.接口 import 模型用量, 模型结果
|
||
from muse.共享.错误 import Muse错误
|
||
from muse.基础设施.模型.计价 import 模型费率, 网关计价, 载入费率文件
|
||
|
||
|
||
def _费率(输入="5", 输出="25", 读取="0.5", 写入="6.25") -> 模型费率:
|
||
return 模型费率(Decimal(输入), Decimal(输出), Decimal(读取), Decimal(写入))
|
||
|
||
|
||
def _结果(实际模型="claude-opus-4-8", 协议="chat-completions", **用量):
|
||
用量.setdefault("输入token", 0)
|
||
用量.setdefault("输出token", 0)
|
||
return 模型结果(
|
||
"completed",
|
||
"{}",
|
||
实际模型,
|
||
模型用量(**用量),
|
||
"resp-1",
|
||
回放协议=协议,
|
||
# 回放内容不属于计价输入,但合同要求终态完整。
|
||
回放内容=(),
|
||
)
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-r2-d9a001",
|
||
environment="离线单元",
|
||
given="chat-completions 用量中 prompt_tokens 含缓存读取",
|
||
when="按费率文件线性计价",
|
||
then=["未缓存输入、缓存读取与输出分别按单价求和并半入进位"],
|
||
contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md",
|
||
)
|
||
def test_含缓存协议按未缓存输入加缓存读取计价__d9a001() -> None:
|
||
计价 = 网关计价("费率-1", {"claude-opus-4-8": _费率()}, "claude-opus-4-8")
|
||
# 输入10(含缓存4)+ 输出3:6×5/1M + 4×0.5/1M + 3×25/1M = 0.000107
|
||
金额 = 计价.金额(_结果(输入token=10, 输出token=3, 缓存读取token=4))
|
||
assert 金额 == Decimal("0.000107")
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-r2-d9a002",
|
||
environment="离线单元",
|
||
given="anthropic 用量把缓存读取与缓存写入单列",
|
||
when="按费率文件线性计价",
|
||
then=["输入不再扣除缓存读取,缓存写入按声明单价计"],
|
||
contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md",
|
||
)
|
||
def test_独立缓存协议输入不含缓存读取__d9a002() -> None:
|
||
计价 = 网关计价("费率-1", {"claude-opus-4-8": _费率()}, "claude-opus-4-8")
|
||
# 输入2 + 读取4 + 写入6 + 输出1 = 10/1M + 2/1M + 37.5/1M + 25/1M = 0.0000745(半入进位)
|
||
金额 = 计价.金额(
|
||
_结果(协议="anthropic", 输入token=2, 输出token=1, 缓存读取token=4, 缓存写入token=6)
|
||
)
|
||
assert 金额 == Decimal("0.000075")
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-r2-d9a003",
|
||
environment="离线单元",
|
||
given="模型结果缺用量或回放协议未登记",
|
||
when="请求计价金额",
|
||
then=["返回 None,不把一个未知成本记成零"],
|
||
contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md",
|
||
)
|
||
def test_缺用量或未知协议返回未知而不推定免费__d9a003() -> None:
|
||
计价 = 网关计价("费率-1", {"claude-opus-4-8": _费率()}, "claude-opus-4-8")
|
||
无用量 = 模型结果("completed", "{}", "claude-opus-4-8", None, None)
|
||
assert 计价.金额(无用量) is None
|
||
assert 计价.金额(_结果(协议="unknown")) is None
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-r2-d9a004",
|
||
environment="离线单元",
|
||
given="响应实际模型不在费率表",
|
||
when="请求计价金额",
|
||
then=["按声明的兜底模型单价计算,不漏计"],
|
||
contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md",
|
||
)
|
||
def test_未列模型按兜底模型高估__d9a004() -> None:
|
||
计价 = 网关计价(
|
||
"费率-1",
|
||
{"claude-opus-4-8": _费率(), "qwen3.8-flash": _费率("0.15", "0.47", "0.016", "0.2")},
|
||
"claude-opus-4-8",
|
||
)
|
||
未知 = 计价.金额(_结果(实际模型="另一个模型", 输入token=1, 输出token=0))
|
||
兜底 = 计价.金额(_结果(实际模型="claude-opus-4-8", 输入token=1, 输出token=0))
|
||
assert 未知 == 兜底 == Decimal("0.000005")
|
||
|
||
|
||
@pytest.mark.case_id(
|
||
"NC-r2-d9a005",
|
||
environment="离线单元",
|
||
given="费率文件缺字段、负价或不存在",
|
||
when="载入部署费率文件",
|
||
then=["结构非法即拒绝,不回退内置费率"],
|
||
contract="docs/系统架构/新版设计/文件设计/工程配置与部署.md",
|
||
)
|
||
def test_费率文件载入并拒绝缺字段或非法金额__d9a005(tmp_path: Path) -> None:
|
||
文件 = tmp_path / "费率.toml"
|
||
文件.write_text(
|
||
'"版本" = "费率-1"\n'
|
||
'"兜底模型" = "模型甲"\n'
|
||
'["费率"."模型甲"]\n'
|
||
'"输入单价" = "1"\n"输出单价" = "2"\n"缓存读取单价" = "0.1"\n"缓存写入单价" = "1"\n',
|
||
encoding="utf-8",
|
||
)
|
||
计价 = 载入费率文件(文件)
|
||
assert 计价.版本 == "费率-1" and 计价.兜底模型 == "模型甲"
|
||
assert 计价.金额(_结果(实际模型="模型甲", 输入token=1)) == Decimal("0.000001")
|
||
|
||
缺字段 = tmp_path / "缺.toml"
|
||
缺字段.write_text('"版本" = "v"\n"兜底模型" = "甲"\n["费率"."甲"]\n"输入单价" = "1"\n')
|
||
with pytest.raises(Muse错误, match="不完整"):
|
||
载入费率文件(缺字段)
|
||
|
||
负价 = tmp_path / "负.toml"
|
||
负价.write_text(
|
||
'"版本" = "v"\n"兜底模型" = "甲"\n'
|
||
'["费率"."甲"]\n"输入单价" = "-1"\n"输出单价" = "2"\n'
|
||
'"缓存读取单价" = "0.1"\n"缓存写入单价" = "1"\n'
|
||
)
|
||
with pytest.raises(Muse错误, match="非负"):
|
||
载入费率文件(负价)
|
||
|
||
with pytest.raises(Muse错误):
|
||
载入费率文件(tmp_path / "不存在.toml")
|