fix: keep a low-tier answer out of the cache slot a max-tier one filled
The per-call reasoning tier never reached the cache key, and the model fingerprint could not stand in for it: the fingerprint is computed once at assembly time, so two calls on the same client asking for low and max looked identical to it. Same messages, different tiers, one shared entry -- the verbatim replay of issue #4's five seeds all hitting the same response. Source-level tiers join the fingerprint under the same rule enable_thinking already follows (appended only when the source takes a position), and the filter that decides which sources enter the mark set is widened to match -- without that, a source configured with nothing but REASONING_EFFORT would never reach _fingerprint_mark at all. None (no opinion) and Effort.NONE (asked not to reason) stay distinct keys. Sources that opine on neither keep byte-identical keys and fingerprints, so nothing existing cold-starts.
This commit is contained in:
@@ -401,6 +401,48 @@ class TestModelFingerprint:
|
||||
b = build_model_fingerprint([_source(extra_body={"temperature": 1})])
|
||||
assert a != b
|
||||
|
||||
def test_source_tier_enters_fingerprint(self):
|
||||
"""源级 `reasoning_effort` 改变请求体,就必须改变缓存身份(与 issue #5 同理)。
|
||||
|
||||
本用例同时守着一个易漏点: 只配 `REASONING_EFFORT`、既无 `extra_body` 也无
|
||||
`ENABLE_THINKING` 的源,必须能进入指纹的 marks 集合——否则 `_fingerprint_mark`
|
||||
改了也白改,四个指纹会全部相等。
|
||||
"""
|
||||
from polygateway.client import build_model_fingerprint
|
||||
|
||||
plain = build_model_fingerprint([_source()])
|
||||
low = build_model_fingerprint([_source(reasoning_effort=Effort.LOW)])
|
||||
max_ = build_model_fingerprint([_source(reasoning_effort=Effort.MAX)])
|
||||
off = build_model_fingerprint([_source(reasoning_effort=Effort.NONE)])
|
||||
assert len({plain, low, max_, off}) == 4
|
||||
|
||||
def test_source_tier_is_distinguished_from_the_thinking_sugar(self):
|
||||
"""`reasoning_effort=NONE` 与 `enable_thinking=False` 不得摘要成同一个指纹。
|
||||
|
||||
两者语义等价但取值不同(`"none"` vs `false`),让它们撞车会把"两种写法"
|
||||
变成"一种缓存身份",日后任一侧语义微调都会静默复用另一侧的响应。
|
||||
"""
|
||||
from polygateway.client import build_model_fingerprint
|
||||
|
||||
by_tier = build_model_fingerprint([_source(reasoning_effort=Effort.NONE)])
|
||||
by_sugar = build_model_fingerprint([_source(enable_thinking=False)])
|
||||
assert by_tier != by_sugar
|
||||
|
||||
def test_absent_tier_fingerprint_is_byte_identical_to_before(self):
|
||||
"""不表态档位的存量源不得因本次升级平白冷启动: 字面量逐字相同。
|
||||
|
||||
两条: 纯净源仍是裸 model 合集;只配 extra_body 的源仍是升级前那个摘要。
|
||||
"""
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
from polygateway.client import build_model_fingerprint
|
||||
|
||||
assert build_model_fingerprint([_source()]) == "qwen-max"
|
||||
mark = json.dumps(["qwen-max", {"temperature": 0}], sort_keys=True, ensure_ascii=False)
|
||||
expected = "qwen-max|" + hashlib.sha256(mark.encode("utf-8")).hexdigest()
|
||||
assert build_model_fingerprint([_source(extra_body={"temperature": 0})]) == expected
|
||||
|
||||
|
||||
class TestFactories:
|
||||
def test_from_env_assembles(self):
|
||||
|
||||
Reference in New Issue
Block a user