feat: give zhipu, moonshot, anthropic and google a wire of their own
Eight segments now, and each one holds a ThinkingWire instead of two fixed fragments: off, on_base, and the key a tier gets written to. The two fragments could not say "on, at this depth", which is what every current generation model wants. Two deliberate behaviour changes fall out of it. The openai segment stops reporting its shape as unknown — reasoning_effort is OpenAI's own field, not a vendor dialect, so a compatible endpoint behind the gateway takes it. And minimax's on-tier stops carrying a hardcoded medium: that was the library picking a price for the caller, and medium is not even a tier GLM, kimi or deepseek serve. The issue #5 guards stay; their sample moves from "the openai segment" to an explicitly registered unknown one, which is what they always meant to test.
This commit is contained in:
@@ -1,57 +1,99 @@
|
||||
"""providers.py 注册表测试(M1 设计 §7;register_provider 为纯函数,无可变全局)。"""
|
||||
"""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_qwen_profile(self):
|
||||
p = get_provider("qwen")
|
||||
assert p.thinking_on == {"enable_thinking": True}
|
||||
assert p.thinking_off == {"enable_thinking": False}
|
||||
assert p.strip_think_tags is True
|
||||
assert p.supports_native_schema is False
|
||||
def test_all_eight_profiles_registered(self):
|
||||
"""issue #20: 智谱缺段,下游只能把 GLM 挂在 openai 兜底段下再手写 extra_body。"""
|
||||
assert set(DEFAULT_PROFILES) == _EXPECTED_SEGMENTS
|
||||
|
||||
def test_deepseek_profile(self):
|
||||
p = get_provider("deepseek")
|
||||
assert p.thinking_on == {"thinking": {"type": "enabled"}}
|
||||
assert p.thinking_off == {"thinking": {"type": "disabled"}}
|
||||
assert p.strip_think_tags is False
|
||||
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_openai_slots_are_unknown_not_empty(self):
|
||||
"""issue #5: 该段名实践中被复用为任意兼容厂商的兜底(下游把 kimi 挂在此),
|
||||
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"
|
||||
|
||||
故不能下发任何厂商方言参数。None = 形态未知 → 配了 enable_thinking 即报错,
|
||||
而不是空字典那种"注入了个寂寞"的静默失效。
|
||||
"""
|
||||
p = get_provider("openai")
|
||||
assert p.thinking_on is None and p.thinking_off is None
|
||||
assert p.strip_think_tags is False
|
||||
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_minimax_profile_uses_reasoning_effort(self):
|
||||
"""2026-08-02 实测: reasoning_effort 才是 MiniMax 认的开关。"""
|
||||
p = get_provider("minimax")
|
||||
assert p.thinking_off == {"reasoning_effort": "none"}
|
||||
assert p.thinking_on == {"reasoning_effort": "medium"}
|
||||
assert p.strip_think_tags is False
|
||||
def test_openai_family_sends_the_standard_field_only(self):
|
||||
"""gpt/claude/gemini 经网关都吃 OpenAI 标准的 reasoning_effort,不下发厂商方言。"""
|
||||
for name in ("openai", "anthropic", "google", "minimax"):
|
||||
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_unknown_provider_fails_loudly(self):
|
||||
"""消灭子串猜测: 未注册 provider 装配期即报错,不做模糊匹配。"""
|
||||
with pytest.raises(ValueError, match="glm"):
|
||||
get_provider("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` = 不知道怎么表达。"""
|
||||
w = get_provider("minimax").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_on={}, thinking_off={}, strip_think_tags=False)
|
||||
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
|
||||
# 默认表未被污染(无可变全局状态铁律)
|
||||
@@ -59,12 +101,14 @@ class TestPureFunctionRegistration:
|
||||
get_provider("glm")
|
||||
|
||||
def test_register_on_custom_base_and_override(self):
|
||||
custom_qwen = ProviderProfile(
|
||||
name="qwen", thinking_on={"x": 1}, thinking_off={}, strip_think_tags=False
|
||||
custom = ProviderProfile(
|
||||
name="qwen",
|
||||
thinking=ThinkingWire(off={}, on_base={"x": 1}, effort_key=None),
|
||||
strip_think_tags=False,
|
||||
)
|
||||
table = register_provider(custom_qwen, base=DEFAULT_PROFILES)
|
||||
assert get_provider("qwen", registry=table).thinking_on == {"x": 1}
|
||||
assert get_provider("qwen").thinking_on == {"enable_thinking": True}
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user