diff --git a/scripts/provider-update-guide.zh-CN.md b/scripts/provider-update-guide.zh-CN.md index 3e9a66de..5040a93e 100644 --- a/scripts/provider-update-guide.zh-CN.md +++ b/scripts/provider-update-guide.zh-CN.md @@ -392,3 +392,15 @@ git diff HEAD -- iac-code-rs | Kimi 官方 | 无新模型;K3 仍为旗舰。公告:`kimi-k2.5` 与 `moonshot-v1` 系列不再对新注册用户开放,全平台 2026-08-31 下线;存量用户在此之前仍可调用,目录暂不删除。DashScope/火山托管的同名模型不受该公告影响 | 无代码变更(目录保留 `kimi-k2.5`) | [Model List](https://platform.kimi.ai/docs/models) | | MiniMax / OpenAI / Anthropic | MiniMax 无新文本模型(M3 仍为旗舰;H3 为视频模型,不在范围内)。OpenAI 无新增通用模型:GPT-5.6 Sol「Ultrafast」是服务层级而非新 model ID(2026-08-13,受限预览),`gpt-5.6-cyber` 仅限 Daybreak Red 审批客户,均不进入目录。Anthropic 无新 GA 模型:`claude-mythos-5` 属 Project Glasswing 邀请制,不进入目录 | 无代码变更 | [OpenAI Changelog](https://developers.openai.com/api/docs/changelog)、[Anthropic Models](https://platform.claude.com/docs/en/about-claude/models/overview)、[MiniMax Models](https://platform.minimax.io/docs/guides/models-intro) | | 降级链实现 | `_get_fallback_model` 放宽为「仅要求降级目标仍在 provider 目录内」:源模型可能是已移出可选目录但仍可调用的遗留 ID(如 `qwen3.8-max-preview`),其保存的配置仍应享受降级保护;空目录 provider(compatible/azure/ollama 等)不受影响 | `manager.py` 及 `tests/providers/test_manager.py` | 本次官方模型证据与仓库内降级规则 | + +### 2026-08-27 增量证据附录 + +本节记录 2026-08-27 在上述基线之后重新核验的模型变化;若与更早的证据附录冲突,以本节为准。 + +| 范围 | 2026-08-27 核验结论 | 代码/测试落点 | 官方证据 | +| --- | --- | --- | --- | +| DashScope / Qwen | 按完整百炼 Chat Completions 模型目录重新核验,而不是只看 2026-08-18 之后的增量:新增 `qwen3.8-flash`、优速模式 `qwen3.8-max-prime`、开源服务 `qwen3.8-2.4t-a95b` / `qwen3.8-27b`,并补齐 `qwen3.6-35b-a3b` / `qwen3.6-27b`。官方没有 `qwen3.8-35b`:Qwen3.8 当前开放权重只有 2.4T-A95B 与 27B,35B 的精确 ID 属于 Qwen3.6。Qwen3.8 两款开源服务均为 1M context / 131,072 最大输出并支持显式缓存;Qwen3.6 两款为 262,144 / 65,536 且不支持显式缓存。上述开源模型为可启停的混合思考模型,使用 `enable_thinking` 与可选 `thinking_budget`,不套用仅 Max 支持的 `reasoning_effort`。`qwen3.8-flash` 同时进入标准百炼与 Token Plan 个人版,并支持视觉输入和 1M context;其最大输出未在当前公开页给出,运行时继续使用保守默认值 | `registry.py`、`thinking.py`、`dashscope_provider.py`、`manager.py`、`context_manager.py`、遥测及对应测试 | [模型上下架与更新](https://help.aliyun.com/zh/model-studio/newly-released-models)、[模型价格](https://help.aliyun.com/zh/model-studio/model-pricing)、[Qwen3.8-2.4T-A95B](https://help.aliyun.com/zh/model-studio/qwen3-8-2-4t-a95b)、[Qwen3.8-27B](https://help.aliyun.com/zh/model-studio/qwen3-8-27b)、[Qwen3.6-35B-A3B](https://help.aliyun.com/zh/model-studio/qwen3-6-35b-a3b)、[Qwen3.6-27B](https://help.aliyun.com/zh/model-studio/qwen3-6-27b)、[深度思考](https://help.aliyun.com/zh/model-studio/deep-thinking)、[优速模式](https://help.aliyun.com/zh/model-studio/fast-mode)、[Token Plan 个人版](https://help.aliyun.com/zh/model-studio/token-plan-personal-overview) | +| DashScope / 第三方直供 | 补入百炼自部署 `kimi-k3`(与月之暗面直供的 `kimi/kimi-k3` 是不同服务边界)、小米直供 `xiaomi/mimo-v2.5-pro` 和阶跃星辰直供 `stepfun/step-3.7-flash`。百炼 `kimi-k3` 为仅思考模型,`enable_thinking` 不可关闭且 `preserve_thinking` 默认开启,支持 Base64 图片;MiMo 为默认开启但可关闭的混合思考纯文本模型,不支持 effort/budget/preserve;Stepfun 为多模态混合思考模型,支持 `low/medium/high` effort,不支持 budget/preserve。三个命名空间/重名模型均按百炼兼容端点协议单独登记,避免复用厂商直连协议 | `config.py`、`registry.py`、`thinking.py`、`dashscope_provider.py`、`manager.py`、`context_manager.py`、遥测及对应测试 | [百炼 Kimi](https://help.aliyun.com/zh/model-studio/kimi-api)、[kimi-k3 模型信息](https://help.aliyun.com/zh/model-studio/aliyun-kimi-k3)、[MiMo](https://help.aliyun.com/zh/model-studio/mimo)、[MiMo-V2.5-Pro 模型信息](https://help.aliyun.com/zh/model-studio/mimo-v2-5-pro)、[Stepfun](https://help.aliyun.com/zh/model-studio/stepfun)、[Step 3.7 Flash 模型信息](https://help.aliyun.com/zh/model-studio/step-3-7-flash) | +| Z.AI / GLM 官方 | `glm-5.3` 已同时开放标准 Model API 与 Coding Plan,不再只限 Coding Plan,因此进入中国站、国际站的标准 provider 并成为默认;2026-08-26 新发布 `glm-5.3-flash`,同时开放 Model API 与 Coding Plan,支持 1M context、原生图片/视频/文件输入、Function Calling 与结构化输出。其文本参数与 GLM-5.3 一致:思考始终开启,`thinking.type` 仅接受 `enabled`,`reasoning_effort` 支持 `low/high/max`(推荐 `max`);当前附件适配器使用官方明确支持的 Base64 Data URL,因此开放图片输入标记。关闭思考时继续按 GLM-5.3 迁移规则降级为 `enabled + low` | `config.py`、`registry.py`、`thinking.py`、`zhipu_provider.py`、`manager.py`、`context_manager.py`、遥测及对应测试 | [GLM-5.3(CN)](https://docs.bigmodel.cn/cn/guide/models/text/glm-5.3)、[GLM-5.3(Intl)](https://docs.z.ai/guides/llm/glm-5.3)、[GLM-5.3-Flash](https://docs.z.ai/guides/vlm/glm-5.3-flash)、[发布公告](https://z.ai/blog/glm-5.3-flash) | +| DashScope 智谱直供 | 百炼华北2(北京)新增智谱原厂直供 `ZHIPU/GLM-5.3`,精确 model ID 包含大写命名空间;仅文本输入,context 为 1,048,576、最大输出为 131,072,支持 Function Calling 与隐式缓存。模型信息页把结构化输出标为支持,但端点级调用页标为不支持,本次不依赖该能力并以端点级限制为准。模型始终思考,`enable_thinking` 必须保持 `true`,`reasoning_effort` 接受 `low/high/max`;关闭请求降级为 `enable_thinking=true + low`。该模型未进入 Token Plan 目录,且按百炼兼容端点协议发送参数,不能复用 Z.AI 直连的 `thinking.type` wire format | `config.py`、`registry.py`、`thinking.py`、`dashscope_provider.py`、`manager.py`、`context_manager.py`、遥测及对应测试 | [GLM-智谱直供调用](https://help.aliyun.com/zh/model-studio/glm-zhipu)、[ZHIPU/GLM-5.3 模型信息](https://help.aliyun.com/zh/model-studio/glm-5-3-by-zhipu)、[Token Plan 个人版目录](https://help.aliyun.com/zh/model-studio/token-plan-personal-overview) | +| 其他厂商直连 provider | 重新检查 OpenAI、Anthropic、Gemini、DeepSeek、Kimi 与 MiniMax 的官方模型目录和更新日志后,没有发现 2026-08-18 基线之后适合当前 Chat Completions/Message 直连接入路径的新 GA 文本模型;上表的 `kimi-k3`、MiMo 和 Stepfun 变化只属于百炼服务。Kimi K2.5 的 2026-08-31 下线日期尚未到达,继续保留以兼容存量用户 | 无代码变更 | [OpenAI Models](https://developers.openai.com/api/docs/models)、[Anthropic Models](https://platform.claude.com/docs/en/about-claude/models/overview)、[Gemini Release Notes](https://ai.google.dev/gemini-api/docs/changelog)、[DeepSeek Models](https://api-docs.deepseek.com/quick_start/pricing)、[Kimi Models](https://platform.kimi.ai/docs/models)、[MiniMax Models](https://platform.minimax.io/docs/guides/models-intro) | diff --git a/src/iac_code/config.py b/src/iac_code/config.py index 3ba6a718..7383edcd 100644 --- a/src/iac_code/config.py +++ b/src/iac_code/config.py @@ -124,13 +124,13 @@ def _build_canonical_names() -> tuple[str, ...]: "glm-5.2-fast-preview": "dashscope", "kimi/kimi-k3": "dashscope", "minimax/minimax-m3": "dashscope", + "zhipu/glm-5.3": "dashscope", + "xiaomi/mimo-v2.5-pro": "dashscope", + "stepfun/step-3.7-flash": "dashscope", # Dated DeepSeek snapshots only exist on Bailian; the official DeepSeek # endpoint keeps the undated model IDs. "deepseek-v4-pro-0813": "dashscope", "deepseek-v4-flash-0731": "dashscope", - # GLM-5.3 is currently only served through the GLM Coding Plan endpoints; - # the standard ZhiPu model API is not live for it yet. - "glm-5.3": "zhipu_cn_codingplan", } _MODEL_PREFIX_TO_PROVIDER: tuple[tuple[str, str], ...] = ( diff --git a/src/iac_code/providers/dashscope_provider.py b/src/iac_code/providers/dashscope_provider.py index 4b6bb487..543a5eda 100644 --- a/src/iac_code/providers/dashscope_provider.py +++ b/src/iac_code/providers/dashscope_provider.py @@ -18,6 +18,9 @@ # Ref: https://help.aliyun.com/zh/model-studio/context-cache _EXPLICIT_CACHE_MODEL_PREFIXES: tuple[str, ...] = ( "qwen3.8-max", + "qwen3.8-flash", + "qwen3.8-2.4t-a95b", + "qwen3.8-27b", "qwen3.7-max", "qwen3.7-plus", "qwen3-coder-plus", @@ -41,6 +44,7 @@ "qwen3.6-flash", "kimi-k2.7-code", "kimi-k2.6", + "kimi-k3", "kimi/kimi-k3", "kimi/kimi-k2.7-code", "kimi/kimi-k2.6", @@ -160,9 +164,18 @@ def _build_thinking_kwargs(self) -> dict[str, Any]: elif effort not in {None, "auto"} and spec.default_effort is not None: kwargs["reasoning_effort"] = spec.default_effort.value return kwargs + if self._model == "kimi-k3": + # Bailian-hosted K3 is always-on and defaults to preserved + # reasoning. Keep both flags explicit when callers ask to disable. + return {"extra_body": {"enable_thinking": True, "preserve_thinking": True}} disabled_by_effort = effort in _DISABLE_THINKING_EFFORTS and effort not in allowed if self._thinking_disabled() or disabled_by_effort: if not spec.supports_disable: + if spec.uses_reasoning_effort_param and "low" in allowed: + return { + "extra_body": {"enable_thinking": True}, + "reasoning_effort": "low", + } return self._preserve_thinking_kwargs() return {"extra_body": {"enable_thinking": False}} extra_body: dict[str, Any] = {"enable_thinking": True} diff --git a/src/iac_code/providers/manager.py b/src/iac_code/providers/manager.py index 0f6d4e3d..41b49ee3 100644 --- a/src/iac_code/providers/manager.py +++ b/src/iac_code/providers/manager.py @@ -346,17 +346,25 @@ def _error_event_from_exception(exc: BaseException) -> ErrorEvent: "gpt-5.5": "gpt-5.4", "gpt-5.4": "gpt-5.4-mini", "qwen3.8-max": "qwen3.7-plus", + "qwen3.8-max-prime": "qwen3.8-max", + "qwen3.8-2.4t-a95b": "qwen3.8-max", + "qwen3.8-27b": "qwen3.8-flash", "qwen3.8-max-preview": "qwen3.8-max", "qwen3.7-max": "qwen3.7-plus", "qwen3.7-flash": "qwen3.6-flash", + "qwen3.6-35b-a3b": "qwen3.6-flash", + "qwen3.6-27b": "qwen3.6-flash", "kimi/kimi-k3": "kimi-k2.7-code", "kimi-k3": "kimi-k2.7-code", - "glm-5.3": "glm-5.2", + "glm-5.3": "glm-5.3-flash", + "ZHIPU/GLM-5.3": "glm-5.2", "glm-5.2-fast-preview": "glm-5.2", "glm-5.2": "glm-5.1", "deepseek-v4-pro": "deepseek-v4-flash", "deepseek-v4-pro-0813": "deepseek-v4-pro", "deepseek-v4-flash-0731": "deepseek-v4-flash", + "xiaomi/mimo-v2.5-pro": "qwen3.8-flash", + "stepfun/step-3.7-flash": "qwen3.8-flash", } _MODEL_REFUSAL_FALLBACK_MAP = { @@ -365,8 +373,14 @@ def _error_event_from_exception(exc: BaseException) -> ErrorEvent: } _PROVIDER_MODEL_FALLBACK_MAP = { - "dashscope": {"qwen3.6-plus": "qwen3.6-flash"}, - "dashscope_token_plan": {"qwen3.6-plus": "qwen3.6-flash"}, + "dashscope": { + "qwen3.8-flash": "qwen3.7-flash", + "qwen3.6-plus": "qwen3.6-flash", + }, + "dashscope_token_plan": { + "qwen3.8-flash": "qwen3.6-flash", + "qwen3.6-plus": "qwen3.6-flash", + }, "aliyun_codingplan": {"qwen3.6-plus": "qwen3.5-plus"}, "aliyun_codingplan_intl": {"qwen3.6-plus": "qwen3.5-plus"}, } diff --git a/src/iac_code/providers/registry.py b/src/iac_code/providers/registry.py index dc1c8af5..f1d519aa 100644 --- a/src/iac_code/providers/registry.py +++ b/src/iac_code/providers/registry.py @@ -46,11 +46,17 @@ def model_ids(self) -> list[str]: base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", models=[ ModelEntry("qwen3.8-max", is_default=True, support_multimodal=True), + ModelEntry("qwen3.8-max-prime", support_multimodal=True), + ModelEntry("qwen3.8-flash", support_multimodal=True), + ModelEntry("qwen3.8-2.4t-a95b"), + ModelEntry("qwen3.8-27b", support_multimodal=True), ModelEntry("qwen3.7-max"), ModelEntry("qwen3.7-plus", support_multimodal=True), ModelEntry("qwen3.7-flash", support_multimodal=True), ModelEntry("qwen3.6-plus", support_multimodal=True), ModelEntry("qwen3.6-flash", support_multimodal=True), + ModelEntry("qwen3.6-35b-a3b", support_multimodal=True), + ModelEntry("qwen3.6-27b", support_multimodal=True), ModelEntry("qwen3.6-max-preview"), ModelEntry("qwen3-max"), ModelEntry("qwen3.5-plus", support_multimodal=True), @@ -60,6 +66,7 @@ def model_ids(self) -> list[str]: ModelEntry("qwq-plus"), ModelEntry("qwen3-coder-plus"), ModelEntry("qwen3-coder-next"), + ModelEntry("kimi-k3", support_multimodal=True), # This adapter sends local images as data URLs, while DashScope's # Moonshot-hosted K3 accepts public URLs only. ModelEntry("kimi/kimi-k3"), @@ -73,8 +80,11 @@ def model_ids(self) -> list[str]: ModelEntry("glm-5.2-fast-preview"), ModelEntry("glm-5.2"), ModelEntry("glm-5.1"), + ModelEntry("ZHIPU/GLM-5.3"), ModelEntry("MiniMax/MiniMax-M3", support_multimodal=True), ModelEntry("MiniMax-M2.5"), + ModelEntry("xiaomi/mimo-v2.5-pro"), + ModelEntry("stepfun/step-3.7-flash", support_multimodal=True), ], qwenpaw_provider_ids=["dashscope"], ), @@ -86,6 +96,7 @@ def model_ids(self) -> list[str]: base_url="https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1", models=[ ModelEntry("qwen3.8-max", is_default=True, support_multimodal=True), + ModelEntry("qwen3.8-flash", support_multimodal=True), ModelEntry("qwen3.7-max"), ModelEntry("qwen3.7-plus", support_multimodal=True), ModelEntry("qwen3.6-plus", support_multimodal=True), @@ -267,7 +278,9 @@ def model_ids(self) -> list[str]: provider_class="iac_code.providers.zhipu_provider.ZhiPuProvider", base_url="https://open.bigmodel.cn/api/paas/v4", models=[ - ModelEntry("glm-5.2", is_default=True), + ModelEntry("glm-5.3", is_default=True), + ModelEntry("glm-5.3-flash", support_multimodal=True), + ModelEntry("glm-5.2"), ModelEntry("glm-5.1"), ModelEntry("glm-5-turbo"), ModelEntry("glm-5"), @@ -290,7 +303,9 @@ def model_ids(self) -> list[str]: provider_class="iac_code.providers.zhipu_provider.ZhiPuProvider", base_url="https://api.z.ai/api/paas/v4", models=[ - ModelEntry("glm-5.2", is_default=True), + ModelEntry("glm-5.3", is_default=True), + ModelEntry("glm-5.3-flash", support_multimodal=True), + ModelEntry("glm-5.2"), ModelEntry("glm-5.1"), ModelEntry("glm-5-turbo"), ModelEntry("glm-5"), @@ -434,6 +449,7 @@ def model_ids(self) -> list[str]: base_url="https://open.bigmodel.cn/api/coding/paas/v4", models=[ ModelEntry("glm-5.3", is_default=True), + ModelEntry("glm-5.3-flash", support_multimodal=True), ModelEntry("glm-5.2"), ModelEntry("glm-5-turbo"), ModelEntry("glm-4.7"), @@ -451,6 +467,7 @@ def model_ids(self) -> list[str]: base_url="https://api.z.ai/api/coding/paas/v4", models=[ ModelEntry("glm-5.3", is_default=True), + ModelEntry("glm-5.3-flash", support_multimodal=True), ModelEntry("glm-5.2"), ModelEntry("glm-5-turbo"), ModelEntry("glm-4.7"), diff --git a/src/iac_code/providers/thinking.py b/src/iac_code/providers/thinking.py index 319c418b..e313c6aa 100644 --- a/src/iac_code/providers/thinking.py +++ b/src/iac_code/providers/thinking.py @@ -250,6 +250,12 @@ def effort_range(self) -> tuple[EffortLevel, EffortLevel] | None: thinking_enabled_by_default=True, ) +_DASHSCOPE_QWEN_HYBRID_SPEC = ThinkingSpec( + ThinkingFamily.DASHSCOPE, + supports_thinking_budget=True, + thinking_enabled_by_default=True, +) + _DASHSCOPE_QWEN38_PREVIEW_SPEC = ThinkingSpec( ThinkingFamily.DASHSCOPE, (EffortLevel.LOW, EffortLevel.MEDIUM, EffortLevel.XHIGH), @@ -267,6 +273,27 @@ def effort_range(self) -> tuple[EffortLevel, EffortLevel] | None: supports_disable=False, ) +_DASHSCOPE_HOSTED_KIMI_K3_SPEC = ThinkingSpec( + ThinkingFamily.DASHSCOPE, + supports_disable=False, + thinking_enabled_by_default=True, +) + +_DASHSCOPE_STEP37_SPEC = ThinkingSpec( + ThinkingFamily.DASHSCOPE, + (EffortLevel.LOW, EffortLevel.MEDIUM, EffortLevel.HIGH), + uses_reasoning_effort_param=True, +) + +_DASHSCOPE_ZHIPU_GLM53_SPEC = ThinkingSpec( + ThinkingFamily.DASHSCOPE, + _ZHIPU_GLM53_EFFORTS, + EffortLevel.MAX, + uses_reasoning_effort_param=True, + supports_disable=False, + thinking_enabled_by_default=True, +) + _ANTHROPIC_ADAPTIVE_SPEC = ThinkingSpec( ThinkingFamily.ANTHROPIC_ADAPTIVE, _ANTHROPIC_MODERN_EFFORTS, @@ -362,12 +389,18 @@ def effort_range(self) -> tuple[EffortLevel, EffortLevel] | None: }, "dashscope": { "qwen3.8-max": _DASHSCOPE_QWEN38_SPEC, + "qwen3.8-max-prime": _DASHSCOPE_QWEN38_SPEC, + "qwen3.8-flash": _DASHSCOPE_QWEN_HYBRID_SPEC, + "qwen3.8-2.4t-a95b": _DASHSCOPE_QWEN_HYBRID_SPEC, + "qwen3.8-27b": _DASHSCOPE_QWEN_HYBRID_SPEC, "qwen3.7-max": ThinkingSpec(ThinkingFamily.DASHSCOPE), "qwen3.7-plus": ThinkingSpec(ThinkingFamily.DASHSCOPE), "qwen3.7-flash": ThinkingSpec(ThinkingFamily.DASHSCOPE), "qwen3.6-max-preview": ThinkingSpec(ThinkingFamily.DASHSCOPE), "qwen3.6-plus": ThinkingSpec(ThinkingFamily.DASHSCOPE), "qwen3.6-flash": ThinkingSpec(ThinkingFamily.DASHSCOPE), + "qwen3.6-35b-a3b": _DASHSCOPE_QWEN_HYBRID_SPEC, + "qwen3.6-27b": _DASHSCOPE_QWEN_HYBRID_SPEC, "qwen3.5-plus": ThinkingSpec(ThinkingFamily.DASHSCOPE), "qwen3.5-flash": ThinkingSpec(ThinkingFamily.DASHSCOPE), "qwen-plus": ThinkingSpec(ThinkingFamily.DASHSCOPE), @@ -376,12 +409,19 @@ def effort_range(self) -> tuple[EffortLevel, EffortLevel] | None: "kimi-k2.6": ThinkingSpec(ThinkingFamily.DASHSCOPE), "kimi-k2.5": ThinkingSpec(ThinkingFamily.DASHSCOPE), "kimi-k2.7-code": _DASHSCOPE_KIMI_K27_CODE_SPEC, + "kimi-k3": _DASHSCOPE_HOSTED_KIMI_K3_SPEC, "kimi/kimi-k3": _DASHSCOPE_KIMI_K3_SPEC, "glm-5.2-fast-preview": _DASHSCOPE_GLM52_SPEC, "glm-5.2": _DASHSCOPE_GLM52_SPEC, "glm-5.1": _DASHSCOPE_GLM51_SPEC, + "ZHIPU/GLM-5.3": _DASHSCOPE_ZHIPU_GLM53_SPEC, "MiniMax-M2.5": ThinkingSpec(ThinkingFamily.DASHSCOPE), "MiniMax/MiniMax-M3": ThinkingSpec(ThinkingFamily.MINIMAX), + "xiaomi/mimo-v2.5-pro": ThinkingSpec( + ThinkingFamily.DASHSCOPE, + thinking_enabled_by_default=True, + ), + "stepfun/step-3.7-flash": _DASHSCOPE_STEP37_SPEC, "deepseek-v4-pro": ThinkingSpec( ThinkingFamily.DASHSCOPE, _DASHSCOPE_DEEPSEEK_EFFORTS, @@ -409,6 +449,7 @@ def effort_range(self) -> tuple[EffortLevel, EffortLevel] | None: }, "dashscope_token_plan": { "qwen3.8-max": _DASHSCOPE_QWEN38_SPEC, + "qwen3.8-flash": _DASHSCOPE_QWEN_HYBRID_SPEC, "qwen3.8-max-preview": _DASHSCOPE_QWEN38_PREVIEW_SPEC, "qwen3.7-max": ThinkingSpec(ThinkingFamily.DASHSCOPE), "qwen3.7-plus": ThinkingSpec(ThinkingFamily.DASHSCOPE), @@ -508,6 +549,8 @@ def effort_range(self) -> tuple[EffortLevel, EffortLevel] | None: "MiniMax-M2.5-highspeed": ThinkingSpec(ThinkingFamily.MINIMAX), }, "zhipu_cn": { + "glm-5.3": _ZHIPU_GLM53_SPEC, + "glm-5.3-flash": _ZHIPU_GLM53_SPEC, "glm-5.2": _ZHIPU_GLM52_SPEC, "glm-5.1": ThinkingSpec(ThinkingFamily.ZHIPU), "glm-5": ThinkingSpec(ThinkingFamily.ZHIPU), @@ -524,6 +567,7 @@ def effort_range(self) -> tuple[EffortLevel, EffortLevel] | None: }, "zhipu_cn_codingplan": { "glm-5.3": _ZHIPU_GLM53_SPEC, + "glm-5.3-flash": _ZHIPU_GLM53_SPEC, "glm-5.2": _ZHIPU_GLM52_SPEC, "glm-5-turbo": ThinkingSpec(ThinkingFamily.ZHIPU), "glm-4.7": ThinkingSpec(ThinkingFamily.ZHIPU), diff --git a/src/iac_code/providers/zhipu_provider.py b/src/iac_code/providers/zhipu_provider.py index ac2a9d45..c47cc668 100644 --- a/src/iac_code/providers/zhipu_provider.py +++ b/src/iac_code/providers/zhipu_provider.py @@ -44,7 +44,7 @@ def _build_thinking_kwargs(self) -> dict[str, Any]: if self._thinking_disabled(): if spec.supports_disable: return {"extra_body": {"thinking": {"type": "disabled"}}} - # Always-on models (glm-5.3) reject thinking.type=disabled. The + # Always-on models (glm-5.3 and glm-5.3-flash) reject thinking.type=disabled. The # official migration guidance is to keep thinking enabled at the # lightest effort instead of failing the request. always_on_kwargs: dict[str, Any] = {"extra_body": {"thinking": {"type": "enabled"}}} diff --git a/src/iac_code/services/context_manager.py b/src/iac_code/services/context_manager.py index e1dff050..f795ded5 100644 --- a/src/iac_code/services/context_manager.py +++ b/src/iac_code/services/context_manager.py @@ -71,6 +71,9 @@ def _context_config(context_window: int, max_output_tokens: int = 8_192) -> Cont "gemini-2.5-pro": _context_config(1_048_576, 65_536), "gemini-2.5-flash": _context_config(1_048_576, 65_536), "gemini-2.5-flash-lite": _context_config(1_048_576, 65_536), + # This ID is shared by Kimi's direct provider and Bailian's hosted model. + # Keep the common conservative capacity until context limits are keyed by + # provider as well as model. "kimi-k3": _context_config(1_000_000), # DashScope's Moonshot-hosted K3 advertises a 1M-token context window. # Keep the output cap conservative until the endpoint documents a limit. @@ -80,12 +83,18 @@ def _context_config(context_window: int, max_output_tokens: int = 8_192) -> Cont "kimi-k2.6": _context_config(262_144), "kimi-k2.5": _context_config(262_144), "qwen3.8-max": _context_config(1_000_000), + "qwen3.8-max-prime": _context_config(1_000_000), + "qwen3.8-flash": _context_config(1_000_000), + "qwen3.8-2.4t-a95b": _context_config(1_000_000, 131_072), + "qwen3.8-27b": _context_config(1_000_000, 131_072), "qwen3.8-max-preview": _context_config(1_000_000), "qwen3.7-max": _context_config(1_000_000), "qwen3.7-plus": _context_config(1_000_000), "qwen3.7-flash": _context_config(1_000_000), "qwen3.6-plus": _context_config(1_000_000), "qwen3.6-flash": _context_config(1_000_000), + "qwen3.6-35b-a3b": _context_config(262_144, 65_536), + "qwen3.6-27b": _context_config(262_144, 65_536), "qwen3.5-plus": _context_config(1_000_000), "qwen3.5-flash": _context_config(1_000_000), "qwen-plus": _context_config(1_000_000), @@ -99,6 +108,8 @@ def _context_config(context_window: int, max_output_tokens: int = 8_192) -> Cont "deepseek-v4-flash-0731": _context_config(1_000_000, 393_216), "deepseek-v4-flash": _context_config(1_000_000, 393_216), "glm-5.3": _context_config(1_000_000, 128_000), + "glm-5.3-flash": _context_config(1_000_000, 128_000), + "zhipu/glm-5.3": _context_config(1_048_576, 131_072), "glm-5.2-fast-preview": _context_config(1_048_576, 131_072), "glm-5.2": _context_config(1_000_000, 128_000), "glm-5.1": _context_config(202_752), @@ -109,6 +120,8 @@ def _context_config(context_window: int, max_output_tokens: int = 8_192) -> Cont "minimax-m2.7-highspeed": _context_config(196_608), "minimax-m2.5": _context_config(196_608), "minimax-m2.5-highspeed": _context_config(196_608), + "xiaomi/mimo-v2.5-pro": _context_config(1_048_576, 131_072), + "stepfun/step-3.7-flash": _context_config(262_144, 262_144), } _MODEL_CONFIGS: dict[str, ContextWindowConfig] = { diff --git a/src/iac_code/services/telemetry/constants.py b/src/iac_code/services/telemetry/constants.py index 27cc6598..a52f1913 100644 --- a/src/iac_code/services/telemetry/constants.py +++ b/src/iac_code/services/telemetry/constants.py @@ -79,12 +79,18 @@ "o4-mini", # Dashscope / Qwen "qwen3.8-max", + "qwen3.8-max-prime", + "qwen3.8-flash", + "qwen3.8-2.4t-a95b", + "qwen3.8-27b", "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.7-flash", "qwen3.6-plus", "qwen3.6-flash", + "qwen3.6-35b-a3b", + "qwen3.6-27b", "qwen3.6-max-preview", "qwen3.5-plus", "qwen3.5-flash", @@ -108,6 +114,10 @@ "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "glm-5.3", + "glm-5.3-flash", + "ZHIPU/GLM-5.3", + "xiaomi/mimo-v2.5-pro", + "stepfun/step-3.7-flash", "glm-5.2-fast-preview", "glm-5.2", "glm-5.1", diff --git a/tests/providers/test_dashscope_provider.py b/tests/providers/test_dashscope_provider.py index cf944e28..f18f3498 100644 --- a/tests/providers/test_dashscope_provider.py +++ b/tests/providers/test_dashscope_provider.py @@ -88,6 +88,25 @@ def test_kimi_k3_preserves_thinking_without_enable_flag(self): p = DashScopeProvider(model="kimi/kimi-k3", api_key="k") assert p._build_thinking_kwargs() == {"extra_body": {"preserve_thinking": True}} + def test_bailian_hosted_kimi_k3_keeps_always_on_thinking(self): + p = DashScopeProvider(model="kimi-k3", api_key="k", thinking_enabled=False) + assert p._build_thinking_kwargs() == { + "extra_body": {"enable_thinking": True, "preserve_thinking": True} + } + + def test_qwen38_open_model_supports_thinking_budget(self): + p = DashScopeProvider(model="qwen3.8-2.4t-a95b", api_key="k", thinking_budget=2048) + assert p._build_thinking_kwargs() == { + "extra_body": {"enable_thinking": True, "thinking_budget": 2048} + } + + def test_stepfun_uses_its_documented_effort_values(self): + p = DashScopeProvider(model="stepfun/step-3.7-flash", api_key="k", effort="medium") + assert p._build_thinking_kwargs() == { + "extra_body": {"enable_thinking": True}, + "reasoning_effort": "medium", + } + def test_qwen38_uses_always_on_thinking_without_enable_flag(self): p = DashScopeProvider( model="qwen3.8-max-preview", @@ -120,6 +139,20 @@ def test_glm(self): p = DashScopeProvider(model="glm-5.1", api_key="k") assert p._build_thinking_kwargs() == {"extra_body": {"enable_thinking": True}} + def test_zhipu_glm53_uses_always_on_hosted_protocol(self): + p = DashScopeProvider(model="ZHIPU/GLM-5.3", api_key="k", effort="high") + assert p._build_thinking_kwargs() == { + "extra_body": {"enable_thinking": True}, + "reasoning_effort": "high", + } + + def test_zhipu_glm53_disable_request_degrades_to_low_effort(self): + p = DashScopeProvider(model="ZHIPU/GLM-5.3", api_key="k", thinking_enabled=False) + assert p._build_thinking_kwargs() == { + "extra_body": {"enable_thinking": True}, + "reasoning_effort": "low", + } + @pytest.mark.parametrize("model", ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v4-flash-0731"]) def test_bailian_deepseek_emits_enable_thinking_and_reasoning_effort(self, model): p = DashScopeProvider(model=model, api_key="k", effort="xhigh") diff --git a/tests/providers/test_manager.py b/tests/providers/test_manager.py index 8ff0fc1a..c5873e31 100644 --- a/tests/providers/test_manager.py +++ b/tests/providers/test_manager.py @@ -1676,9 +1676,7 @@ async def test_complete_records_chat_span_event_and_total_metric(self): async def test_complete_attributes_bailian_openai_endpoint_to_dashscope_on_all_signals(self): mock_provider = AsyncMock() - mock_provider._base_url = ( - "https://llm-testworkspace000000.cn-beijing.maas.aliyuncs.com/compatible-mode/v1" - ) + mock_provider._base_url = "https://llm-testworkspace000000.cn-beijing.maas.aliyuncs.com/compatible-mode/v1" mock_provider.complete = AsyncMock( return_value=NonStreamingResponse( message_id="complete-response", @@ -2500,7 +2498,11 @@ def test_saved_config_takes_precedence_over_prefix(self, monkeypatch): ("MiniMax/MiniMax-M3", "dashscope"), ("deepseek-v4-pro-0813", "dashscope"), ("deepseek-v4-flash-0731", "dashscope"), - ("glm-5.3", "zhipu_cn_codingplan"), + ("ZHIPU/GLM-5.3", "dashscope"), + ("xiaomi/mimo-v2.5-pro", "dashscope"), + ("stepfun/step-3.7-flash", "dashscope"), + ("glm-5.3", "zhipu_cn"), + ("glm-5.3-flash", "zhipu_cn"), ], ) def test_exact_hosted_models_override_generic_prefixes(self, monkeypatch, model, expected_provider): @@ -2568,3 +2570,9 @@ def test_qwen36_fallback_is_available_on_each_endpoint(provider_key, expected_fa assert _PROVIDER_MODEL_FALLBACK_MAP[provider_key]["qwen3.6-plus"] == expected_fallback assert expected_fallback in {model.id for model in PROVIDER_REGISTRY[provider_key].models} assert "qwen3.6-plus" not in MODEL_FALLBACK_MAP + + +def test_qwen38_flash_fallback_is_available_on_each_endpoint(): + assert _PROVIDER_MODEL_FALLBACK_MAP["dashscope"]["qwen3.8-flash"] == "qwen3.7-flash" + assert _PROVIDER_MODEL_FALLBACK_MAP["dashscope_token_plan"]["qwen3.8-flash"] == "qwen3.6-flash" + assert "qwen3.8-flash" not in MODEL_FALLBACK_MAP diff --git a/tests/providers/test_provider_model_research_updates.py b/tests/providers/test_provider_model_research_updates.py index 88daa0f6..0775ae75 100644 --- a/tests/providers/test_provider_model_research_updates.py +++ b/tests/providers/test_provider_model_research_updates.py @@ -25,11 +25,17 @@ def test_dashscope_models_match_researched_bailian_catalog() -> None: for model_id in ( "qwen3.8-max", + "qwen3.8-max-prime", + "qwen3.8-flash", + "qwen3.8-2.4t-a95b", + "qwen3.8-27b", "qwen3.7-max", "qwen3.7-plus", "qwen3.7-flash", "qwen3.6-plus", "qwen3.6-flash", + "qwen3.6-35b-a3b", + "qwen3.6-27b", "qwen3.5-plus", "qwen3.5-flash", "qwen-plus", @@ -38,6 +44,7 @@ def test_dashscope_models_match_researched_bailian_catalog() -> None: "deepseek-v4-pro-0813", "deepseek-v4-flash-0731", "deepseek-v4-flash", + "kimi-k3", "kimi/kimi-k3", "kimi-k2.7-code", "kimi-k2.6", @@ -45,8 +52,11 @@ def test_dashscope_models_match_researched_bailian_catalog() -> None: "glm-5.2-fast-preview", "glm-5.2", "glm-5.1", + "ZHIPU/GLM-5.3", "MiniMax/MiniMax-M3", "MiniMax-M2.5", + "xiaomi/mimo-v2.5-pro", + "stepfun/step-3.7-flash", ): assert model_id in models @@ -61,17 +71,27 @@ def test_dashscope_models_match_researched_bailian_catalog() -> None: assert _model_entry("dashscope", "qwen3.8-max").support_multimodal assert not _model_entry("dashscope", "qwen3.7-max").support_multimodal for model_id in ( + "qwen3.8-max-prime", + "qwen3.8-flash", + "qwen3.8-27b", "qwen3.7-plus", "qwen3.7-flash", "qwen3.6-plus", "qwen3.6-flash", + "qwen3.6-35b-a3b", + "qwen3.6-27b", "qwen3.5-plus", "qwen3.5-flash", "kimi-k2.7-code", + "kimi-k3", "MiniMax/MiniMax-M3", + "stepfun/step-3.7-flash", ): assert _model_entry("dashscope", model_id).support_multimodal + assert not _model_entry("dashscope", "qwen3.8-2.4t-a95b").support_multimodal + assert not _model_entry("dashscope", "xiaomi/mimo-v2.5-pro").support_multimodal assert not _model_entry("dashscope", "deepseek-v4-pro-0813").support_multimodal + assert not _model_entry("dashscope", "ZHIPU/GLM-5.3").support_multimodal # The public adapter can only send local attachments as data URLs, but # Moonshot-hosted K3 on DashScope accepts public image URLs only. assert not _model_entry("dashscope", "kimi/kimi-k3").support_multimodal @@ -82,6 +102,7 @@ def test_dashscope_token_plan_uses_exact_supported_chat_models() -> None: for model_id in ( "qwen3.8-max", + "qwen3.8-flash", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", @@ -108,6 +129,7 @@ def test_dashscope_token_plan_uses_exact_supported_chat_models() -> None: assert "qwen3.8-max-preview" not in models assert PROVIDER_REGISTRY["dashscope_token_plan"].default_model == "qwen3.8-max" assert _model_entry("dashscope_token_plan", "qwen3.8-max").support_multimodal + assert _model_entry("dashscope_token_plan", "qwen3.8-flash").support_multimodal assert not _model_entry("dashscope_token_plan", "qwen3.7-max").support_multimodal assert not _model_entry("dashscope_token_plan", "deepseek-v4-pro-0813").support_multimodal for model_id in ("qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash", "kimi-k2.7-code", "kimi-k2.5", "kimi-k2.6"): @@ -175,22 +197,17 @@ def test_direct_kimi_minimax_and_zhipu_models_are_updated() -> None: assert "MiniMax-M2.1" not in _model_ids(provider_key) assert get_thinking_spec(provider_key, "MiniMax-M3").family is ThinkingFamily.MINIMAX - for provider_key in ("zhipu_cn", "zhipu_intl"): - assert "glm-5.2" in _model_ids(provider_key) - assert PROVIDER_REGISTRY[provider_key].default_model == "glm-5.2" - assert get_thinking_spec(provider_key, "glm-5.2").family is ThinkingFamily.ZHIPU - assert get_thinking_spec(provider_key, "glm-5.1").family is ThinkingFamily.ZHIPU - # GLM-5.3 is Coding-Plan-only for now; the standard model API is not - # live yet, so it must not appear on the direct endpoints. - assert "glm-5.3" not in _model_ids(provider_key) - assert get_thinking_spec(provider_key, "glm-5.3").family is ThinkingFamily.NONE - - for provider_key in ("zhipu_cn_codingplan", "zhipu_intl_codingplan"): + for provider_key in ("zhipu_cn", "zhipu_intl", "zhipu_cn_codingplan", "zhipu_intl_codingplan"): assert "glm-5.3" in _model_ids(provider_key) + assert "glm-5.3-flash" in _model_ids(provider_key) assert "glm-5.2" in _model_ids(provider_key) assert PROVIDER_REGISTRY[provider_key].default_model == "glm-5.3" + assert not _model_entry(provider_key, "glm-5.3").support_multimodal + assert _model_entry(provider_key, "glm-5.3-flash").support_multimodal assert get_thinking_spec(provider_key, "glm-5.3").family is ThinkingFamily.ZHIPU + assert get_thinking_spec(provider_key, "glm-5.3-flash").family is ThinkingFamily.ZHIPU assert get_thinking_spec(provider_key, "glm-5.2").family is ThinkingFamily.ZHIPU + assert get_thinking_spec(provider_key, "glm-5.1").family is ThinkingFamily.ZHIPU def test_provider_specific_thinking_wire_formats_do_not_use_openai_or_anthropic_effort_fields() -> None: @@ -217,8 +234,9 @@ def test_provider_specific_thinking_wire_formats_do_not_use_openai_or_anthropic_ "extra_body": {"thinking": {"type": "enabled"}}, "reasoning_effort": "max", } - # GLM-5.3 (Coding Plan) is always-on: it accepts low/high/max and rejects - # thinking.type=disabled, so a disabled toggle degrades to enabled + low. + # The GLM-5.3 family is always-on across Model API and Coding Plan: it + # accepts low/high/max and rejects thinking.type=disabled, so a disabled + # toggle degrades to enabled + low. assert ZhiPuProvider( model="glm-5.3", api_key="k", provider_key="zhipu_cn_codingplan", effort="max" )._build_thinking_kwargs() == { @@ -234,6 +252,18 @@ def test_provider_specific_thinking_wire_formats_do_not_use_openai_or_anthropic_ assert ZhiPuProvider( model="glm-5.3", api_key="k", provider_key="zhipu_intl_codingplan" )._build_thinking_kwargs() == {"extra_body": {"thinking": {"type": "enabled"}}} + assert ZhiPuProvider( + model="glm-5.3-flash", api_key="k", provider_key="zhipu_cn", effort="high" + )._build_thinking_kwargs() == { + "extra_body": {"thinking": {"type": "enabled"}}, + "reasoning_effort": "high", + } + assert ZhiPuProvider( + model="glm-5.3-flash", api_key="k", provider_key="zhipu_intl", thinking_enabled=False + )._build_thinking_kwargs() == { + "extra_body": {"thinking": {"type": "enabled"}}, + "reasoning_effort": "low", + } assert ZhiPuProvider(model="glm-5.2", api_key="k")._build_thinking_kwargs() == { "extra_body": {"thinking": {"type": "enabled"}} } @@ -383,6 +413,52 @@ def test_dashscope_new_model_protocols_are_not_flattened() -> None: "reasoning_effort": "xhigh", } + for model in ( + "qwen3.8-flash", + "qwen3.8-2.4t-a95b", + "qwen3.8-27b", + "qwen3.6-35b-a3b", + "qwen3.6-27b", + ): + spec = get_thinking_spec("dashscope", model) + assert spec.family is ThinkingFamily.DASHSCOPE + assert spec.supports_thinking_budget is True + assert spec.uses_reasoning_effort_param is False + assert spec.thinking_enabled_by_default is True + + assert DashScopeProvider( + model="qwen3.8-27b", + api_key="k", + thinking_budget=4096, + )._build_thinking_kwargs() == {"extra_body": {"enable_thinking": True, "thinking_budget": 4096}} + + hosted_kimi = get_thinking_spec("dashscope", "kimi-k3") + assert hosted_kimi.supports_disable is False + assert DashScopeProvider( + model="kimi-k3", + api_key="k", + thinking_enabled=False, + )._build_thinking_kwargs() == { + "extra_body": {"enable_thinking": True, "preserve_thinking": True} + } + + stepfun = get_thinking_spec("dashscope", "stepfun/step-3.7-flash") + assert stepfun.allowed_efforts == (EffortLevel.LOW, EffortLevel.MEDIUM, EffortLevel.HIGH) + assert DashScopeProvider( + model="stepfun/step-3.7-flash", + api_key="k", + effort="high", + )._build_thinking_kwargs() == { + "extra_body": {"enable_thinking": True}, + "reasoning_effort": "high", + } + + assert DashScopeProvider( + model="xiaomi/mimo-v2.5-pro", + api_key="k", + thinking_enabled=False, + )._build_thinking_kwargs() == {"extra_body": {"enable_thinking": False}} + preview = get_thinking_spec("dashscope_token_plan", "qwen3.8-max-preview") assert preview.allowed_efforts == (EffortLevel.LOW, EffortLevel.MEDIUM, EffortLevel.XHIGH) assert preview.default_effort is EffortLevel.XHIGH diff --git a/tests/providers/test_thinking_registry.py b/tests/providers/test_thinking_registry.py index 6a4fcab6..98ab49e3 100644 --- a/tests/providers/test_thinking_registry.py +++ b/tests/providers/test_thinking_registry.py @@ -2,6 +2,8 @@ from __future__ import annotations +import pytest + from iac_code.providers.thinking import ( EffortLevel, ThinkingFamily, @@ -129,6 +131,35 @@ def test_qwen38_formal_and_preview_have_distinct_thinking_modes(self): assert preview.supports_disable is False assert preview.thinking_enabled_by_default is True + @pytest.mark.parametrize( + "model", + [ + "qwen3.8-flash", + "qwen3.8-2.4t-a95b", + "qwen3.8-27b", + "qwen3.6-35b-a3b", + "qwen3.6-27b", + ], + ) + def test_bailian_open_qwen_models_use_hybrid_thinking_budget(self, model): + spec = get_thinking_spec("dashscope", model) + assert spec.family is ThinkingFamily.DASHSCOPE + assert spec.supports_thinking_budget is True + assert spec.thinking_enabled_by_default is True + + def test_bailian_hosted_kimi_mimo_and_stepfun_specs(self): + kimi = get_thinking_spec("dashscope", "kimi-k3") + assert kimi.supports_disable is False + assert kimi.thinking_enabled_by_default is True + + mimo = get_thinking_spec("dashscope", "xiaomi/mimo-v2.5-pro") + assert mimo.supports_disable is True + assert mimo.thinking_enabled_by_default is True + + stepfun = get_thinking_spec("dashscope", "stepfun/step-3.7-flash") + assert stepfun.allowed_efforts == (EffortLevel.LOW, EffortLevel.MEDIUM, EffortLevel.HIGH) + assert stepfun.uses_reasoning_effort_param is True + def test_anthropic_46_excludes_xhigh_but_keeps_max(self): spec = get_thinking_spec("anthropic", "claude-sonnet-4-6") assert EffortLevel.XHIGH not in spec.allowed_efforts @@ -294,18 +325,25 @@ def test_zhipu_intl_coding_plan_follows_nested_thinking_fallback(self): spec = get_thinking_spec("zhipu_intl_codingplan", "glm-5.1") assert spec.family is ThinkingFamily.ZHIPU - def test_glm53_coding_plan_is_always_on_with_low_high_max(self): - for provider_key in ("zhipu_cn_codingplan", "zhipu_intl_codingplan"): - spec = get_thinking_spec(provider_key, "glm-5.3") - assert spec.family is ThinkingFamily.ZHIPU - assert spec.allowed_efforts == (EffortLevel.LOW, EffortLevel.HIGH, EffortLevel.MAX) - assert spec.default_effort is EffortLevel.MAX - assert spec.uses_reasoning_effort_param is True - assert spec.supports_disable is False - assert spec.thinking_enabled_by_default is True - # The standard ZhiPu model API is not live for glm-5.3 yet. - assert get_thinking_spec("zhipu_cn", "glm-5.3").family is ThinkingFamily.NONE - assert get_thinking_spec("zhipu_intl", "glm-5.3").family is ThinkingFamily.NONE + def test_glm53_family_is_always_on_with_low_high_max(self): + for provider_key in ("zhipu_cn", "zhipu_intl", "zhipu_cn_codingplan", "zhipu_intl_codingplan"): + for model in ("glm-5.3", "glm-5.3-flash"): + spec = get_thinking_spec(provider_key, model) + assert spec.family is ThinkingFamily.ZHIPU + assert spec.allowed_efforts == (EffortLevel.LOW, EffortLevel.HIGH, EffortLevel.MAX) + assert spec.default_effort is EffortLevel.MAX + assert spec.uses_reasoning_effort_param is True + assert spec.supports_disable is False + assert spec.thinking_enabled_by_default is True + + def test_dashscope_zhipu_glm53_uses_hosted_wire_family(self): + spec = get_thinking_spec("dashscope", "ZHIPU/GLM-5.3") + assert spec.family is ThinkingFamily.DASHSCOPE + assert spec.allowed_efforts == (EffortLevel.LOW, EffortLevel.HIGH, EffortLevel.MAX) + assert spec.default_effort is EffortLevel.MAX + assert spec.uses_reasoning_effort_param is True + assert spec.supports_disable is False + assert spec.thinking_enabled_by_default is True def test_token_plan_minimax_m25(self): spec = get_thinking_spec("dashscope_token_plan", "MiniMax-M2.5") @@ -328,9 +366,11 @@ def test_kimi_and_zhipu_unset_default_on(self): assert resolve_thinking_active("zhipu_cn", "glm-5.2", None) is True def test_glm53_cannot_be_turned_off(self): - assert resolve_thinking_active("zhipu_cn_codingplan", "glm-5.3", None) is True + assert resolve_thinking_active("zhipu_cn", "glm-5.3", None) is True + assert resolve_thinking_active("zhipu_intl", "glm-5.3-flash", False) is True assert resolve_thinking_active("zhipu_cn_codingplan", "glm-5.3", False) is True assert resolve_thinking_active("zhipu_intl_codingplan", "glm-5.3", False) is True + assert resolve_thinking_active("dashscope", "ZHIPU/GLM-5.3", False) is True def test_reasoning_families_unset_default_off(self): # reasoning-effort / budget 家族:未配置时不下发思考指令 → 视为关。 diff --git a/tests/services/test_context_manager.py b/tests/services/test_context_manager.py index cf099ccd..f664d863 100644 --- a/tests/services/test_context_manager.py +++ b/tests/services/test_context_manager.py @@ -101,11 +101,17 @@ def test_kimi_k2_models_use_documented_context_capacity(self, model): ("model", "context_window"), [ ("qwen3.8-max", 1_000_000), + ("qwen3.8-max-prime", 1_000_000), + ("qwen3.8-flash", 1_000_000), + ("qwen3.8-2.4t-a95b", 1_000_000), + ("qwen3.8-27b", 1_000_000), ("qwen3.8-max-preview", 1_000_000), ("qwen3.7-max", 1_000_000), ("qwen3.7-plus", 1_000_000), ("qwen3.7-flash", 1_000_000), ("qwen3.6-flash", 1_000_000), + ("qwen3.6-35b-a3b", 262_144), + ("qwen3.6-27b", 262_144), ("deepseek-v4-pro", 1_000_000), ("deepseek-v4-pro-0813", 1_000_000), ("deepseek-v4-flash-0731", 1_000_000), @@ -113,6 +119,8 @@ def test_kimi_k2_models_use_documented_context_capacity(self, model): ("glm-5.1", 202_752), ("MiniMax-M3", 1_000_000), ("MiniMax/MiniMax-M3", 196_608), + ("xiaomi/mimo-v2.5-pro", 1_048_576), + ("stepfun/step-3.7-flash", 262_144), ], ) def test_current_dashscope_models_use_documented_context_capacity(self, model, context_window): @@ -134,16 +142,40 @@ def test_glm53_uses_documented_context_and_output_capacity(self): assert config.context_window == 1_000_000 assert config.max_output_tokens == 128_000 + def test_glm53_flash_uses_documented_context_and_output_capacity(self): + config = get_context_window_config("glm-5.3-flash") + assert config.context_window == 1_000_000 + assert config.max_output_tokens == 128_000 + + def test_dashscope_zhipu_glm53_uses_hosted_capacity(self): + config = get_context_window_config("ZHIPU/GLM-5.3") + assert config.context_window == 1_048_576 + assert config.max_output_tokens == 131_072 + def test_dashscope_glm52_fast_preview_uses_documented_capacity(self): config = get_context_window_config("glm-5.2-fast-preview") assert config.context_window == 1_048_576 assert config.max_output_tokens == 131_072 - def test_direct_kimi_k3_uses_documented_context_window(self): + def test_shared_kimi_k3_id_keeps_conservative_capacity(self): config = get_context_window_config("kimi-k3") assert config.context_window == 1_000_000 assert config.max_output_tokens == 8_192 + @pytest.mark.parametrize( + ("model", "max_output_tokens"), + [ + ("qwen3.8-2.4t-a95b", 131_072), + ("qwen3.8-27b", 131_072), + ("qwen3.6-35b-a3b", 65_536), + ("qwen3.6-27b", 65_536), + ("xiaomi/mimo-v2.5-pro", 131_072), + ("stepfun/step-3.7-flash", 262_144), + ], + ) + def test_new_bailian_models_use_documented_output_capacity(self, model, max_output_tokens): + assert get_context_window_config(model).max_output_tokens == max_output_tokens + def test_unknown_model_uses_default(self): config = get_context_window_config("unknown-model-xyz") assert config.context_window == 128_000 diff --git a/tests/test_config_env_overrides.py b/tests/test_config_env_overrides.py index 8e6feb5b..024496de 100644 --- a/tests/test_config_env_overrides.py +++ b/tests/test_config_env_overrides.py @@ -456,7 +456,11 @@ def test_api_key_env_routed_via_model_prefix_when_no_provider(self, monkeypatch, ("kimi/kimi-k3", "dashscope"), ("MiniMax/MiniMax-M3", "dashscope"), ("deepseek-v4-pro-0813", "dashscope"), - ("glm-5.3", "zhipu_cn_codingplan"), + ("ZHIPU/GLM-5.3", "dashscope"), + ("xiaomi/mimo-v2.5-pro", "dashscope"), + ("stepfun/step-3.7-flash", "dashscope"), + ("glm-5.3", "zhipu_cn"), + ("glm-5.3-flash", "zhipu_cn"), ], ) def test_api_key_env_routed_via_exact_hosted_model(self, monkeypatch, tmp_path, model, expected_slot): diff --git a/tests/test_services/test_telemetry/test_constants.py b/tests/test_services/test_telemetry/test_constants.py index 8c0b45fc..750cd6f8 100644 --- a/tests/test_services/test_telemetry/test_constants.py +++ b/tests/test_services/test_telemetry/test_constants.py @@ -33,10 +33,20 @@ def test_known_models_contains_researched_provider_updates(): "claude-opus-5", "gpt-5.6-sol", "qwen3.8-max", + "qwen3.8-max-prime", + "qwen3.8-flash", + "qwen3.8-2.4t-a95b", + "qwen3.8-27b", "qwen3.8-max-preview", "qwen3.7-flash", + "qwen3.6-35b-a3b", + "qwen3.6-27b", "kimi-k3", "glm-5.3", + "glm-5.3-flash", + "ZHIPU/GLM-5.3", + "xiaomi/mimo-v2.5-pro", + "stepfun/step-3.7-flash", "glm-5.2-fast-preview", "glm-5.2", "gemini-3.7-flash",