Skip to content

bug(providers): custom OpenAI-compat endpoints reject 'developer' role (Qwen/DashScope/DeepSeek/GLM) #183

@hqhq1025

Description

@hqhq1025

Summary

Custom providers with OpenAI Chat wire backed by Chinese OpenAI-compat endpoints (Qwen via DashScope, DeepSeek, GLM, Moonshot, etc.) fail with:

```
CodesignError: 400 developer is not one of ['system', 'assistant', 'user', 'tool', 'function'] - 'messages.[0].role'
CodesignError: 400 角色信息不正确
```

Reproduced with: `custom-dashscope-jhub` / `qwen3.6-plus` (v0.1.3).

Root Cause

`packages/providers/src/index.ts::synthesizeWireModel` hardcodes `reasoning: true` for every synthesized model. pi-ai's openai-responses-shared / openai-chat adapters check `model.reasoning` and pick the message role accordingly:

```js
// @mariozechner/pi-ai — dist/providers/openai-responses-shared.js
const role = model.reasoning ? "developer" : "system";
messages.push({ role, content: sanitizeSurrogates(context.systemPrompt) });
```

So every custom provider gets `role: 'developer'` injected into `messages[0]`, but DashScope and friends strictly enforce the classic OpenAI whitelist `['system','assistant','user','tool','function']`. `developer` is an OpenAI-only role (Responses API, GPT-5 family). Treating it as universal breaks the rest of the OpenAI-compat ecosystem.

Impact

  • Entire Chinese OpenAI-compat ecosystem is broken: Qwen, DeepSeek, GLM (`glm-4`+), Moonshot (`kimi`), Aliyun DashScope, etc. — easily the majority of mainland users
  • Test connection passes (because `/models` does not exercise the role whitelist), so users discover the failure only when they try to generate

Expected

For OpenAI Chat wire + a non-openai.com baseUrl, emit `role: 'system'` instead of `'developer'`.

Proposed Fix

In `packages/providers/src/index.ts::synthesizeWireModel`, do not blanket-set `reasoning: true`:

  • `wire === 'anthropic-messages'` → Anthropic has no role-vs-reasoning coupling → unchanged
  • `wire === 'openai-responses'` → keep `reasoning: true` (genuine Responses API callers expect `developer`, per bug(desktop): macOS generation fails with Instructions are required and no visible output #134 fix)
  • `wire === 'openai-chat'` → default `reasoning: false` unless baseUrl is `api.openai.com` AND model id matches a known reasoning family (`gpt-5*`, `o1*`, `o3*`). For everything else, `reasoning: false` → pi-ai emits `role: 'system'` → compat gateways accept it

Test Plan

Related

cc @hqhq1025

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions