feat: let a source name its reasoning tier, and say so when it contradicts itself
This commit is contained in:
@@ -9,6 +9,7 @@ from loguru import logger
|
||||
from polygateway.client import GatewayClient
|
||||
from polygateway.config import EmbeddingSettings, GatewaySettings, OcrSettings
|
||||
from polygateway.providers import ProviderProfile, ThinkingWire, register_provider
|
||||
from polygateway.types import Effort, SourceConfig
|
||||
|
||||
_BASE_ENV = {
|
||||
"LLM__QWEN__1__BASE_URL": "https://gw-a.example/v1",
|
||||
@@ -133,6 +134,99 @@ class TestExtraBodyParsing:
|
||||
GatewaySettings.from_env("LLM", env=env)
|
||||
|
||||
|
||||
class TestReasoningEffortParsing:
|
||||
"""源级推理档位两个键的 env 解析(issue #20 Task 4)。"""
|
||||
|
||||
def test_effort_key_parsed(self):
|
||||
env = _env(**{"LLM__QWEN__1__REASONING_EFFORT": "low"})
|
||||
s = GatewaySettings.from_env("LLM", env=env)
|
||||
assert s.sources[0].reasoning_effort is Effort.LOW
|
||||
|
||||
def test_absent_keys_keep_the_source_silent(self):
|
||||
"""未配置 = 不表态,与 `Effort.NONE`(要求不推理)是两回事;映射默认关闭。"""
|
||||
src = GatewaySettings.from_env("LLM", env=_env()).sources[0]
|
||||
assert src.reasoning_effort is None
|
||||
assert src.effort_fallback == "error"
|
||||
|
||||
def test_invalid_effort_lists_vocabulary(self):
|
||||
"""写错档位的人要的是"那该填什么",故报错必须把八档全摆出来。"""
|
||||
env = _env(**{"LLM__QWEN__1__REASONING_EFFORT": "lowest"})
|
||||
with pytest.raises(ValueError) as exc:
|
||||
GatewaySettings.from_env("LLM", env=env)
|
||||
message = str(exc.value)
|
||||
assert "REASONING_EFFORT" in message
|
||||
assert all(tier.value in message for tier in Effort)
|
||||
|
||||
def test_effort_fallback_parsed(self):
|
||||
env = _env(**{"LLM__QWEN__1__EFFORT_FALLBACK": "nearest"})
|
||||
s = GatewaySettings.from_env("LLM", env=env)
|
||||
assert s.sources[0].effort_fallback == "nearest"
|
||||
|
||||
def test_invalid_effort_fallback_rejected(self):
|
||||
"""`resolve_thinking` 对未知 fallback 值是 fail-closed,不会替配置兜错。"""
|
||||
env = _env(**{"LLM__QWEN__1__EFFORT_FALLBACK": "closest"})
|
||||
with pytest.raises(ValueError) as exc:
|
||||
GatewaySettings.from_env("LLM", env=env)
|
||||
message = str(exc.value)
|
||||
assert "effort_fallback" in message
|
||||
assert "nearest" in message and "error" in message
|
||||
|
||||
|
||||
class TestThinkingFlagContradiction:
|
||||
"""`enable_thinking` 与 `reasoning_effort` 说的是同一件事(设计 §4.2 语法糖)。
|
||||
|
||||
矛盾时报错而非「后者赢」: 两个字段表达同一件事时,矛盾是配置错误,
|
||||
静默取其一等于替下游猜它想要哪个。
|
||||
"""
|
||||
|
||||
def _source(self, enable_thinking, effort):
|
||||
env = _env(
|
||||
**{
|
||||
"LLM__QWEN__1__ENABLE_THINKING": enable_thinking,
|
||||
"LLM__QWEN__1__REASONING_EFFORT": effort,
|
||||
}
|
||||
)
|
||||
return GatewaySettings.from_env("LLM", env=env).sources[0]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("enable_thinking", "effort"),
|
||||
[("true", "none"), ("false", "low"), ("false", "auto"), ("false", "max")],
|
||||
)
|
||||
def test_contradictory_thinking_flags_rejected(self, enable_thinking, effort):
|
||||
with pytest.raises(ValueError) as exc:
|
||||
self._source(enable_thinking, effort)
|
||||
message = str(exc.value)
|
||||
assert "enable_thinking" in message and "reasoning_effort" in message
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("enable_thinking", "effort"),
|
||||
[("false", "none"), ("true", "auto"), ("true", "low")],
|
||||
)
|
||||
def test_consistent_flags_allowed(self, enable_thinking, effort):
|
||||
"""语义一致就放行: `False`+`none` 与 `True`+某个开启档都只是说了两遍。"""
|
||||
src = self._source(enable_thinking, effort)
|
||||
assert src.enable_thinking is (enable_thinking == "true")
|
||||
assert src.reasoning_effort is Effort(effort)
|
||||
|
||||
def test_one_sided_declaration_never_trips_the_guard(self):
|
||||
"""只配一个键是常态(存量源全是这样),不得被矛盾守卫误伤。"""
|
||||
assert self._source("true", None).reasoning_effort is None
|
||||
assert self._source(None, "high").enable_thinking is None
|
||||
|
||||
def test_contradiction_guarded_on_direct_construction(self):
|
||||
"""守卫挂在构造期而非 env 解析处: 构造函数全量注入那条装配路同样过闸。"""
|
||||
base = SourceConfig(
|
||||
name="s1",
|
||||
provider="qwen",
|
||||
base_url="https://gw.example/v1",
|
||||
api_key="sk-x",
|
||||
model="qwen-max",
|
||||
timeout_s=60.0,
|
||||
)
|
||||
with pytest.raises(ValueError, match="reasoning_effort"):
|
||||
dataclasses.replace(base, enable_thinking=True, reasoning_effort=Effort.NONE)
|
||||
|
||||
|
||||
class TestResilienceKeys:
|
||||
def test_flat_legacy_keys(self):
|
||||
s = GatewaySettings.from_env("LLM", env=_env())
|
||||
|
||||
Reference in New Issue
Block a user