Skip to content

[Provider] alibaba-token-plan: native OpenAI Responses wire is officially supported upstream — request validated opt-in / default flip #5097

Closed

Description

@mdwsk88

Client or integration

Codex App (verified end-to-end through the OpenCodex proxy) / Codex CLI

Provider or upstream service

Alibaba Cloud Model Studio — Token Plan (Beijing) alibaba-token-plan, Token Plan (Intl) alibaba-token-plan-intl

OpenCodex version

2.59.0 (npm @bitkyc08/opencodex); also confirmed on upstream/dev @ 7817cd5 (2026-09-19)

Endpoint or capability

POST {base}/compatible-mode/v1/responses on the Token Plan gateway (https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1) — native OpenAI Responses wire, custom tools, streaming.

Current behaviour

Both Token Plan registry entries pin adapter: "openai-chat" (src/providers/registry/entries-extended.ts:792-795 and the intl sibling at :835), so the proxy translates every inbound Responses request to Chat Completions toward Alibaba. It works — I observed 88-96% prefix-cache hit on real traffic — but:

  • Token Plan's built-in "Harness" tools (web search, code interpreter, web extractor, file_search, MCP-in-Responses) are documented by Alibaba as Responses-only and cannot be triggered over the chat wire.
  • The chat bridge adds a translation layer for Responses-specific shapes (custom tools, reasoning envelopes) with known edge cases.

Expected behaviour

Alibaba now officially documents an OpenAI-compatible Responses API on the same /compatible-mode/v1 base, and publishes an official Codex integration guide with wire_api = "responses" against the same Token Plan base URL. OpenCodex should offer a first-class Responses wire for the Token Plan entries — opt-in first (registry modelWireDefaults or a documented modelAdapters preset), then a default flip after field validation.

Minimal redacted request or reproduction

Provider config (redacted) that opts one model in today:

"alibaba-token-plan": {
  "adapter": "openai-chat",
  "baseUrl": "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1",
  "apiKey": "sk-sp-***",
  "modelAdapters": { "qwen3.8-flash": "openai-responses" }
}

Through the proxy:

curl -N http://127.0.0.1:10100/v1/responses -H 'Content-Type: application/json' -d '{
  "model": "alibaba-token-plan/qwen3.8-flash",
  "instructions": "You are a coding agent. Use the apply_patch tool to make file changes.",
  "input": [{"type":"message","role":"user","content":[{"type":"input_text","text":"Create a file hello.txt containing hello world. Use the apply_patch tool."}]}],
  "tools": [{"type":"custom","name":"apply_patch","description":"Apply a unified diff patch. Input is raw patch text."}],
  "reasoning": {"effort":"low"},
  "stream": true
}'

Unauthenticated route-existence probes (no credentials sent):

curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/responses -d '{}'
# 401 {"code":"InvalidApiKey","message":"No API-key provided."}  -> route registered

curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/resp -d '{}'
# 404 -> near-miss path not registered (same pattern on dashscope-intl)

Actual response or error

(End-to-end verification, 2026-09-19, OpenCodex 2.59.0.)

Through the proxy (per-model modelAdapters hot-applied via the management API; provider-wide wire still openai-chat):

Direct upstream (Beijing Token Plan gateway, glm-5.3, same key-auth):

  • Full agent loop: tool-call turn (custom_tool_call with a correct patch) → 200; continuation turn replaying custom_tool_call + custom_tool_call_output in input → 200, coherent final message, again x_billing_type: "response_api".
  • Route probes above on both dashscope.aliyuncs.com and dashscope-intl.aliyuncs.com.

Upstream documentation

Suggested mapping or implementation notes

  • The Responses-wire model list (qwen3.8/3.7/3.x, deepseek-v4.x, glm-5.3/5.2, kimi-k3) matches ALIBABA_TOKEN_PLAN_MODELS, so a wire switch needs no new catalog.
  • There is precedent for mixed-wire registries: modelWireDefaults already pins per-model wires for the GitHub Copilot entry (src/providers/registry/entries-extended.ts:1218), and src/config/schema/leaf-validators.ts documents DeepSeek routing deepseek-v4-flash over native Responses for a Responses inbound while the provider-wide wire stays openai-chat. The same mechanism fits the Token Plan entries.
  • Correction after deeper live verification: the per-model modelAdapters opt-in works today (verified on 2.59.0 with qwen3.8-flash and qwen3.7-plus). The provider-level adapter field does NOT switch the wire: routedProviderConfig() (src/router.ts) rebuilds matching registry providers with adapter: registryEntry.adapter at request time, so the registry value silently wins and a saved openai-responses is ignored. The request is therefore for a registry-side option (adapter flip or modelWireDefaults) plus docs for the modelAdapters path.
  • Suggested rollout: registry opt-in / documented preset → field validation with Codex App and CLI (custom tools, reasoning replay, streaming, multi-turn continuation — all four verified above) → flip the default.

Additional context and attachments

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification (Alibaba Model Studio documentation linked above).

Activity

  1. added
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    toolstool_calls, MCP, web-search / sidecar tools
    on Sep 19, 2026
  2. github-actions commented on Sep 19, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  3. mdwsk88 commented on Sep 19, 2026

    @mdwsk88
    ContributorAuthor

    Follow-up finding (same day, deeper verification): provider-level adapter is silently normalized at request time.

    While testing, I pushed adapter: "openai-responses" into the running proxy via POST /api/providers and confirmed the live row with GET /api/providers — requests still ran over openai-chat. The cause is routedProviderConfig() in src/router.ts: when a provider's transport matches its registry entry, the resolved row is rebuilt with adapter: registryEntry.adapter, so the registry value wins over the configured one on every request.

    Consequences:

    • The provider-level adapter field cannot opt an alibaba-token-plan row into the Responses wire at all today (GUI, CLI, or hand-edited file all get normalized away). Per-model modelAdapters — resolved after routedProviderConfig() (e.g. src/server/responses/request-prepare.ts:900) — is the only user-side switch. Verified with a second model: qwen3.7-plus via modelAdapters → openai-responses, HTTP 200, the usage log records the responses wire.
    • This strengthens the case for a registry-side change: flip the entry adapter, add modelWireDefaults, or at minimum document modelAdapters as the supported opt-in — the obvious provider-level field is currently a silent no-op for registry providers.

    I have corrected the affected sentence in the issue body accordingly.

  4. Ingwannu commented on Sep 20, 2026

    @Ingwannu
    Owner

    Tracked by PR #5188. The capability gap is real, including the current registry-authority behavior that prevents a provider-wide configured adapter from overriding the canonical entry while per-model overrides still work.

    Do not flip the whole preset merely from route existence. The PR needs the primary-source evidence already supplied plus exact-field validation for both regional endpoints, custom tools, continuation, streaming terminal events, error normalization and credential destination. A staged model-level opt-in is preferable before a preset-wide default. #5188 is currently draft/conflicting, so keep this issue open until it is rebased, reviewed and validated.

  5. lidge-jun commented on Sep 23, 2026

    @lidge-jun
    Owner

    Fixed on dev by #5673 (squash-merged as 8ffd323, carrying #5188): Alibaba Token Plan (Beijing) now defaults qwen3.8-flash, qwen3.7-plus and glm-5.3 to native Responses for Responses inbound, with reasoning content preserved; Chat and Anthropic inbound stay on Chat, and modelAdapters still overrides either way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions