feat: warn when the capability table and reality disagree
The M3 evidence sat at 08-02 for twenty-three days while nobody could tell whether it still held. A declaration that goes stale in silence is the failure this issue is really about, so the library now compares what it declared against what it just observed and says so when the two part ways. Judgement is separated from logging: reconcile_thinking returns the warning text, so tests assert on the text instead of parsing logs. Two cases that look alike are kept apart — a model whose capability is registered gets a drift warning quoting its evidence, an unregistered one is never told the table said anything, because it never did. False x UNKNOWN stays silent on purpose. UNKNOWN cannot falsify anything, and warning on it would fire on every disabled call M3 makes over the plain endpoint. A warning that always fires is not a warning.
This commit is contained in:
@@ -14,6 +14,7 @@ from polygateway.thinking import (
|
||||
ThinkingCapability,
|
||||
get_capability,
|
||||
observe_thinking,
|
||||
reconcile_thinking,
|
||||
register_capability,
|
||||
resolve_thinking,
|
||||
)
|
||||
@@ -160,3 +161,119 @@ class TestResolveThinking:
|
||||
cap = ThinkingCapability(can_disable=False, evidence="构造")
|
||||
with pytest.raises(ValueError, match="register_provider"):
|
||||
resolve_thinking(get_provider("openai"), cap, False, model="whatever")
|
||||
|
||||
|
||||
class TestReconcileThinking:
|
||||
"""声明 × 观测对账(设计 §5): 矛盾出文案,不表态出 None。
|
||||
|
||||
文案本身是被断言对象——判定与日志分离正是为此: 告警内容可直接比对,不必
|
||||
去解析日志格式。
|
||||
"""
|
||||
|
||||
_CAP = ThinkingCapability(
|
||||
can_disable=True, evidence="2026-08-02 实测 reasoning_effort=none 可关闭"
|
||||
)
|
||||
|
||||
def test_off_but_observed_with_a_registered_capability_blames_the_table(self):
|
||||
"""已登记却实测推理了 = 能力表漂移: 必须附 evidence 与更新指路。"""
|
||||
msg = reconcile_thinking(
|
||||
enable_thinking=False,
|
||||
observation=ThinkingObservation.OBSERVED,
|
||||
capability=self._CAP,
|
||||
model="MiniMax-M3",
|
||||
)
|
||||
assert msg is not None
|
||||
assert "MiniMax-M3" in msg
|
||||
assert "2026-08-02 实测 reasoning_effort=none 可关闭" in msg
|
||||
assert "register_capability" in msg
|
||||
|
||||
def test_off_but_observed_unregistered_never_claims_a_table_entry(self):
|
||||
"""未登记模型没有"能力表声称"这回事——说它就是撒谎。"""
|
||||
msg = reconcile_thinking(
|
||||
enable_thinking=False,
|
||||
observation=ThinkingObservation.OBSERVED,
|
||||
capability=None,
|
||||
model="MiniMax-M9",
|
||||
)
|
||||
assert msg is not None
|
||||
assert "MiniMax-M9" in msg
|
||||
assert "能力表" not in msg
|
||||
assert "register_capability" in msg
|
||||
|
||||
def test_registered_and_unregistered_wordings_differ(self):
|
||||
registered = reconcile_thinking(
|
||||
enable_thinking=False,
|
||||
observation=ThinkingObservation.OBSERVED,
|
||||
capability=self._CAP,
|
||||
model="MiniMax-M3",
|
||||
)
|
||||
unregistered = reconcile_thinking(
|
||||
enable_thinking=False,
|
||||
observation=ThinkingObservation.OBSERVED,
|
||||
capability=None,
|
||||
model="MiniMax-M3",
|
||||
)
|
||||
assert registered != unregistered
|
||||
|
||||
@pytest.mark.parametrize("capability", [None, _CAP])
|
||||
def test_on_but_absent_is_a_contradiction(self, capability):
|
||||
"""上游明确上报未推理: 这是唯一的正面证伪,与能力表登记与否无关。"""
|
||||
msg = reconcile_thinking(
|
||||
enable_thinking=True,
|
||||
observation=ThinkingObservation.ABSENT,
|
||||
capability=capability,
|
||||
model="qwen3.7-plus",
|
||||
)
|
||||
assert msg is not None
|
||||
assert "qwen3.7-plus" in msg
|
||||
|
||||
@pytest.mark.parametrize("capability", [None, _CAP])
|
||||
def test_on_but_unknown_admits_it_cannot_confirm(self, capability):
|
||||
"""issue #17 的诚实版本: 明说"我注入了,但我看不见结果"。"""
|
||||
msg = reconcile_thinking(
|
||||
enable_thinking=True,
|
||||
observation=ThinkingObservation.UNKNOWN,
|
||||
capability=capability,
|
||||
model="MiniMax-M3",
|
||||
)
|
||||
assert msg is not None
|
||||
assert "MiniMax-M3" in msg
|
||||
|
||||
def test_off_and_unknown_stays_silent(self):
|
||||
"""UNKNOWN 没有证伪力: 拿它报警等于每次关闭调用都喊(M3 关闭档恒落此档)。"""
|
||||
assert (
|
||||
reconcile_thinking(
|
||||
enable_thinking=False,
|
||||
observation=ThinkingObservation.UNKNOWN,
|
||||
capability=self._CAP,
|
||||
model="MiniMax-M3",
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"observation",
|
||||
[ThinkingObservation.OBSERVED, ThinkingObservation.ABSENT, ThinkingObservation.UNKNOWN],
|
||||
)
|
||||
def test_no_request_no_grievance(self, observation):
|
||||
"""调用方不表态,就无从谈"违背"。"""
|
||||
assert (
|
||||
reconcile_thinking(
|
||||
enable_thinking=None,
|
||||
observation=observation,
|
||||
capability=self._CAP,
|
||||
model="MiniMax-M3",
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
def test_on_and_observed_is_exactly_what_was_asked_for(self):
|
||||
assert (
|
||||
reconcile_thinking(
|
||||
enable_thinking=True,
|
||||
observation=ThinkingObservation.OBSERVED,
|
||||
capability=self._CAP,
|
||||
model="MiniMax-M3",
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user