feat: carry the per-call tier down to the transport that must send it
The Transport port took the request apart into five arguments, so a tier placed on ChatRequest could never reach _build_payload: the field was set, read by nobody, and silently ignored - the exact shape of failure that sent downstream to extra_body in the first place. complete() now takes reasoning_effort with no default, matching the TelemetryRecorder convention: a default would turn a missing hand-off into a silent 'no opinion'. All four fakes move with it, since @runtime_checkable checks method names and not signatures. EmbeddingTransport and OcrTransport are deliberately left alone - they have no reasoning semantics - and a test now holds that line. _build_payload drops its inline sugar conversion for effective_effort(), so the guard and the hot path share one judgement, and passes the source's effort_fallback for the same reason.
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
"""ports.py 端口冻结测试(M1 设计 §4): Protocol 结构性检查 + Gate 快照校验。"""
|
||||
|
||||
import inspect
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from polygateway.ports import (
|
||||
CacheBackend,
|
||||
EmbeddingTransport,
|
||||
GateDecision,
|
||||
GateState,
|
||||
GateUpdate,
|
||||
Middleware,
|
||||
OcrTransport,
|
||||
Permit,
|
||||
ProviderGate,
|
||||
RateLimiter,
|
||||
@@ -69,7 +72,7 @@ class _DummyMw:
|
||||
|
||||
|
||||
class _DummyTransport:
|
||||
async def complete(self, *, messages, source, stream, overlay, call_id):
|
||||
async def complete(self, *, messages, source, stream, overlay, call_id, reasoning_effort):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -142,6 +145,34 @@ def test_protocols_are_runtime_checkable(impl, protocol):
|
||||
assert isinstance(impl, protocol)
|
||||
|
||||
|
||||
class TestReasoningTierIsOnlyOnTheChatPort:
|
||||
"""档位属于 chat 端口,且**只属于**它(Task 5b)。
|
||||
|
||||
`@runtime_checkable` 只查方法名不查签名,故协议签名本身必须被显式断言——
|
||||
否则实现漏改一个参数,要到运行期调用才会以 `TypeError` 现形,而那时的现场
|
||||
离根因已经很远。
|
||||
"""
|
||||
|
||||
def test_chat_transport_carries_the_per_call_tier(self):
|
||||
params = inspect.signature(Transport.complete).parameters
|
||||
assert "reasoning_effort" in params
|
||||
# 不给默认值是有意的(与 TelemetryRecorder 同一既有约定): 库外无第三方
|
||||
# 实现者,写全签名成本为零,而默认值会把"漏传"变成静默的"不表态"
|
||||
assert params["reasoning_effort"].default is inspect.Parameter.empty
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("protocol", "method"),
|
||||
[
|
||||
(EmbeddingTransport, "embed"),
|
||||
(OcrTransport, "recognize_text"),
|
||||
(OcrTransport, "parse_layout"),
|
||||
],
|
||||
)
|
||||
def test_other_transports_have_no_reasoning_tier(self, protocol, method):
|
||||
"""embedding 与 OCR 没有推理语义,给它们加档位只会静默无效(issue #4 同款决策)。"""
|
||||
assert "reasoning_effort" not in inspect.signature(getattr(protocol, method)).parameters
|
||||
|
||||
|
||||
class _DummyStatusProvider(_DummyRecorder):
|
||||
@property
|
||||
def telemetry_status(self) -> TelemetryStatus:
|
||||
|
||||
Reference in New Issue
Block a user