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:
@@ -546,6 +546,56 @@ class TestThinkingObservationVerdict:
|
||||
assert result.thinking_observation is ThinkingObservation.ABSENT
|
||||
|
||||
|
||||
class TestThinkingReconciliation:
|
||||
"""对账告警按 (model, direction) 节流(设计 §5)。
|
||||
|
||||
节流键必须含方向: 同一模型的开、关两档是两个独立的矛盾,合并键会让先出现
|
||||
的那一档把另一档永久静音。
|
||||
"""
|
||||
|
||||
def _handler(self, request):
|
||||
payload = json.loads(request.content)
|
||||
if payload.get("reasoning_effort") == "none":
|
||||
# 关闭档却回了推理正文 → OBSERVED,与"要求关闭"矛盾
|
||||
return _sse_stream(
|
||||
_chunk(reasoning="偷偷想了"), _chunk(content="ok"), _chunk(usage=_USAGE)
|
||||
)
|
||||
# 开启档却零信号 → UNKNOWN,无法确认是否生效(M3 实测形态)
|
||||
return _sse_stream(_chunk(content="ok"), _chunk(usage=_USAGE))
|
||||
|
||||
def _minimax(self, enable_thinking):
|
||||
return _source(
|
||||
name="mm", provider="minimax", model="MiniMax-M3", enable_thinking=enable_thinking
|
||||
)
|
||||
|
||||
async def test_same_model_and_direction_warns_only_once(self):
|
||||
transport = _transport_for(self._handler)
|
||||
source = self._minimax(False)
|
||||
messages: list[str] = []
|
||||
sink_id = logger.add(messages.append, level="WARNING")
|
||||
try:
|
||||
await _complete(transport, source)
|
||||
await _complete(transport, source)
|
||||
finally:
|
||||
logger.remove(sink_id)
|
||||
hits = [m for m in messages if "MiniMax-M3" in m]
|
||||
assert len(hits) == 1, f"同一 (model, direction) 应只告警一次,实得 {len(hits)} 次"
|
||||
|
||||
async def test_switching_direction_earns_a_second_warning(self):
|
||||
transport = _transport_for(self._handler)
|
||||
messages: list[str] = []
|
||||
sink_id = logger.add(messages.append, level="WARNING")
|
||||
try:
|
||||
await _complete(transport, self._minimax(False))
|
||||
await _complete(transport, self._minimax(False))
|
||||
await _complete(transport, self._minimax(True))
|
||||
await _complete(transport, self._minimax(True))
|
||||
finally:
|
||||
logger.remove(sink_id)
|
||||
hits = [m for m in messages if "MiniMax-M3" in m]
|
||||
assert len(hits) == 2, f"两个方向各应告警一次,实得 {len(hits)} 次"
|
||||
|
||||
|
||||
class TestNonStreamFastPath:
|
||||
async def test_non_stream_parses_message(self):
|
||||
def handler(request):
|
||||
|
||||
Reference in New Issue
Block a user