Skip to content

Commit bb25681

Browse files
regiskscdiscopops
authored andcommitted
feat: add Alibaba Coding Plan (DashScope) provider support (Gitlawb#509)
* feat: add Alibaba Coding Plan provider presets * fix: add DashScope presets to ProviderManager UI selection list * feat: read DASHSCOPE_API_KEY env var for DashScope provider presets * adds regression testing for alibaba models * docs: add time descriptive comment * feat(dashscope): add qwen3.6-plus model support * fix(dashscope): remove MiniMax-M2.5 entries to prevent future key conflicts
1 parent 75c9445 commit bb25681

4 files changed

Lines changed: 174 additions & 0 deletions

File tree

src/components/ProviderManager.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,16 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode {
10321032
label: 'LM Studio',
10331033
description: 'Local LM Studio endpoint',
10341034
},
1035+
{
1036+
value: 'dashscope-cn',
1037+
label: 'Alibaba Coding Plan (China)',
1038+
description: 'Alibaba DashScope China endpoint',
1039+
},
1040+
{
1041+
value: 'dashscope-intl',
1042+
label: 'Alibaba Coding Plan',
1043+
description: 'Alibaba DashScope International endpoint',
1044+
},
10351045
{
10361046
value: 'custom',
10371047
label: 'Custom',

src/utils/context.test.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,116 @@ test('MiniMax-M2.5 and M2.1 use explicit provider-specific context and output ca
141141
upperLimit: 131_072,
142142
})
143143
})
144+
145+
test('DashScope qwen3.6-plus uses provider-specific context and output caps', () => {
146+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
147+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
148+
149+
expect(getContextWindowForModel('qwen3.6-plus')).toBe(1_000_000)
150+
expect(getModelMaxOutputTokens('qwen3.6-plus')).toEqual({
151+
default: 65_536,
152+
upperLimit: 65_536,
153+
})
154+
expect(getMaxOutputTokensForModel('qwen3.6-plus')).toBe(65_536)
155+
})
156+
157+
test('DashScope qwen3.5-plus uses provider-specific context and output caps', () => {
158+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
159+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
160+
161+
expect(getContextWindowForModel('qwen3.5-plus')).toBe(1_000_000)
162+
expect(getModelMaxOutputTokens('qwen3.5-plus')).toEqual({
163+
default: 65_536,
164+
upperLimit: 65_536,
165+
})
166+
expect(getMaxOutputTokensForModel('qwen3.5-plus')).toBe(65_536)
167+
})
168+
169+
test('DashScope qwen3-coder-plus uses provider-specific context and output caps', () => {
170+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
171+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
172+
173+
expect(getContextWindowForModel('qwen3-coder-plus')).toBe(1_000_000)
174+
expect(getModelMaxOutputTokens('qwen3-coder-plus')).toEqual({
175+
default: 65_536,
176+
upperLimit: 65_536,
177+
})
178+
})
179+
180+
test('DashScope qwen3-coder-next uses provider-specific context and output caps', () => {
181+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
182+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
183+
184+
expect(getContextWindowForModel('qwen3-coder-next')).toBe(262_144)
185+
expect(getModelMaxOutputTokens('qwen3-coder-next')).toEqual({
186+
default: 65_536,
187+
upperLimit: 65_536,
188+
})
189+
})
190+
191+
test('DashScope qwen3-max uses provider-specific context and output caps', () => {
192+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
193+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
194+
195+
expect(getContextWindowForModel('qwen3-max')).toBe(262_144)
196+
expect(getModelMaxOutputTokens('qwen3-max')).toEqual({
197+
default: 32_768,
198+
upperLimit: 32_768,
199+
})
200+
})
201+
202+
test('DashScope qwen3-max dated variant resolves to base entry via prefix match', () => {
203+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
204+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
205+
206+
expect(getContextWindowForModel('qwen3-max-2026-01-23')).toBe(262_144)
207+
expect(getModelMaxOutputTokens('qwen3-max-2026-01-23')).toEqual({
208+
default: 32_768,
209+
upperLimit: 32_768,
210+
})
211+
})
212+
213+
test('DashScope kimi-k2.5 uses provider-specific context and output caps', () => {
214+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
215+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
216+
217+
expect(getContextWindowForModel('kimi-k2.5')).toBe(262_144)
218+
expect(getModelMaxOutputTokens('kimi-k2.5')).toEqual({
219+
default: 32_768,
220+
upperLimit: 32_768,
221+
})
222+
})
223+
224+
test('DashScope glm-5 uses provider-specific context and output caps', () => {
225+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
226+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
227+
228+
expect(getContextWindowForModel('glm-5')).toBe(202_752)
229+
expect(getModelMaxOutputTokens('glm-5')).toEqual({
230+
default: 16_384,
231+
upperLimit: 16_384,
232+
})
233+
})
234+
235+
test('DashScope glm-4.7 uses provider-specific context and output caps', () => {
236+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
237+
delete process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS
238+
239+
expect(getContextWindowForModel('glm-4.7')).toBe(202_752)
240+
expect(getModelMaxOutputTokens('glm-4.7')).toEqual({
241+
default: 16_384,
242+
upperLimit: 16_384,
243+
})
244+
})
245+
246+
test('DashScope models clamp oversized max output overrides to the provider limit', () => {
247+
process.env.CLAUDE_CODE_USE_OPENAI = '1'
248+
process.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = '100000'
249+
250+
expect(getMaxOutputTokensForModel('qwen3.6-plus')).toBe(65_536)
251+
expect(getMaxOutputTokensForModel('qwen3.5-plus')).toBe(65_536)
252+
expect(getMaxOutputTokensForModel('qwen3-coder-next')).toBe(65_536)
253+
expect(getMaxOutputTokensForModel('qwen3-max')).toBe(32_768)
254+
expect(getMaxOutputTokensForModel('kimi-k2.5')).toBe(32_768)
255+
expect(getMaxOutputTokensForModel('glm-5')).toBe(16_384)
256+
})

src/utils/model/openaiContextWindows.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,21 @@ const OPENAI_CONTEXT_WINDOWS: Record<string, number> = {
202202
'llama3.2:1b': 128_000,
203203
'qwen3:8b': 128_000,
204204
'codestral': 32_768,
205+
206+
// Alibaba DashScope (Coding Plan)
207+
// Model context windows from DashScope API /models endpoint (April 2026).
208+
// Values sourced from: qwen3.5-plus/qwen3-coder-plus (1M), qwen3-coder-next/max (256K),
209+
// kimi-k2.5 (256K), glm-5/glm-4.7 (198K).
210+
// Max output tokens: Qwen variants (64K/32K), GLM (16K).
211+
'qwen3.6-plus': 1_000_000,
212+
'qwen3.5-plus': 1_000_000,
213+
'qwen3-coder-plus': 1_000_000,
214+
'qwen3-coder-next': 262_144,
215+
'qwen3-max': 262_144,
216+
'qwen3-max-2026-01-23': 262_144,
217+
'kimi-k2.5': 262_144,
218+
'glm-5': 202_752,
219+
'glm-4.7': 202_752,
205220
}
206221

207222
/**
@@ -330,6 +345,11 @@ const OPENAI_MAX_OUTPUT_TOKENS: Record<string, number> = {
330345
'deepseek-r1:14b': 8_192,
331346
'mistral:7b': 4_096,
332347
'phi4:14b': 4_096,
348+
'gemma2:27b': 4_096,
349+
'codellama:13b': 4_096,
350+
'llama3.2:1b': 4_096,
351+
'qwen3:8b': 8_192,
352+
'codestral': 8_192,
333353

334354
// NVIDIA NIM models
335355
'nvidia/llama-3.1-nemotron-70b-instruct': 32_768,
@@ -356,6 +376,17 @@ const OPENAI_MAX_OUTPUT_TOKENS: Record<string, number> = {
356376
'databricks/dbrx-instruct': 32_768,
357377
'ai21labs/jamba-1.5-large-instruct': 32_768,
358378
'01-ai/yi-large': 8_192,
379+
380+
// Alibaba DashScope (Coding Plan)
381+
'qwen3.6-plus': 65_536,
382+
'qwen3.5-plus': 65_536,
383+
'qwen3-coder-plus': 65_536,
384+
'qwen3-coder-next': 65_536,
385+
'qwen3-max': 32_768,
386+
'qwen3-max-2026-01-23': 32_768,
387+
'kimi-k2.5': 32_768,
388+
'glm-5': 16_384,
389+
'glm-4.7': 16_384,
359390
}
360391

361392
function lookupByModel<T>(table: Record<string, T>, model: string): T | undefined {

src/utils/providerProfiles.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export type ProviderPreset =
2020
| 'azure-openai'
2121
| 'openrouter'
2222
| 'lmstudio'
23+
| 'dashscope-cn'
24+
| 'dashscope-intl'
2325
| 'custom'
2426
| 'nvidia-nim'
2527
| 'minimax'
@@ -220,6 +222,24 @@ export function getProviderPresetDefaults(
220222
apiKey: '',
221223
requiresApiKey: false,
222224
}
225+
case 'dashscope-cn':
226+
return {
227+
provider: 'openai',
228+
name: 'Alibaba Coding Plan (China)',
229+
baseUrl: 'https://coding.dashscope.aliyuncs.com/v1',
230+
model: 'qwen3.6-plus',
231+
apiKey: process.env.DASHSCOPE_API_KEY ?? '',
232+
requiresApiKey: true,
233+
}
234+
case 'dashscope-intl':
235+
return {
236+
provider: 'openai',
237+
name: 'Alibaba Coding Plan',
238+
baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1',
239+
model: 'qwen3.6-plus',
240+
apiKey: process.env.DASHSCOPE_API_KEY ?? '',
241+
requiresApiKey: true,
242+
}
223243
case 'custom':
224244
return {
225245
provider: 'openai',

0 commit comments

Comments
 (0)