Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,10 @@ describe('icon + typography governance contract', () => {
assert.match(marks, /license: MIT/);
assert.match(marks, /73ea97baf919edb3d888a77cd45b21574e190a80124a59e199ea6953a28a959c/);
assert.match(marks, /import alibabaBrandMark from '\.\.\/assets\/provider-brands\/alibabacloud\.svg';/);
assert.match(marks, /case 'alibaba':\s*return <img src=\{alibabaBrandMark\} alt="" \/>/);
assert.match(
marks,
/case 'alibaba':\s*case 'alibaba-coding-plan-cn':\s*case 'alibaba-coding-plan':\s*return <img src=\{alibabaBrandMark\} alt="" \/>/,
);
assert.match(
notices,
/apps\/desktop\/src\/renderer\/assets\/provider-brands\/alibabacloud\.svg[\s\S]*e4302041fbb3039608d25f9f618bd462783b875e[\s\S]*packages\/static-svg\/icons\/alibabacloud-color\.svg[\s\S]*73ea97baf919edb3d888a77cd45b21574e190a80124a59e199ea6953a28a959c/,
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/renderer/settings/provider-brand-marks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ export function ProviderBrandMark({ type }: { type: ProviderType }): ReactElemen
case 'openrouter':
return <img src={openrouterBrandMark} alt="" />;
case 'alibaba':
case 'alibaba-coding-plan-cn':
case 'alibaba-coding-plan':
return <img src={alibabaBrandMark} alt="" />;
case 'cloudflare-workers-ai':
return <img src={cloudflareMarkUrl} alt="" />;
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop/src/renderer/settings/provider-display-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ export const PROVIDER_DISPLAY_COPY = {
zh: { name: 'Alibaba', description: '阿里云百炼接入,通义千问 Qwen 模型', badge: 'API' },
en: { name: 'Alibaba', description: 'Alibaba Cloud API access for Qwen models.', badge: 'API' },
},
'alibaba-coding-plan-cn': {
zh: { name: 'Alibaba Coding Plan 中国站', description: '阿里云百炼 Coding Plan 订阅 · 中国站', badge: 'Plan' },
en: { name: 'Alibaba Coding Plan (China)', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools · China.', badge: 'Plan' },
},
'alibaba-coding-plan': {
zh: { name: 'Alibaba Coding Plan 国际站', description: '阿里云百炼 Coding Plan 订阅 · 国际站', badge: 'Plan' },
en: { name: 'Alibaba Coding Plan', description: 'Alibaba Cloud Model Studio Coding Plan for interactive coding tools.', badge: 'Plan' },
},
// OAuth account providers (not in CATALOG_PROVIDER_TYPES; shown in the
// accounts section and on connection rows).
'github-copilot': {
Expand Down
28 changes: 28 additions & 0 deletions packages/cli/src/__tests__/connection-target.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,34 @@ describe('default session target resolver', () => {
assert.equal(target.model, 'deepseek-v4-pro-202606');
});

test('resolves both Alibaba Coding Plan regions without rewriting the selected model id', async () => {
for (const [providerType, defaultModel] of [
['alibaba-coding-plan-cn', 'qwen3.7-plus'],
['alibaba-coding-plan', 'qwen3-coder-plus'],
] as const) {
const connection = makeConnection({
slug: providerType,
name: providerType,
providerType,
defaultModel,
});

const target = await resolveDefaultSessionTarget({
connectionStore: {
getDefault: async () => providerType,
get: async (slug) => slug === providerType ? connection : null,
},
credentialStore: {
getSecret: async (_slug, kind) => kind === 'api_key' ? `${providerType}-test-key` : null,
},
});

assert.equal(target.connection.providerType, providerType);
assert.equal(target.apiKey, `${providerType}-test-key`);
assert.equal(target.model, defaultModel);
}
});

test('resolves LM Studio without reading a credential or rewriting the selected model id', async () => {
const connection = makeConnection({
slug: 'lm-studio',
Expand Down
75 changes: 75 additions & 0 deletions packages/core/src/__tests__/llm-connections.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ describe('provider compatibility contract', () => {
'groq',
'openrouter',
'alibaba',
'alibaba-coding-plan-cn',
'alibaba-coding-plan',
'cloudflare-workers-ai',
'ollama-cloud',
'ollama',
Expand Down Expand Up @@ -128,6 +130,8 @@ describe('provider compatibility contract', () => {
'groq',
'openrouter',
'alibaba',
'alibaba-coding-plan-cn',
'alibaba-coding-plan',
]);
assert.deepEqual(CATALOG_PROVIDER_TYPES, [
'kimi-coding-plan',
Expand Down Expand Up @@ -174,6 +178,8 @@ describe('provider compatibility contract', () => {
'groq',
'openrouter',
'alibaba',
'alibaba-coding-plan-cn',
'alibaba-coding-plan',
]);

for (const orderField of ['readyOrder', 'catalogOrder', 'recommendedOrder'] as const) {
Expand Down Expand Up @@ -767,6 +773,75 @@ describe('provider compatibility contract', () => {
]);
});

const ALIBABA_CODING_PLAN_MODELS = [
'qwen3.7-plus',
'qwen3.7-max',
'qwen3.6-plus',
'qwen3.6-flash',
'qwen3.5-plus',
'qwen3-max-2026-01-23',
'qwen3-coder-next',
'qwen3-coder-plus',
'glm-5',
'glm-4.7',
'kimi-k2.5',
'MiniMax-M2.5',
];

it('owns Alibaba Coding Plan (China) behavior under its independent persisted id', () => {
const plan = (PROVIDER_REGISTRY as Partial<Record<string, (typeof PROVIDER_REGISTRY)[keyof typeof PROVIDER_REGISTRY]>>)['alibaba-coding-plan-cn'];

assert.ok(plan, 'Alibaba Coding Plan (China) must be available through the shared provider registry');
assert.equal(plan.label, 'Alibaba Coding Plan (China)');
assert.equal(plan.baseUrl, 'https://coding.dashscope.aliyuncs.com/v1');
assert.equal(plan.authKind, 'api_key');
assert.equal(plan.backendKind, 'ai-sdk');
assert.equal(plan.status, 'ready');
assert.equal(plan.protocol, 'openai');
assert.deepEqual(plan.runtimeAdapter, { kind: 'openai-compatible', name: 'provider' });
assert.deepEqual(plan.modelDiscovery, { kind: 'fallback' });
assert.equal(plan.category, 'domestic');
assert.equal(plan.catalogGroup, 'plans');
assert.equal(plan.catalogBadge, 'Plan');
assert.equal(plan.signupUrl, 'https://www.aliyun.com/benefit/scene/codingplan');
assert.equal(plan.modelsDevId, 'alibaba-coding-plan-cn');
assert.deepEqual(plan.fallbackModels, ALIBABA_CODING_PLAN_MODELS);
});

it('owns Alibaba Coding Plan (global) behavior under its independent persisted id', () => {
const plan = (PROVIDER_REGISTRY as Partial<Record<string, (typeof PROVIDER_REGISTRY)[keyof typeof PROVIDER_REGISTRY]>>)['alibaba-coding-plan'];

assert.ok(plan, 'Alibaba Coding Plan must be available through the shared provider registry');
assert.equal(plan.label, 'Alibaba Coding Plan');
assert.equal(plan.baseUrl, 'https://coding-intl.dashscope.aliyuncs.com/v1');
assert.equal(plan.authKind, 'api_key');
assert.equal(plan.backendKind, 'ai-sdk');
assert.equal(plan.status, 'ready');
assert.equal(plan.protocol, 'openai');
assert.deepEqual(plan.runtimeAdapter, { kind: 'openai-compatible', name: 'provider' });
assert.deepEqual(plan.modelDiscovery, { kind: 'fallback' });
assert.equal(plan.category, 'overseas');
assert.equal(plan.catalogGroup, 'plans');
assert.equal(plan.catalogBadge, 'Plan');
assert.equal(plan.signupUrl, 'https://www.alibabacloud.com/help/en/model-studio/coding-plan');
assert.equal(plan.modelsDevId, 'alibaba-coding-plan');
assert.deepEqual(plan.fallbackModels, ALIBABA_CODING_PLAN_MODELS);
});

it('separates the two Alibaba Coding Plan regions from the Alibaba direct API path', () => {
const registry = PROVIDER_REGISTRY as Partial<Record<string, (typeof PROVIDER_REGISTRY)[keyof typeof PROVIDER_REGISTRY]>>;
const cn = registry['alibaba-coding-plan-cn'];
const global = registry['alibaba-coding-plan'];
const direct = registry.alibaba;

assert.ok(cn && global && direct);
const baseUrls = new Set([cn.baseUrl, global.baseUrl, direct.baseUrl]);
assert.equal(baseUrls.size, 3, 'each Alibaba access path must have a distinct endpoint');
assert.equal(direct.catalogGroup, 'api');
assert.equal(cn.catalogGroup, 'plans');
assert.equal(global.catalogGroup, 'plans');
});

it('owns StepFun China direct API behavior under the stable stepfun id', () => {
const stepfun = (PROVIDER_REGISTRY as Partial<Record<string, (typeof PROVIDER_REGISTRY)[keyof typeof PROVIDER_REGISTRY]>>).stepfun;

Expand Down
33 changes: 33 additions & 0 deletions packages/core/src/__tests__/model-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,39 @@ describe('ModelCatalogEntry', () => {
});
});

it('uses the official Alibaba Coding Plan snapshot for both regions without inventing live discovery', () => {
const expectedIds = [
'qwen3.7-plus',
'qwen3.7-max',
'qwen3.6-plus',
'qwen3.6-flash',
'qwen3.5-plus',
'qwen3-max-2026-01-23',
'qwen3-coder-next',
'qwen3-coder-plus',
'glm-5',
'glm-4.7',
'kimi-k2.5',
'MiniMax-M2.5',
];
for (const providerType of ['alibaba-coding-plan-cn', 'alibaba-coding-plan'] as const) {
const entries = buildConnectionModelCatalogEntries({
connection: { slug: providerType, providerType, defaultModel: 'qwen3.7-plus' },
});

assert.deepEqual(entries.map((entry) => entry.id), expectedIds);
assert.equal(entries[0]?.source, 'static_catalog');
assert.equal(entries[0]?.provenance.modelSource, 'fallback');
assert.equal(entries[0]?.contextWindow, 1_000_000);
assert.equal(entries[0]?.maxOutputTokens, 64_000);
assert.deepEqual(entries[0]?.capabilities, {
vision: true,
reasoning: true,
functionCalling: true,
});
}
});

it('uses the checked-in StepFun Global snapshot until account discovery succeeds', () => {
const entries = buildConnectionModelCatalogEntries({
connection: {
Expand Down
26 changes: 26 additions & 0 deletions packages/core/src/__tests__/provider-auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,32 @@ describe('ProviderAuth contract', () => {
expect(contract.actionAvailability.fetch_models).toBe('hidden');
});

test('Alibaba Coding Plan (China) uses an independent API key and fallback-model flow', () => {
const contract = deriveProviderAuthContract({
providerType: 'alibaba-coding-plan-cn',
hasSecret: true,
});

expect(contract.providerType).toBe('alibaba-coding-plan-cn');
expect(contract.setupMode).toBe('api_key');
expect(contract.requiresSecret).toBe(true);
expect(contract.actionAvailability.test_credentials).toBe('available');
expect(contract.actionAvailability.fetch_models).toBe('hidden');
});

test('Alibaba Coding Plan (global) uses an independent API key and fallback-model flow', () => {
const contract = deriveProviderAuthContract({
providerType: 'alibaba-coding-plan',
hasSecret: true,
});

expect(contract.providerType).toBe('alibaba-coding-plan');
expect(contract.setupMode).toBe('api_key');
expect(contract.requiresSecret).toBe(true);
expect(contract.actionAvailability.test_credentials).toBe('available');
expect(contract.actionAvailability.fetch_models).toBe('hidden');
});

test('StepFun Global uses an independent API-key credential and model-discovery flow', () => {
const contract = deriveProviderAuthContract({
providerType: 'stepfun-ai',
Expand Down
Loading
Loading