131 lines
5.5 KiB
Python
131 lines
5.5 KiB
Python
"""providers.py 注册表测试(M1 设计 §7;register_provider 为纯函数,无可变全局)。
|
|
|
|
2026-09-04 起 profile 存的是 `ThinkingWire`(off / on_base / effort_key)而非两个
|
|
固定片段——档位型模型(GLM-5.3、kimi-k3、deepseek-v4…)的"开"档需要附一个档位值,
|
|
两个固定片段表达不了。
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from polygateway.providers import (
|
|
DEFAULT_PROFILES,
|
|
ProviderProfile,
|
|
ThinkingWire,
|
|
get_provider,
|
|
register_provider,
|
|
)
|
|
|
|
_EXPECTED_SEGMENTS = frozenset(
|
|
{"qwen", "deepseek", "zhipu", "moonshot", "minimax", "openai", "anthropic", "google"}
|
|
)
|
|
|
|
|
|
class TestDefaultProfiles:
|
|
def test_all_eight_profiles_registered(self):
|
|
"""issue #20: 智谱缺段,下游只能把 GLM 挂在 openai 兜底段下再手写 extra_body。"""
|
|
assert set(DEFAULT_PROFILES) == _EXPECTED_SEGMENTS
|
|
|
|
def test_qwen_is_a_switch_with_no_tiers(self):
|
|
w = get_provider("qwen").thinking
|
|
assert w.on_base == {"enable_thinking": True}
|
|
assert w.off == {"enable_thinking": False}
|
|
assert w.effort_key is None # 百炼靠 thinking_budget 调深度,不是档位
|
|
assert get_provider("qwen").strip_think_tags is True
|
|
|
|
def test_deepseek_carries_both_switch_and_tier(self):
|
|
w = get_provider("deepseek").thinking
|
|
assert w.on_base == {"thinking": {"type": "enabled"}}
|
|
assert w.off == {"thinking": {"type": "disabled"}}
|
|
assert w.effort_key == "reasoning_effort"
|
|
|
|
def test_zhipu_matches_the_vendor_migration_note(self):
|
|
"""智谱官方: thinking.type=enabled + reasoning_effort 才是 GLM-5.3 的正确形态。"""
|
|
w = get_provider("zhipu").thinking
|
|
assert w.on_base == {"thinking": {"type": "enabled"}}
|
|
assert w.off == {"thinking": {"type": "disabled"}}
|
|
assert w.effort_key == "reasoning_effort"
|
|
|
|
def test_openai_family_sends_the_standard_field_only(self):
|
|
"""gpt/claude/gemini 经网关都吃 OpenAI 标准的 reasoning_effort,不下发厂商方言。
|
|
|
|
minimax 2026-09-05 起不在本组: 它的形态相同,但"开"这一档被迫带上了一个
|
|
档位值(见 `test_minimax_on_tier_carries_a_tier_value`)。
|
|
"""
|
|
for name in ("openai", "anthropic", "google"):
|
|
w = get_provider(name).thinking
|
|
assert w.on_base == {}, name
|
|
assert w.off == {"reasoning_effort": "none"}, name
|
|
assert w.effort_key == "reasoning_effort", name
|
|
|
|
def test_minimax_on_does_not_select_a_tier(self):
|
|
"""形态不代替模型能力,也不替调用者选择付费档位。"""
|
|
w = get_provider("minimax").thinking
|
|
assert w.on_base == {}
|
|
assert w.off == {"reasoning_effort": "none"}
|
|
assert w.effort_key == "reasoning_effort"
|
|
|
|
def test_unknown_provider_fails_loudly(self):
|
|
"""消灭子串猜测: 未注册 provider 装配期即报错,不做模糊匹配。"""
|
|
with pytest.raises(ValueError, match="glm"):
|
|
get_provider("glm") # 段名是 zhipu,不是 glm
|
|
with pytest.raises(ValueError):
|
|
get_provider("qwen2") # 子串相似也不放行
|
|
|
|
def test_error_lists_every_registered_segment(self):
|
|
with pytest.raises(ValueError, match="zhipu"):
|
|
get_provider("nope")
|
|
|
|
|
|
class TestWireNoneSemantics:
|
|
"""三个 `None` 语义互不重叠(issue #5 的成果,不可退回成"注入了个寂寞")。"""
|
|
|
|
def test_on_base_none_means_shape_unknown(self):
|
|
w = ThinkingWire(off=None, on_base=None, effort_key=None)
|
|
assert w.on_base is None
|
|
|
|
def test_off_none_means_no_off_shape(self):
|
|
"""有开启形态但没有关闭形态,与"整个形态未知"是两回事。"""
|
|
w = ThinkingWire(off=None, on_base={"x": 1}, effort_key=None)
|
|
assert w.on_base is not None and w.off is None
|
|
|
|
def test_effort_key_none_means_switch_only(self):
|
|
"""qwen 是这一档: 能开能关,但没有档位可谈。"""
|
|
assert get_provider("qwen").thinking.effort_key is None
|
|
|
|
def test_empty_on_base_is_not_none(self):
|
|
"""`{}` = 已知无需注入任何参数即处于该档;`None` = 不知道怎么表达。
|
|
|
|
样本 2026-09-05 由 minimax 换成 openai: minimax 的 `on_base` 因 issue #21
|
|
改回带值,不再是空片段;openai 段是现存 `{}` 语义的代表。
|
|
"""
|
|
w = get_provider("openai").thinking
|
|
assert w.on_base == {} and w.on_base is not None
|
|
|
|
|
|
class TestPureFunctionRegistration:
|
|
def test_register_returns_new_mapping(self):
|
|
glm = ProviderProfile(
|
|
name="glm",
|
|
thinking=ThinkingWire(off={}, on_base={}, effort_key=None),
|
|
strip_think_tags=False,
|
|
)
|
|
table = register_provider(glm)
|
|
assert get_provider("glm", registry=table) is glm
|
|
# 默认表未被污染(无可变全局状态铁律)
|
|
with pytest.raises(ValueError):
|
|
get_provider("glm")
|
|
|
|
def test_register_on_custom_base_and_override(self):
|
|
custom = ProviderProfile(
|
|
name="qwen",
|
|
thinking=ThinkingWire(off={}, on_base={"x": 1}, effort_key=None),
|
|
strip_think_tags=False,
|
|
)
|
|
table = register_provider(custom, base=DEFAULT_PROFILES)
|
|
assert get_provider("qwen", registry=table).thinking.on_base == {"x": 1}
|
|
assert get_provider("qwen").thinking.on_base == {"enable_thinking": True}
|
|
|
|
def test_default_profiles_mapping_is_read_only(self):
|
|
with pytest.raises(TypeError):
|
|
DEFAULT_PROFILES["hack"] = None # type: ignore[index]
|