"""实际宿主封装配合显式传输替身;不作为真实模型调用证据。""" import asyncio import pytest from muse.任务运行.接口 import 模型协议错误, 模型请求 from muse.基础设施.宿主.直接调用 import 直接宿主 class 合成传输: 协议 = "anthropic" def __init__(self): self.收到 = [] def 准备(self, 请求, 总期限秒): self.请求, self.期限 = 请求, 总期限秒 return self async def 流(self): 请求, 总期限秒 = self.请求, self.期限 self.收到.append((请求, 总期限秒)) yield { "type": "message_start", "message": {"id": "m1", "model": "claude-opus-4-8", "usage": {"input_tokens": 1}}, } yield { "type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": "{}"}, } yield { "type": "message_delta", "delta": {"stop_reason": "end_turn"}, "usage": {"output_tokens": 1}, } yield {"type": "message_stop"} @pytest.mark.case_id( "NC-host-thinking-request", environment="离线协议,显式合成传输", given="合成Messages传输与固定Opus请求", when="实际直接宿主封装请求并消费事件", then=["模型、期限、adaptive thinking与effort按请求传递,终态保留实际模型"], contract="docs/系统架构/新版设计/接口契约/任务工具与事件.md", ) def test_直接宿主传递显式模型期限与推理等级__a64001() -> None: 传输 = 合成传输() 请求 = 模型请求( "call-1", "configured", "claude-opus-4-8[1M]", "系统说明", "合成输入", {"type": "object"}, 1024, 30, thinking="high", ) 结果 = asyncio.run(直接宿主(传输).调用(请求)) 已发, 秒 = 传输.收到[0] assert 已发["model"] == 请求.model and 秒 == 30 assert 已发["thinking"] == {"type": "adaptive"} assert 已发["output_config"] == {"effort": "high"} assert 结果.状态 == "completed" and 结果.实际模型 == "claude-opus-4-8" @pytest.mark.case_id( "NC-host-tools-required", environment="离线协议,显式合成传输", given="无工具直接宿主收到要求工具的请求", when="调用宿主入口", then=["发出任何请求前明确拒绝,不静默去掉工具"], contract="docs/系统架构/新版设计/接口契约/任务工具与事件.md", ) def test_无工具直接宿主拒绝请求工具而不静默降级__a64002() -> None: 传输 = 合成传输() 请求 = 模型请求( "call-1", "configured", "claude-opus-4-8[1M]", "系统说明", "合成输入", {"type": "object"}, 1024, 30, 工具=({"name": "read"},), ) with pytest.raises(模型协议错误): asyncio.run(直接宿主(传输).调用(请求)) assert not 传输.收到 @pytest.mark.case_id( "NC-pi-event-settlement", environment="离线纯归一;事件夹具来自真实SDK合成提供方,不计PG或外部模型", given="实际Pi SDK合成工具循环事件及S02核对结果", when="归一工具身份并检查会话收尾", then=["保留工具身份,agent_end不能代替settled,结果沿用已核对回合"], contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md", ) def test_Pi真实事件保留工具身份并以已结算回合收尾__a64003(): import json from pathlib import Path from muse.任务运行.接口 import 模型用量, 模型结果 from muse.基础设施.宿主.Pi事件归一 import 归一事件, 检查会话结束 事件 = json.loads((Path(__file__).parents[1] / "夹具/Pi合成事件.json").read_text()) 工具 = [v for e in 事件 for v in 归一事件(e) if v["type"] == "tool.completed"] assert 工具 == [ {"type": "tool.completed", "call_id": "probe-call", "tool": "schema_read", "failed": False} ] 回合 = 模型结果( "completed", '{"text":"合成工具已返回"}', "verified-upstream-model", 模型用量(1, 1) ) assert 检查会话结束(事件, 0, 回合) is 回合 with pytest.raises(模型协议错误, match="结束"): 检查会话结束(事件[:-1], 0, 回合) @pytest.mark.case_id( "NC-pi-no-unregistered-continuation", environment="离线纯归一;事件夹具来自真实SDK合成提供方,不计PG或外部模型", given="自动续跑事件或宿主改写的最终消息", when="检查角色会话结束", then=["未登记续跑和内容不符明确失败"], contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md", ) def test_Pi自动续跑或改写结果不能冒充完成__a64004(): from muse.任务运行.接口 import 模型结果 from muse.基础设施.宿主.Pi事件归一 import 检查会话结束 回合 = 模型结果("completed", "原始结果", "verified-upstream-model", None) with pytest.raises(模型协议错误, match="未登记"): 检查会话结束([{"type": "auto_retry_start"}], 0, 回合) 事件 = [ { "type": "message_end", "message": { "role": "assistant", "stopReason": "stop", "content": [{"type": "text", "text": "另一个结果"}], }, }, {"type": "agent_settled"}, ] with pytest.raises(模型协议错误, match="回合"): 检查会话结束(事件, 0, 回合)