From ef54e8275d6fe00afbfa1039a79379dcf9041f68 Mon Sep 17 00:00:00 2001 From: t Date: Mon, 7 Sep 2026 07:38:00 +0900 Subject: [PATCH 1/5] docs: describe BigModel and Raycast integration contracts Carry architecture context for #3641 and #3733. Live BigModel discovery remains deferred; local suites are not run. Co-authored-by: jamespan Co-authored-by: Chanhee Lee --- structure/01_runtime.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/structure/01_runtime.md b/structure/01_runtime.md index 7fb1c00997e..1601e5762bb 100644 --- a/structure/01_runtime.md +++ b/structure/01_runtime.md @@ -166,6 +166,22 @@ OAuth presets resolve discovery against the same canonical registry transport as before any adapter-specific transport override, so a stale configured `baseUrl` cannot receive an OAuth bearer token. +The BigModel Coding Plan Responses preset uses the separately documented +`https://open.bigmodel.cn/api/v1` transport and a static catalog. Its provider row +disables live discovery: a local Codex `models.json` example does not establish an +authenticated HTTP models endpoint. Its static context and reasoning metadata are +kept in the canonical registry, including an explicit empty selectable effort +ladder for `glm-5-turbo`. + +Raycast is a managed client export, not an upstream model provider. Its YAML +contribution owns only the unique `providers/[id=opencodex]` entry, with the +existing manifest and fingerprint checks protecting user-owned provider values. +Ambiguous selector matches and incompatible containers cannot be adopted or +mutated. Catalog refresh uses the existing owned-integration activation check; +an unowned client remains disconnected. OpenCodex omits Raycast API-key fields +and exports only to eligible local targets. Pro detection is an advisory hint, +not an authentication or entitlement decision. + ## Remote Hub hardening ownership `src/remote/protocol.ts` owns pure interval/feature negotiation. `src/client/hub-client.ts` owns bounded, schema-validated remote catalog consumption and key-id probes. `src/client/hub-relay.ts` is a fixed-authority management relay with URL, header, body, redirect, and stream bounds. The public data listener remains the direct client→hub path; the loopback management ingress never serves data-plane routes. From c7f6ba730f6146ef1b7737157f81311fff304796 Mon Sep 17 00:00:00 2001 From: t Date: Mon, 7 Sep 2026 07:39:31 +0900 Subject: [PATCH 2/5] feat(providers): carry static BigModel Responses preset from #3641 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Narrowed carry of jamespan’s PR #3641, source origin/axis2-source-3641 at b675d832bff9249701ba8575ad1a23196be8f156. Reconstruct the separate Responses preset using only the two models in the official Codex example: https://docs.bigmodel.cn/cn/coding-plan/tool/codex.md (checked 2026-09-07). Keep liveModels false and preserve custom destinations and Responses reasoning replay. Map exact context windows, effort ladders, max defaults, summary support, and text modalities. Do not carry model-discovery.ts or envelopeKey/idKey; Flash Responses metadata and live discovery remain unverified. Add consumer metadata and custom-transport collision regressions; document the static roster and existing Codex export policy (compatibility ultra on GLM-5.3, omitted default field on Turbo’s empty ladder). Validation: git diff --check passed. Tests, typecheck, lint, and builds intentionally not run per worker scope; parent final CI owns execution. Co-authored-by: jamespan --- .../src/content/docs/fr/guides/providers.md | 3 +- .../src/content/docs/guides/providers.md | 27 +++++- .../src/content/docs/ja/guides/providers.md | 3 +- .../src/content/docs/ko/guides/providers.md | 3 +- .../docs/reference/configuration/providers.md | 6 ++ .../src/content/docs/ru/guides/providers.md | 3 +- .../src/content/docs/tr/guides/providers.md | 3 +- .../content/docs/zh-cn/guides/providers.md | 3 +- .../content/docs/zh-tw/guides/providers.md | 3 +- src/providers/registry.ts | 29 +++++++ .../provider-registry-parity.test.ts | 85 ++++++++++++++++++- 11 files changed, 159 insertions(+), 9 deletions(-) diff --git a/docs-site/src/content/docs/fr/guides/providers.md b/docs-site/src/content/docs/fr/guides/providers.md index 988ca03cf70..89b3a7c626e 100644 --- a/docs-site/src/content/docs/fr/guides/providers.md +++ b/docs-site/src/content/docs/fr/guides/providers.md @@ -312,6 +312,7 @@ promotionnels de Cline ne sont accessibles que dans l'IDE ou la CLI Cline, pas p | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | | Zhipu AI (BigModel) | `https://open.bigmodel.cn/api/paas/v4` | +| [BigModel Coding Plan — Responses (liste statique)](/guides/providers/#bigmodel-coding-plan-over-responses) | `https://open.bigmodel.cn/api/v1` | | Qwen Cloud | Forfait à jetons (par défaut) : `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` · Facturation à l'usage : `https://dashscope.aliyuncs.com/compatible-mode/v1` · ou personnalisé | | Tencent Cloud Coding Plan | `https://api.lkeap.cloud.tencent.com/coding/v3` | | SiliconFlow | `https://api.siliconflow.cn/v1` | @@ -495,7 +496,7 @@ une barre trompeuse. > programmation interactifs. L'automatisation générale par API, les services applicatifs personnalisés et les > traitements par lots non interactifs sont interdits et peuvent entraîner la suspension de la clé du forfait. -> **Deux routes GLM :** `zai` correspond à l'abonnement international Z.AI Coding Plan ; `zhipu-bigmodel` +> **Facturation GLM :** `zai` correspond à l'abonnement international Z.AI Coding Plan ; `zhipu-bigmodel` > correspond au point de terminaison national BigModel de Zhipu, facturé à l'usage. Les hôtes, les clés et la > facturation diffèrent : une clé émise pour l'un ne permet pas de s'authentifier auprès de l'autre. diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index 05f586a8160..f5c4ddc1903 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -367,6 +367,7 @@ free-experimentation model. | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | | Zhipu AI (BigModel) | `https://open.bigmodel.cn/api/paas/v4` | +| BigModel Coding Plan (Responses, static roster) | `https://open.bigmodel.cn/api/v1` | | Qwen Cloud | Token plan (default): `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` · Pay as you go: `https://dashscope.aliyuncs.com/compatible-mode/v1` · or Custom | | Tencent Cloud Coding Plan | `https://api.lkeap.cloud.tencent.com/coding/v3` | | SiliconFlow | `https://api.siliconflow.cn/v1` | @@ -582,10 +583,34 @@ negative, or internally inconsistent billing totals produce no report rather tha > interactive coding tools only. General API automation, custom application backends, and > non-interactive batch use are prohibited and may cause the plan key to be suspended. -> **Two GLM routes:** `zai` is the Z.AI international coding-plan subscription; `zhipu-bigmodel` +> **GLM billing routes:** `zai` is the Z.AI international coding-plan subscription; `zhipu-bigmodel` > is Zhipu's domestic BigModel pay-as-you-go endpoint. Different hosts, different keys, different > billing — a key issued for one will not authenticate against the other. +### BigModel Coding Plan over Responses + +Select **Zhipu AI — BigModel Coding Plan (Responses)** (`zhipu-bigmodel-responses`) +for the `openai-responses` endpoint `https://open.bigmodel.cn/api/v1`. This is separate +from `zhipu-bigmodel-coding`, which uses Chat Completions at `/api/coding/paas/v4`. + +The preset uses a **static roster** (`liveModels: false`) taken from the +[official BigModel Codex example](https://docs.bigmodel.cn/cn/coding-plan/tool/codex.md): + +| Model | Context tokens | Upstream selectable effort | Default effort | Reasoning summaries | +| --- | ---: | --- | --- | --- | +| `glm-5.3` | 1,048,576 | `low`, `high`, `max` | `max` | Supported | +| `glm-5-turbo` | 204,800 | None (empty list) | `max` | Supported | + +Both entries declare text input. The default model is `glm-5.3`; Responses reasoning +content is preserved on replay. The existing Codex export adds its compatibility +`ultra` tier to GLM-5.3 and omits Turbo's default-effort field because Turbo has no +selectable ladder; the provider metadata still records `max` for both models. + +The example's `models.json` is a local catalog file, not a documented HTTP model-list +response. This preset does not perform live model discovery. `glm-5.3-flash` is not +seeded here because its exact Responses metadata is not verified. An existing custom +provider with the same name keeps its configured destination and metadata. + ### Multiple API keys Key-based providers can also keep multiple keys. Adding a key through the Providers page stores it diff --git a/docs-site/src/content/docs/ja/guides/providers.md b/docs-site/src/content/docs/ja/guides/providers.md index 33be9fc6947..ae692e1e2c5 100644 --- a/docs-site/src/content/docs/ja/guides/providers.md +++ b/docs-site/src/content/docs/ja/guides/providers.md @@ -216,6 +216,7 @@ Cline IDE/CLI のみで API からは使えません。`minimax/minimax-m2.5` | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | | Zhipu AI (BigModel) | `https://open.bigmodel.cn/api/paas/v4` | +| [BigModel Coding Plan — Responses (静的モデル一覧)](/guides/providers/#bigmodel-coding-plan-over-responses) | `https://open.bigmodel.cn/api/v1` | | Qwen Cloud | トークンプラン(デフォルト): `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` · 従量課金: `https://dashscope.aliyuncs.com/compatible-mode/v1` · またはカスタム | | Tencent Cloud Coding Plan | `https://api.lkeap.cloud.tencent.com/coding/v3` | | SiliconFlow | `https://api.siliconflow.cn/v1` | @@ -338,7 +339,7 @@ model ごとに capability が異なるため、provider 全体の parallel tool > コーディングツール専用としています。一般的な API 自動化、カスタムアプリのバックエンド、 > 非対話型バッチ利用は禁止されており、プランキーが停止される場合があります。 -> **GLM の経路は 2 つあります:** `zai` は Z.AI の国際コーディングプラン契約、`zhipu-bigmodel` +> **GLM の課金経路:** `zai` は Z.AI の国際コーディングプラン契約、`zhipu-bigmodel` > は Zhipu の中国国内向け BigModel 従量課金エンドポイントです。ホストもキーも課金も別で、 > 一方で発行したキーはもう一方では認証されません。 diff --git a/docs-site/src/content/docs/ko/guides/providers.md b/docs-site/src/content/docs/ko/guides/providers.md index 7781cc4f52d..c49ede4ec67 100644 --- a/docs-site/src/content/docs/ko/guides/providers.md +++ b/docs-site/src/content/docs/ko/guides/providers.md @@ -216,6 +216,7 @@ Cline IDE/CLI에서만 제공되며 API로는 사용할 수 없습니다. `minim | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | | Zhipu AI (BigModel) | `https://open.bigmodel.cn/api/paas/v4` | +| [BigModel Coding Plan — Responses (정적 모델 목록)](/guides/providers/#bigmodel-coding-plan-over-responses) | `https://open.bigmodel.cn/api/v1` | | Qwen Cloud | Token plan(기본): `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` · 종량제: `https://dashscope.aliyuncs.com/compatible-mode/v1` · 또는 사용자 지정 | | Tencent Cloud Coding Plan | `https://api.lkeap.cloud.tencent.com/coding/v3` | | SiliconFlow | `https://api.siliconflow.cn/v1` | @@ -328,7 +329,7 @@ provider 전체 parallel tool call이나 OpenAI `reasoning_effort`를 광고하 > 안내합니다. 일반 API 자동화, 사용자 애플리케이션 백엔드 및 비대화형 일괄 호출은 금지되며 > 플랜 키가 정지될 수 있습니다. -> **GLM 경로는 두 개입니다:** `zai`는 Z.AI 국제 코딩 플랜 구독이고, `zhipu-bigmodel`은 +> **GLM 과금 경로:** `zai`는 Z.AI 국제 코딩 플랜 구독이고, `zhipu-bigmodel`은 > Zhipu의 중국 내수 BigModel 종량제 엔드포인트입니다. 호스트도 키도 과금도 다르며, 한쪽에서 > 발급한 키는 다른 쪽에서 인증되지 않습니다. diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 02d5ba43237..85a9ba69037 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -747,6 +747,12 @@ container usually has no unlocked keychain session, so requests would fail close `${ENV_VAR}` reference in the service environment there instead. Env references are left untouched by `store`. +The `zhipu-bigmodel-responses` preset seeds `glm-5.3` and `glm-5-turbo` with +`liveModels: false` for `https://open.bigmodel.cn/api/v1`. Its static roster and +per-model context, effort, and summary metadata come from the +[BigModel Responses guide](/guides/providers/#bigmodel-coding-plan-over-responses). +The official local `models.json` example does not establish a live `/models` API. + With `liveModels: false`, an empty or omitted `models` list seeds the configured `defaultModel` first, followed by `retainModels`; duplicate ids are removed while preserving first occurrence. A nonempty explicit `models` list instead seeds `models` followed by `retainModels`, without diff --git a/docs-site/src/content/docs/ru/guides/providers.md b/docs-site/src/content/docs/ru/guides/providers.md index b057cf77c6f..e680f1bd913 100644 --- a/docs-site/src/content/docs/ru/guides/providers.md +++ b/docs-site/src/content/docs/ru/guides/providers.md @@ -229,6 +229,7 @@ opencodex поставляется с 79 встроенными пресетам | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | | Zhipu AI (BigModel) | `https://open.bigmodel.cn/api/paas/v4` | +| [BigModel Coding Plan — Responses (статический список)](/guides/providers/#bigmodel-coding-plan-over-responses) | `https://open.bigmodel.cn/api/v1` | | Qwen Cloud | Token plan (по умолчанию): `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` · Pay as you go: `https://dashscope.aliyuncs.com/compatible-mode/v1` · или Custom | | Tencent Cloud Coding Plan | `https://api.lkeap.cloud.tencent.com/coding/v3` | | SiliconFlow | `https://api.siliconflow.cn/v1` | @@ -367,7 +368,7 @@ plan. Ключ создаётся в [дашборде Featherless](https://feat > в интерактивных инструментах программирования. Автоматизация общего API, серверы пользовательских > приложений и неинтерактивные пакетные вызовы запрещены и могут привести к блокировке ключа плана. -> **Два маршрута GLM:** `zai` — это международная подписка Z.AI на coding-план, а `zhipu-bigmodel` — +> **Тарификация GLM:** `zai` — это международная подписка Z.AI на coding-план, а `zhipu-bigmodel` — > внутренняя китайская конечная точка BigModel с оплатой по факту использования. Разные хосты, > разные ключи, разная тарификация: ключ от одного сервиса не подойдёт к другому. diff --git a/docs-site/src/content/docs/tr/guides/providers.md b/docs-site/src/content/docs/tr/guides/providers.md index c5564e74a1f..6ff4f1c0381 100644 --- a/docs-site/src/content/docs/tr/guides/providers.md +++ b/docs-site/src/content/docs/tr/guides/providers.md @@ -355,6 +355,7 @@ yalnızca Cline IDE/CLI içinde mevcuttur; `minimax/minimax-m2.5` belgelenmiş A | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Kodlama) | `https://api.z.ai/api/coding/paas/v4` | | Zhipu AI (BigModel) | `https://open.bigmodel.cn/api/paas/v4` | +| [BigModel Coding Plan — Responses (statik model listesi)](/guides/providers/#bigmodel-coding-plan-over-responses) | `https://open.bigmodel.cn/api/v1` | | Qwen Cloud | Token planı (varsayılan): `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` · Kullandıkça öde: `https://dashscope.aliyuncs.com/compatible-mode/v1` · veya Özel | | Tencent Cloud Coding Plan | `https://api.lkeap.cloud.tencent.com/coding/v3` | | SiliconFlow | `https://api.siliconflow.cn/v1` | @@ -537,7 +538,7 @@ tutarsız faturalandırma toplamları yanıltıcı bir çubuk yerine hiçbir rap > **Tencent Cloud Coding Plan kullanım kısıtlaması:** Tencent bu aboneliği yalnızca etkileşimli kodlama araçları için belgeler. Genel API otomasyonu, özel uygulama arka uçları ve etkileşimsiz toplu kullanım yasaktır ve plan anahtarının askıya alınmasına neden olabilir. -> **İki GLM rotası:** `zai`, Z.AI uluslararası kodlama planı aboneliğidir; `zhipu-bigmodel`, Zhipu'nun yerel BigModel kullandıkça öde uç noktasıdır. Farklı ana bilgisayarlar, farklı anahtarlar, farklı faturalandırma — biri için verilen bir anahtar diğerine karşı kimlik doğrulaması yapmaz. +> **GLM faturalandırma rotaları:** `zai`, Z.AI uluslararası kodlama planı aboneliğidir; `zhipu-bigmodel`, Zhipu'nun yerel BigModel kullandıkça öde uç noktasıdır. Farklı ana bilgisayarlar, farklı anahtarlar, farklı faturalandırma — biri için verilen bir anahtar diğerine karşı kimlik doğrulaması yapmaz. ### Birden fazla API anahtarı diff --git a/docs-site/src/content/docs/zh-cn/guides/providers.md b/docs-site/src/content/docs/zh-cn/guides/providers.md index 3fb72a4e6a2..b4010cdae53 100644 --- a/docs-site/src/content/docs/zh-cn/guides/providers.md +++ b/docs-site/src/content/docs/zh-cn/guides/providers.md @@ -205,6 +205,7 @@ Cline IDE/CLI 中提供,不能通过 API 使用;`minimax/minimax-m2.5` 是 | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | | 智谱 AI (BigModel) | `https://open.bigmodel.cn/api/paas/v4` | +| [BigModel Coding Plan — Responses (静态模型列表)](/guides/providers/#bigmodel-coding-plan-over-responses) | `https://open.bigmodel.cn/api/v1` | | Qwen Cloud | Token plan(默认): `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` · 按量付费: `https://dashscope.aliyuncs.com/compatible-mode/v1` · 或自定义 | | 腾讯云 Coding Plan | `https://api.lkeap.cloud.tencent.com/coding/v3` | | SiliconFlow | `https://api.siliconflow.cn/v1` | @@ -316,7 +317,7 @@ Bearer key。公开模型列表只保留同时报告 `model_type: chat` 和 `cha > **腾讯云 Coding Plan 使用限制:**腾讯将此订阅限定为交互式编程工具使用。禁止通用 API > 自动化、自定义应用后端和非交互式批量调用;违规使用可能导致套餐密钥被停用。 -> **两条 GLM 线路:**`zai` 是 Z.AI 的国际 coding plan 订阅,`zhipu-bigmodel` 是智谱国内 +> **GLM 计费线路:**`zai` 是 Z.AI 的国际 coding plan 订阅,`zhipu-bigmodel` 是智谱国内 > BigModel 的按量付费端点。二者主机、密钥与计费均不同,为其中一方签发的密钥无法在另一方通过鉴权。 ### 多个 API 密钥 diff --git a/docs-site/src/content/docs/zh-tw/guides/providers.md b/docs-site/src/content/docs/zh-tw/guides/providers.md index 96cf91ccfb2..d26d093b7ec 100644 --- a/docs-site/src/content/docs/zh-tw/guides/providers.md +++ b/docs-site/src/content/docs/zh-tw/guides/providers.md @@ -273,6 +273,7 @@ IDE/CLI,不透過 API;`minimax/minimax-m2.5` 是文件列出的 API 免費 | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | | Z.AI (GLM Coding) | `https://api.z.ai/api/coding/paas/v4` | | Zhipu AI (BigModel) | `https://open.bigmodel.cn/api/paas/v4` | +| [BigModel Coding Plan — Responses (靜態模型清單)](/guides/providers/#bigmodel-coding-plan-over-responses) | `https://open.bigmodel.cn/api/v1` | | Qwen Cloud | Token plan(預設):`https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` · pay as you go:`https://dashscope.aliyuncs.com/compatible-mode/v1` · 或 Custom | | Tencent Cloud Coding Plan | `https://api.lkeap.cloud.tencent.com/coding/v3` | | SiliconFlow | `https://api.siliconflow.cn/v1` | @@ -418,7 +419,7 @@ quota probe 只會把 active key 傳送到 canonical A6API host,並拒絕 redi > **Tencent Cloud Coding Plan 使用限制:** Tencent 文件將此訂閱限定為互動式 coding tool。一般 API > automation、自訂 application backend 與非互動 batch 使用都被禁止,並可能造成 plan key 被停用。 -> **兩條 GLM 路徑:** `zai` 是 Z.AI 國際 Coding Plan 訂閱;`zhipu-bigmodel` 是智譜國內 BigModel +> **GLM 計費路徑:** `zai` 是 Z.AI 國際 Coding Plan 訂閱;`zhipu-bigmodel` 是智譜國內 BigModel > pay-as-you-go endpoint。兩者 host、key 與 billing 都不同;其中一邊發出的 key 無法在另一邊通過認證。 ### 多個 API 金鑰 diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 82f70a6a6a4..1c3396a48ae 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -2544,6 +2544,35 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // yields an empty picker at runtime. note: "Domestic BigModel Coding Plan endpoint (open.bigmodel.cn)", }, + // Narrowed carry of #3641: the official Codex example declares a local static catalog, + // not an HTTP /models contract. Keep Responses separate from the Chat endpoint above. + // Source: https://docs.bigmodel.cn/cn/coding-plan/tool/codex.md (checked 2026-09-07). + { + id: "zhipu-bigmodel-responses", + label: "Zhipu AI — BigModel Coding Plan (Responses)", + baseUrl: "https://open.bigmodel.cn/api/v1", + adapter: "openai-responses", + authKind: "key", + dashboardUrl: "https://bigmodel.cn/console/usercenter/apikeys", + defaultModel: "glm-5.3", + models: ["glm-5.3", "glm-5-turbo"], + liveModels: false, + jawcodeBundle: "zai", + // A pre-existing same-named custom provider must retain its destination and key boundary. + preserveCustomDestination: true, + modelContextWindows: { "glm-5.3": 1_048_576, "glm-5-turbo": 204_800 }, + modelInputModalities: { "glm-5.3": ["text"], "glm-5-turbo": ["text"] }, + modelReasoningEfforts: { + "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS, + // Explicitly empty: Turbo must not inherit the generic selectable effort ladder. + "glm-5-turbo": [], + }, + modelDefaultReasoningEfforts: { "glm-5.3": "max", "glm-5-turbo": "max" }, + modelSupportsReasoningSummaries: { "glm-5.3": true, "glm-5-turbo": true }, + // Responses replay uses this provider-level flag, not the Chat-path model list. + preserveResponsesReasoningContent: true, + note: "Domestic BigModel Coding Plan Responses endpoint; static model roster", + }, { id: "nanogpt", label: "NanoGPT", baseUrl: "https://nano-gpt.com/api/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://nano-gpt.com/api" }, { id: "synthetic", label: "Synthetic", baseUrl: "https://api.synthetic.new/openai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://synthetic.new" }, // SiliconFlow publishes an OpenAI-compatible chat endpoint and a dynamic model catalog. Do not diff --git a/tests/providers/provider-registry-parity.test.ts b/tests/providers/provider-registry-parity.test.ts index be84701e0b3..0d935d12a2e 100644 --- a/tests/providers/provider-registry-parity.test.ts +++ b/tests/providers/provider-registry-parity.test.ts @@ -11,6 +11,7 @@ import { deriveJawcodeAliases, deriveKeyLoginMap, deriveProviderPresets, + enrichProviderFromRegistry, providerConfigSeed, } from "../../src/providers/derive"; import { PROVIDER_REGISTRY } from "../../src/providers/registry"; @@ -33,7 +34,7 @@ function nativeTemplate(): Record { const EXPECTED_KEY_PROVIDER_IDS = [ "anthropic-apikey", "openai-apikey", "meta-model", "umans", "opencode-go", "neuralwatt", "openrouter", "cline-pass", "cline", "orcarouter", "bizrouter", "groq", "google", "google-vertex", "azure-openai", "deepseek", "cerebras", "chutes", "deepinfra", "hyperbolic", "nscale", "vultr", "baseten", "commandcode", "sambanova", "nebius", "digitalocean", "scaleway", "featherless", "novita", "together", "fireworks", "firepass", "moonshot", - "huggingface", "nvidia", "venice", "zai", "zhipu-bigmodel", "zhipu-bigmodel-coding", "nanogpt", "synthetic", "siliconflow", "qwen-cloud", "tencent-coding-plan", + "huggingface", "nvidia", "venice", "zai", "zhipu-bigmodel", "zhipu-bigmodel-coding", "zhipu-bigmodel-responses", "nanogpt", "synthetic", "siliconflow", "qwen-cloud", "tencent-coding-plan", "volcengine", "volcengine-coding-plan", "volcengine-agent-plan", "qianfan", "alibaba", "alibaba-token-plan", "alibaba-token-plan-intl", "parallel", "zenmux", "litellm", "ollama-cloud", "mistral", "minimax", "minimax-cn", "kimi-code", "opencode-zen", "vercel-ai-gateway", "opencode-free", "xiaomi", "xiaomi-mimo", "kilo", "mimo-free", "mimo", "cloudflare-ai-gateway", "cloudflare-workers-ai", "gitlab-duo", @@ -440,6 +441,87 @@ describe("provider registry parity", () => { expect(glm53Entry?.default_reasoning_level).toBe("max"); }); + test("BigModel Responses exports only the officially documented static Codex models", () => { + // Independent oracle: https://docs.bigmodel.cn/cn/coding-plan/tool/codex.md, + // local models.json example checked 2026-09-07; not an authenticated /models response. + const id = "zhipu-bigmodel-responses"; + const registry = PROVIDER_REGISTRY.find(entry => entry.id === id)!; + expect(registry).toMatchObject({ + adapter: "openai-responses", + baseUrl: "https://open.bigmodel.cn/api/v1", + authKind: "key", + defaultModel: "glm-5.3", + models: ["glm-5.3", "glm-5-turbo"], + liveModels: false, + preserveCustomDestination: true, + preserveResponsesReasoningContent: true, + }); + expect(registry.modelDiscovery).toBeUndefined(); + expect(registry.preserveReasoningContentModels).toBeUndefined(); + expect(KEY_LOGIN_PROVIDERS[id]).toMatchObject({ + models: ["glm-5.3", "glm-5-turbo"], liveModels: false, + }); + const provider = providerConfigSeed(registry); + enrichProviderFromRegistry(id, provider); + expect(provider.liveModels).toBe(false); + expect(provider.preserveResponsesReasoningContent).toBe(true); + const models = provider.models!.map(modelId => applyProviderConfigHints(id, provider, { + provider: id, id: modelId, + })); + expect(models).toMatchObject([ + { id: "glm-5.3", contextWindow: 1_048_576, reasoningEfforts: ["low", "high", "max"], + defaultReasoningEffort: "max", supportsReasoningSummaries: true, inputModalities: ["text"] }, + { id: "glm-5-turbo", contextWindow: 204_800, reasoningEfforts: [], + defaultReasoningEffort: "max", supportsReasoningSummaries: true, inputModalities: ["text"] }, + ]); + const entries = buildCatalogEntries(nativeTemplate(), [], models); + for (const [modelId, window, efforts] of [ + ["glm-5.3", 1_048_576, ["low", "high", "max", "ultra"]], + ["glm-5-turbo", 204_800, []], + ] as const) { + const entry = entries.find(row => row.slug === `${id}/${modelId}`); + expect(entry).toMatchObject({ + context_window: window, supports_reasoning_summaries: true, + }); + // Existing export policy adds a compatibility ultra tier and omits the default + // for empty ladders. The provider/CatalogModel defaults above remain official max. + expect(entry?.default_reasoning_level).toBe(modelId === "glm-5-turbo" ? undefined : "max"); + expect((entry?.supported_reasoning_levels as Array<{ effort: string }>).map(row => row.effort)) + .toEqual([...efforts]); + } + expect(entries.some(entry => String(entry.slug).includes("glm-5.3-flash"))).toBe(false); + }); + + test("BigModel Responses name collisions preserve custom transport and metadata", () => { + const id = "zhipu-bigmodel-responses"; + // Exercise both a different destination on the same wire and the canonical URL on + // another wire. Neither may acquire this preset's transport or per-model defaults. + for (const transport of [ + { adapter: "openai-responses", baseUrl: "https://custom.example.test/api/v1" }, + { adapter: "openai-chat", baseUrl: "https://open.bigmodel.cn/api/v1" }, + ]) { + const provider: OcxProviderConfig = { + ...transport, authMode: "key", apiKey: "test-custom-key", liveModels: true, + models: ["glm-5.3"], modelContextWindows: { "glm-5.3": 32_768 }, + modelReasoningEfforts: { "glm-5.3": ["medium"] }, + modelDefaultReasoningEfforts: { "glm-5.3": "medium" }, + modelSupportsReasoningSummaries: { "glm-5.3": false }, + }; + const enriched = structuredClone(provider); + enrichProviderFromRegistry(id, enriched); + expect(enriched).toEqual(provider); + const config: OcxConfig = { port: 10100, defaultProvider: id, providers: { [id]: provider } }; + const routed = routeModel(config, `${id}/glm-5.3`); + expect(routed.provider).toMatchObject(provider); + expect(routed.provider.modelContextWindows).toEqual({ "glm-5.3": 32_768 }); + expect(routed.provider.modelReasoningEfforts).toEqual({ "glm-5.3": ["medium"] }); + expect(routed.provider.modelDefaultReasoningEfforts).toEqual({ "glm-5.3": "medium" }); + expect(routed.provider.modelSupportsReasoningSummaries).toEqual({ "glm-5.3": false }); + expect(routed.provider.preserveResponsesReasoningContent).toBeUndefined(); + expect(routed.modelId).toBe("glm-5.3"); + } + }); + test("Anthropic API-key provider mirrors the OAuth entry's models on the key flow", () => { const anthropicOauth = PROVIDER_REGISTRY.find(entry => entry.id === "anthropic"); expect(KEY_LOGIN_PROVIDERS["anthropic-apikey"]).toMatchObject({ @@ -948,6 +1030,7 @@ describe("provider registry parity", () => { "minimax-cn": "minimax", "zhipu-bigmodel": "zai", "zhipu-bigmodel-coding": "zai", + "zhipu-bigmodel-responses": "zai", }); expect(resolveMetadataProvider("gemini")).toBe("google"); expect(resolveMetadataProvider("minimax-cn")).toBe("minimax"); From d52399070f098865394500398862b05ef95261c6 Mon Sep 17 00:00:00 2001 From: t Date: Mon, 7 Sep 2026 07:46:19 +0900 Subject: [PATCH 3/5] fix(providers): repair static BigModel login and Responses effort Skip undocumented HTTP model-list validation through the existing unknown key-validation policy. Add a zero-fetch regression. For explicitly empty model/provider effort ladders, omit only the Responses effort field and retain reasoning summaries. Preserve omitted requests, nonempty model override precedence, unknown/non-rankable ladders, and forward behavior. Keep the documented Turbo max metadata without adding a selectable tier or injecting a wire default. Existing __omit__ mappings and noReasoningModels policy are unchanged. Add outbound serialization cases for omitted, max and ultra effort, summary preservation, input immutability, and unchanged consumer paths. Regenerate model metadata; the generated delta is only the BigModel Responses to zai alias. Validation: bun run generate:model-metadata and git diff --check passed. No tests, typecheck, lint or builds run; commit hooks skipped to honor that restriction. Main final CI and separate independent review remain required. Co-authored-by: jamespan --- .../src/content/docs/guides/providers.md | 5 ++ src/adapters/openai-responses.ts | 7 +++ src/generated/model-metadata.ts | 1 + src/providers/registry.ts | 2 + .../provider-registry-parity.test.ts | 17 ++++-- .../openai-responses-passthrough.test.ts | 52 +++++++++++++++++++ 6 files changed, 81 insertions(+), 3 deletions(-) diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index f5c4ddc1903..081dced988e 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -605,11 +605,16 @@ Both entries declare text input. The default model is `glm-5.3`; Responses reaso content is preserved on replay. The existing Codex export adds its compatibility `ultra` tier to GLM-5.3 and omits Turbo's default-effort field because Turbo has no selectable ladder; the provider metadata still records `max` for both models. +For Turbo, outgoing Responses requests omit `reasoning.effort`, including a caller's +`max` or `ultra`, while preserving requested reasoning summaries. This leaves effort +selection to the upstream default; opencodex does not inject a selectable or wire `max`. The example's `models.json` is a local catalog file, not a documented HTTP model-list response. This preset does not perform live model discovery. `glm-5.3-flash` is not seeded here because its exact Responses metadata is not verified. An existing custom provider with the same name keeps its configured destination and metadata. +CLI key login also skips the undocumented `/models` probe and reports validation as +unknown; successful key authentication is established by a subsequent inference request. ### Multiple API keys diff --git a/src/adapters/openai-responses.ts b/src/adapters/openai-responses.ts index 07c0556d5ef..1b8c1b076e1 100644 --- a/src/adapters/openai-responses.ts +++ b/src/adapters/openai-responses.ts @@ -630,6 +630,13 @@ function mapRoutedResponsesReasoningEffort( if (provider.authMode === "forward") return body; if (configuredReasoningEfforts(provider, modelId) === undefined) return body; if (!isPlainObject(body) || !isPlainObject(body.reasoning)) return body; + const declaredEfforts = modelRecordValue(provider.modelReasoningEfforts, modelId) ?? provider.reasoningEfforts; + // An explicitly empty ladder means no effort control, not no reasoning output. + // Omit only effort so the upstream default applies; unknown/non-rankable ladders stay untouched. + if (declaredEfforts?.length === 0 && Object.hasOwn(body.reasoning, "effort")) { + const { effort: _effort, ...reasoning } = body.reasoning; + return { ...body, reasoning: Object.keys(reasoning).length > 0 ? reasoning : undefined }; + } const requested = body.reasoning.effort; if (typeof requested !== "string") return body; diff --git a/src/generated/model-metadata.ts b/src/generated/model-metadata.ts index 662cf6f1a7d..7220aa7509e 100644 --- a/src/generated/model-metadata.ts +++ b/src/generated/model-metadata.ts @@ -31,6 +31,7 @@ const PROVIDER_ALIASES: Record = { "moonshot": "moonshot", "zhipu-bigmodel": "zai", "zhipu-bigmodel-coding": "zai", + "zhipu-bigmodel-responses": "zai", "minimax": "minimax", "minimax-cn": "minimax" } as const; diff --git a/src/providers/registry.ts b/src/providers/registry.ts index 1c3396a48ae..ef7cb59e004 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -2557,6 +2557,8 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ defaultModel: "glm-5.3", models: ["glm-5.3", "glm-5-turbo"], liveModels: false, + // The local Codex catalog does not establish an authenticated HTTP /models contract. + apiKeyValidation: "unknown", jawcodeBundle: "zai", // A pre-existing same-named custom provider must retain its destination and key boundary. preserveCustomDestination: true, diff --git a/tests/providers/provider-registry-parity.test.ts b/tests/providers/provider-registry-parity.test.ts index 0d935d12a2e..e2083646a59 100644 --- a/tests/providers/provider-registry-parity.test.ts +++ b/tests/providers/provider-registry-parity.test.ts @@ -1,10 +1,10 @@ -import { describe, expect, test } from "bun:test"; +import { describe, expect, spyOn, test } from "bun:test"; import { buildCatalogEntries } from "../../src/codex/catalog"; import { CURSOR_NO_VISION_MODELS } from "../../src/adapters/cursor/discovery"; import { getModelMetadata, resolveMetadataProvider } from "../../src/generated/model-metadata"; import { buildInitProviders } from "../../src/cli/init"; import { OAUTH_PROVIDERS } from "../../src/oauth"; -import { enrichProviderFromCatalog, KEY_LOGIN_PROVIDERS } from "../../src/oauth/key-providers"; +import { enrichProviderFromCatalog, KEY_LOGIN_PROVIDERS, validateApiKey } from "../../src/oauth/key-providers"; import { deriveFeaturedProviderIds, deriveInitProviders, @@ -459,7 +459,7 @@ describe("provider registry parity", () => { expect(registry.modelDiscovery).toBeUndefined(); expect(registry.preserveReasoningContentModels).toBeUndefined(); expect(KEY_LOGIN_PROVIDERS[id]).toMatchObject({ - models: ["glm-5.3", "glm-5-turbo"], liveModels: false, + models: ["glm-5.3", "glm-5-turbo"], liveModels: false, apiKeyValidation: "unknown", }); const provider = providerConfigSeed(registry); enrichProviderFromRegistry(id, provider); @@ -492,6 +492,17 @@ describe("provider registry parity", () => { expect(entries.some(entry => String(entry.slug).includes("glm-5.3-flash"))).toBe(false); }); + test("BigModel Responses key login does not probe an undocumented models endpoint", async () => { + const fetchSpy = spyOn(globalThis, "fetch").mockImplementation(async () => new Response(null, { status: 403 })); + try { + const id = "zhipu-bigmodel-responses"; + expect(await validateApiKey(id, KEY_LOGIN_PROVIDERS[id], "test-bigmodel-key")).toBe("unknown"); + expect(fetchSpy).not.toHaveBeenCalled(); + } finally { + fetchSpy.mockRestore(); + } + }); + test("BigModel Responses name collisions preserve custom transport and metadata", () => { const id = "zhipu-bigmodel-responses"; // Exercise both a different destination on the same wire and the canonical URL on diff --git a/tests/responses/openai-responses-passthrough.test.ts b/tests/responses/openai-responses-passthrough.test.ts index 0277da71d4e..7e97217dc2d 100644 --- a/tests/responses/openai-responses-passthrough.test.ts +++ b/tests/responses/openai-responses-passthrough.test.ts @@ -569,6 +569,58 @@ describe("DeepSeek Responses endpoint contract", () => { } }); + test.each([undefined, "max", "ultra"])("BigModel Turbo omits outbound effort %s and preserves summary requests", (effort) => { + const id = "zhipu-bigmodel-responses"; + const config: OcxConfig = { + port: 10100, + defaultProvider: id, + providers: { [id]: providerConfigSeed(getProviderRegistryEntry(id)!) }, + }; + const route = routeModel(config, `${id}/glm-5-turbo`); + for (const withSummary of [false, true]) { + const raw = { + model: route.modelId, + input: "ping", + ...(effort !== undefined || withSummary ? { + reasoning: { + ...(effort !== undefined ? { effort } : {}), + ...(withSummary ? { summary: "auto" } : {}), + }, + } : {}), + }; + const before = structuredClone(raw); + const request = createResponsesPassthroughAdapter(route.provider).buildRequest(parseRequest(raw)); + const wire = JSON.parse(request.body); + expect(request.url).toBe("https://open.bigmodel.cn/api/v1/responses"); + if (withSummary) expect(wire.reasoning).toEqual({ summary: "auto" }); + else expect(wire).not.toHaveProperty("reasoning"); + expect(raw).toEqual(before); + } + }); + + test("a provider-wide empty ladder removes even non-string raw effort", () => { + const keyed = { adapter: "openai-responses", baseUrl: "https://example.test/v1", authMode: "key" as const }; + const raw = { model: "model", input: "ping", reasoning: { effort: 123, summary: "auto" } }; + const wire = JSON.parse(createResponsesPassthroughAdapter({ ...keyed, reasoningEfforts: [] }) + .buildRequest(parseRequest(raw)).body); + expect(wire.reasoning).toEqual({ summary: "auto" }); + expect(raw.reasoning.effort).toBe(123); + }); + + test("empty-ladder repair preserves unknown, non-rankable and native forward effort behavior", () => { + const keyed = { adapter: "openai-responses", baseUrl: "https://example.test/v1", authMode: "key" as const }; + for (const unchanged of [keyed, { ...keyed, reasoningEfforts: ["enabled"] }, { ...provider, reasoningEfforts: [] }]) { + const raw = { model: "gpt-5.6-sol", input: "ping", reasoning: { effort: "ultra" } }; + const wire = JSON.parse(createResponsesPassthroughAdapter(unchanged).buildRequest(parseRequest(raw)).body); + expect(wire.reasoning.effort).toBe("ultra"); + } + // A model-specific nonempty ladder overrides a provider-wide empty declaration. + const wire = JSON.parse(createResponsesPassthroughAdapter({ + ...keyed, reasoningEfforts: [], modelReasoningEfforts: { model: ["low", "high", "max"] }, + }).buildRequest(parseRequest({ model: "model", input: "ping", reasoning: { effort: "ultra" } })).body); + expect(wire.reasoning.effort).toBe("max"); + }); + test("a config saved before the fix is backfilled, and a hand-set path is preserved", () => { const saved = { adapter: "openai-chat", baseUrl: "https://api.deepseek.com", apiKey: "sk-test" } as Parameters[1]; enrichProviderFromRegistry("deepseek", saved); From 9336a27ffeed3023500de914654b85b1e889fb81 Mon Sep 17 00:00:00 2001 From: t Date: Mon, 7 Sep 2026 07:47:39 +0900 Subject: [PATCH 4/5] test(providers): distinguish BigModel upstream and bridge modalities First CI run 34064726690 reports text,image rather than text for both BigModel models at provider-registry-parity.test.ts:471; the Turbo effort array remains empty. Source tracing confirms applyProviderConfigHints uses isModelVisionSidecarConsumer, also used by the runtime vision planner, to expose image attachment support for configured text-only models. Keep the official text-only registry declarations and assert them independently before checking the bridge-enriched model and final catalog modalities. Preserve exact Turbo empty effort expectations and all generic modality behavior. Clarify that image handling requires the existing available, enabled vision sidecar rather than native BigModel image support. Validation: supplied CI failure log and source contract reviewed; git diff --check passed. No local tests, typecheck, lint or builds run. Commit hooks skipped per execution restriction. Main final CI and independent review remain required. Co-authored-by: jamespan --- docs-site/src/content/docs/guides/providers.md | 9 +++++++-- tests/providers/provider-registry-parity.test.ts | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index 081dced988e..5e469798164 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -601,8 +601,13 @@ The preset uses a **static roster** (`liveModels: false`) taken from the | `glm-5.3` | 1,048,576 | `low`, `high`, `max` | `max` | Supported | | `glm-5-turbo` | 204,800 | None (empty list) | `max` | Supported | -Both entries declare text input. The default model is `glm-5.3`; Responses reasoning -content is preserved on replay. The existing Codex export adds its compatibility +Both entries declare upstream text-only input. The Codex catalog advertises text and +image because opencodex's existing vision sidecar can describe images for text-only +models. Image handling requires an available, enabled vision sidecar; this does not +declare native BigModel image support. + +The default model is `glm-5.3`; Responses reasoning content is preserved on replay. +The existing Codex export adds its compatibility `ultra` tier to GLM-5.3 and omits Turbo's default-effort field because Turbo has no selectable ladder; the provider metadata still records `max` for both models. For Turbo, outgoing Responses requests omit `reasoning.effort`, including a caller's diff --git a/tests/providers/provider-registry-parity.test.ts b/tests/providers/provider-registry-parity.test.ts index e2083646a59..238a5808ea4 100644 --- a/tests/providers/provider-registry-parity.test.ts +++ b/tests/providers/provider-registry-parity.test.ts @@ -458,6 +458,8 @@ describe("provider registry parity", () => { }); expect(registry.modelDiscovery).toBeUndefined(); expect(registry.preserveReasoningContentModels).toBeUndefined(); + const upstreamModalities = { "glm-5.3": ["text"], "glm-5-turbo": ["text"] }; + expect(registry.modelInputModalities).toEqual(upstreamModalities); expect(KEY_LOGIN_PROVIDERS[id]).toMatchObject({ models: ["glm-5.3", "glm-5-turbo"], liveModels: false, apiKeyValidation: "unknown", }); @@ -468,11 +470,14 @@ describe("provider registry parity", () => { const models = provider.models!.map(modelId => applyProviderConfigHints(id, provider, { provider: id, id: modelId, })); + // The official upstream declaration stays text-only. Catalog hints add image for the + // existing vision sidecar (vision/eligibility.ts), not native BigModel image support. + expect(provider.modelInputModalities).toEqual(upstreamModalities); expect(models).toMatchObject([ { id: "glm-5.3", contextWindow: 1_048_576, reasoningEfforts: ["low", "high", "max"], - defaultReasoningEffort: "max", supportsReasoningSummaries: true, inputModalities: ["text"] }, + defaultReasoningEffort: "max", supportsReasoningSummaries: true, inputModalities: ["text", "image"] }, { id: "glm-5-turbo", contextWindow: 204_800, reasoningEfforts: [], - defaultReasoningEffort: "max", supportsReasoningSummaries: true, inputModalities: ["text"] }, + defaultReasoningEffort: "max", supportsReasoningSummaries: true, inputModalities: ["text", "image"] }, ]); const entries = buildCatalogEntries(nativeTemplate(), [], models); for (const [modelId, window, efforts] of [ @@ -482,6 +487,7 @@ describe("provider registry parity", () => { const entry = entries.find(row => row.slug === `${id}/${modelId}`); expect(entry).toMatchObject({ context_window: window, supports_reasoning_summaries: true, + input_modalities: ["text", "image"], }); // Existing export policy adds a compatibility ultra tier and omits the default // for empty ladders. The provider/CatalogModel defaults above remain official max. From 22f39ff105bea7675a82076b44b30aaf109826df Mon Sep 17 00:00:00 2001 From: t Date: Mon, 7 Sep 2026 08:10:12 +0900 Subject: [PATCH 5/5] test(responses): exercise empty effort ladder through valid ingress CI 34065098625 and 34065112182 exposed a numeric-effort fixture rejected by parseRequest before it reaches the adapter. Use schema-valid high effort to exercise provider-wide omission and input immutability; preserve strict ingress validation. No production change and no local suites. Co-authored-by: jamespan --- tests/responses/openai-responses-passthrough.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/responses/openai-responses-passthrough.test.ts b/tests/responses/openai-responses-passthrough.test.ts index 7e97217dc2d..24bc8e0e020 100644 --- a/tests/responses/openai-responses-passthrough.test.ts +++ b/tests/responses/openai-responses-passthrough.test.ts @@ -598,13 +598,13 @@ describe("DeepSeek Responses endpoint contract", () => { } }); - test("a provider-wide empty ladder removes even non-string raw effort", () => { + test("a provider-wide empty ladder removes schema-valid raw effort", () => { const keyed = { adapter: "openai-responses", baseUrl: "https://example.test/v1", authMode: "key" as const }; - const raw = { model: "model", input: "ping", reasoning: { effort: 123, summary: "auto" } }; + const raw = { model: "model", input: "ping", reasoning: { effort: "high", summary: "auto" } }; const wire = JSON.parse(createResponsesPassthroughAdapter({ ...keyed, reasoningEfforts: [] }) .buildRequest(parseRequest(raw)).body); expect(wire.reasoning).toEqual({ summary: "auto" }); - expect(raw.reasoning.effort).toBe(123); + expect(raw.reasoning.effort).toBe("high"); }); test("empty-ladder repair preserves unknown, non-rankable and native forward effort behavior", () => {