fix: close the failure modes review found in the new code

Three of them were the same shape as the bug this branch exists to fix:
something goes wrong, the library swallows it, and the caller is left
with a number that means the opposite of what happened.

The throttle key had no source in it. Five sources on one model is the
normal case here, so the first one to break would warn once and silence
the other four for the life of the process, and the message never said
which gateway to look at.

An unknown verdict in a cached entry threw away the whole response. The
rehydrator tolerates unknown fields but not unknown values of a known
field, so two library versions sharing a Redis would each invalidate
the other's entries: halved hit rate, and the only log line says the
cache rebuild failed. A purely observational field should not be able
to void a response whose content is intact.

Normalising for telemetry now degrades instead of raising, both for a
bare string and for a value outside the domain. Either one used to
reach the same except and cost the whole row, which is exactly how
1.3.0 lost nineteen calls without anyone noticing.
This commit is contained in:
2026-08-26 02:37:24 -04:00
parent c0b544d233
commit 1307a02b92
9 changed files with 270 additions and 30 deletions
+35 -1
View File
@@ -66,6 +66,18 @@ class TestObserveThinking:
observe_thinking(thinking="想了想", reasoning_tokens=0) is ThinkingObservation.OBSERVED
)
@pytest.mark.parametrize("negative", [-1, -205])
def test_negative_token_count_is_not_evidence_of_absence(self, negative):
"""负数是坏数据,不是"上游明确上报未推理"这个最强的正面结论。
当前 transport 已在边界把负数归 `None`,所以这条走不通;但本函数的
docstring 自称"外部输入校验后使用",第二个 transport 直接填该值时,
`> 0 else ABSENT` 会给出一个方向相反的强结论。函数自身必须闭合(P5)。
"""
assert observe_thinking(thinking="", reasoning_tokens=negative) is (
ThinkingObservation.UNKNOWN
)
class TestThinkingObservationEnum:
def test_values_are_stable_strings(self):
@@ -85,7 +97,12 @@ class TestThinkingObservationEnum:
@pytest.mark.parametrize("bogus", ["", "OBSERVED", "yes", "none"])
def test_unknown_strings_are_rejected(bogus):
"""非法值必须抛 ValueError: 缓存回放靠它把污染数据挡成"未命中"(设计 §6)。"""
"""非法值必须抛 ValueError: 缓存回放与遥测归一化都靠它识别域外取值(设计 §6)。
两处接住这个 ValueError 后**降级而非作废**(缓存复活内容 + 记 UNKNOWN、遥测
照常落行),但降级的前提是构造器真的会拒绝——它一旦放行,域外取值就会一路
进到 `LLMResponse` 与遥测列里。
"""
with pytest.raises(ValueError):
ThinkingObservation(bogus)
@@ -239,6 +256,23 @@ class TestReconcileThinking:
assert msg is not None
assert "MiniMax-M3" in msg
def test_off_and_absent_stays_silent(self):
"""要求关闭 + 上游明确上报未推理 = 要求被满足,没有可报的矛盾。
这一格与 `test_off_and_unknown_stays_silent` 的沉默理由**不同**: 那里是
"没有证伪力",这里是"正面证实要求已满足"。两者都必须沉默,漏测哪一格,
把 Phase 2 的判据写成 `is ABSENT` 之类的反向条件都不会被抓住。
"""
assert (
reconcile_thinking(
enable_thinking=False,
observation=ThinkingObservation.ABSENT,
capability=self._CAP,
model="qwen3.7-plus",
)
is None
)
def test_off_and_unknown_stays_silent(self):
"""UNKNOWN 没有证伪力: 拿它报警等于每次关闭调用都喊(M3 关闭档恒落此档)。"""
assert (