"""把决策解释契约接到一个测试替身上。 库不带这个接缝的实现——带了就等于替某一家定了动作语言。所以这里造一个最小的替身,它存在的 唯一目的是让套件的每一条用例至少被真的求值一次:一条引用了记录工厂里不存在的方法的用例, 只有在被执行的时候才会红。 **这个替身住在 `tests/` 里,不进 wheel,任何下游都拿不到它。** 「库不带默认实现」那条禁的是 `src/` 下出现一个能用的实现——下游装了包就拿得到,就会有人直接用,于是动作语言被库替它定了。 判据是下游拿不拿得到,不是代码库里有没有一个能跑的实现 (`research-wiki/design/0014-contract-suite-distribution.md` 决策六)。 `contract` 这个标记打在本文件上,不打在套件里,理由见 `test_run_stores.py`。 """ from collections.abc import Mapping from dataclasses import dataclass import pytest from polyloop.ports import Action, InvalidDecision, ParsedReply from polyloop.testing import DecisionParserContract, RecordFactory from polyloop.types import ModelReply #: 这个替身认得的全部动作语言:正文以它开头就是一个动作,剩下的部分是动作本身。 _ACTION_PREFIX = "DO " pytestmark = pytest.mark.contract class _PrefixDecisionParser: """认一种一行前缀的动作语言,别的一律解释不出动作。 **两支都要走得到**,这是契约对样本的要求在替身这一侧的对应:一个「什么都解释得出来」的 实现会让无效决策那一支变成死代码,而套件照样绿,绿的含义从「这一支对」变成「这一支没验」。 它满足 `polyloop.ports.DecisionParser`,但不显式继承那个 Protocol:结构化子类型不需要继承。 """ def parse(self, reply: ModelReply) -> ParsedReply: if reply.content.startswith(_ACTION_PREFIX): return ParsedReply( history_text=reply.content, decision=Action(text=reply.content[len(_ACTION_PREFIX) :], tool_call=None), ) return ParsedReply( history_text=reply.content, decision=InvalidDecision( explanation=f"这段回复没有以 {_ACTION_PREFIX!r} 开头,解释不出动作" ), ) def parameters(self) -> Mapping[str, str]: return {"prefix": _ACTION_PREFIX} @dataclass(frozen=True, slots=True, kw_only=True) class _ReplySamples: yields_an_action: ModelReply yields_invalid: ModelReply class TestPrefixDecisionParser(DecisionParserContract): @pytest.fixture def decision_parser(self) -> _PrefixDecisionParser: return _PrefixDecisionParser() @pytest.fixture def reply_samples(self, records: RecordFactory) -> _ReplySamples: return _ReplySamples( yields_an_action=records.reply(content=f"{_ACTION_PREFIX}做点事"), yields_invalid=records.reply(content="我先想想。"), )