From bb7eded0890f306d137ac15a6d85ff9c5f5cf923 Mon Sep 17 00:00:00 2001
From: lucas77778 <3098274296@qq.com>
Date: Tue, 4 Aug 2026 07:53:06 +0000
Subject: [PATCH 1/2] feat(providers): add DeepSeek support
Amp-Thread-ID: https://ampcode.com/threads/T-019fcb14-b13b-7164-b3dd-32cb1ead0770
---
.../providers/__tests__/add-flow.test.tsx | 26 ++++++++++++++++
.../providers/__tests__/capability.test.ts | 31 +++++++++++++++++++
.../src/settings/providers/capability.ts | 6 +++-
.../src/settings/providers/catalog.ts | 15 +++++++++
packages/presentation/i18n/src/locales/en.ts | 2 ++
.../presentation/i18n/src/locales/zh-cn.ts | 2 ++
6 files changed, 81 insertions(+), 1 deletion(-)
diff --git a/packages/client/workbench/src/settings/providers/__tests__/add-flow.test.tsx b/packages/client/workbench/src/settings/providers/__tests__/add-flow.test.tsx
index 8d0fef32c..9b8f47a9c 100644
--- a/packages/client/workbench/src/settings/providers/__tests__/add-flow.test.tsx
+++ b/packages/client/workbench/src/settings/providers/__tests__/add-flow.test.tsx
@@ -164,4 +164,30 @@ describe('non-subscription account creation', () => {
),
);
});
+
+ it('creates a DeepSeek account with its Chat-compatible endpoint', async () => {
+ const onSubmit = vi.fn();
+ render(
+ ,
+ );
+
+ fireEvent.change(screen.getByPlaceholderText('sk-…'), { target: { value: 'sk-deepseek' } });
+ fireEvent.click(screen.getByRole('button', { name: 'form.submit' }));
+ await waitFor(() =>
+ expect(onSubmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ service: 'deepseek',
+ credential: { type: 'api-key', key: 'sk-deepseek' },
+ endpoint: { baseUrl: 'https://api.deepseek.com', protocol: 'openai-chat' },
+ }),
+ ),
+ );
+ });
});
diff --git a/packages/client/workbench/src/settings/providers/__tests__/capability.test.ts b/packages/client/workbench/src/settings/providers/__tests__/capability.test.ts
index e3a83195d..8a42c67d3 100644
--- a/packages/client/workbench/src/settings/providers/__tests__/capability.test.ts
+++ b/packages/client/workbench/src/settings/providers/__tests__/capability.test.ts
@@ -59,6 +59,20 @@ describe('bindingAvailability', () => {
});
});
+ it('supports DeepSeek through Chat-compatible agents but not Responses-only Codex', () => {
+ const deepseek = account({
+ service: 'deepseek',
+ endpoint: { baseUrl: 'https://api.deepseek.com', protocol: 'openai-chat' },
+ });
+ expect(bindingAvailability(deepseek, 'claude-code')).toEqual({ tier: 'translate' });
+ expect(bindingAvailability(deepseek, 'opencode')).toEqual({ tier: 'native' });
+ expect(bindingAvailability(deepseek, 'pi')).toEqual({ tier: 'native' });
+ expect(bindingAvailability(deepseek, 'codex')).toEqual({
+ tier: 'unavailable',
+ reason: 'protocol-unsupported',
+ });
+ });
+
it('binds Grok Build to xAI catalog accounts', () => {
expect(bindingAvailability(account({ service: 'xai' }), 'grok-build')).toEqual({
tier: 'native',
@@ -130,6 +144,7 @@ describe('catalog helpers', () => {
it('implies a protocol from the service when the endpoint is absent', () => {
expect(accountProtocol(account({ service: 'anthropic-api' }))).toBe('anthropic');
+ expect(accountProtocol(account({ service: 'deepseek' }))).toBe('openai-chat');
expect(accountProtocol(account({ service: 'xai' }))).toBe('openai-chat');
// Dual-protocol gateways imply nothing without an endpoint.
expect(accountProtocol(account({ service: 'openrouter' }))).toBeUndefined();
@@ -144,4 +159,20 @@ describe('catalog helpers', () => {
),
).toBe('anthropic');
});
+
+ it('seeds the official DeepSeek Chat-compatible endpoint', () => {
+ const deepseek = serviceById('deepseek');
+ expect(deepseek).toMatchObject({
+ id: 'deepseek',
+ group: 'direct',
+ kind: 'endpoint',
+ variants: [
+ {
+ protocol: 'openai-chat',
+ baseUrl: 'https://api.deepseek.com',
+ credentialType: 'api-key',
+ },
+ ],
+ });
+ });
});
diff --git a/packages/client/workbench/src/settings/providers/capability.ts b/packages/client/workbench/src/settings/providers/capability.ts
index 6e571c925..90489f887 100644
--- a/packages/client/workbench/src/settings/providers/capability.ts
+++ b/packages/client/workbench/src/settings/providers/capability.ts
@@ -7,7 +7,8 @@ import { accountProtocol } from './catalog';
* adapters, packages/host/engine `translator.ts`):
* - claude-code speaks Anthropic natively (`ANTHROPIC_*` env); an `openai-chat` endpoint is
* reachable through the local aigateway translator, which needs a base URL AND a key.
- * - codex reaches any OpenAI-shaped endpoint via `OPENAI_BASE_URL` + `CODEX_API_KEY`.
+ * - codex reaches Responses endpoints via `OPENAI_BASE_URL` + `CODEX_API_KEY`; existing generic
+ * Chat-compatible services remain accepted, except known Chat-only services such as DeepSeek.
* - opencode and pi: openai-chat is the verified path; an Anthropic endpoint plausibly works but
* is unverified against the SDK — flip it here once verified live.
* - grok-build only consumes an xAI key as `XAI_API_KEY`; it ignores custom endpoint base URLs.
@@ -52,6 +53,9 @@ export function bindingAvailability(account: Account, kind: AgentKind): BindingA
? { tier: 'native' }
: { tier: 'unavailable', reason: 'protocol-unsupported' };
}
+ if (kind === 'codex' && account.service === 'deepseek') {
+ return { tier: 'unavailable', reason: 'protocol-unsupported' };
+ }
// Unknown protocol (pre-catalog custom account without endpoint): keep it bindable everywhere,
// matching the pre-catalog behavior — the user knows which vendor the bare key belongs to.
if (protocol === undefined) return { tier: 'native' };
diff --git a/packages/client/workbench/src/settings/providers/catalog.ts b/packages/client/workbench/src/settings/providers/catalog.ts
index 5fbfc98b7..09ee80eb2 100644
--- a/packages/client/workbench/src/settings/providers/catalog.ts
+++ b/packages/client/workbench/src/settings/providers/catalog.ts
@@ -75,6 +75,21 @@ export const SERVICE_CATALOG: ServiceDescriptor[] = [
],
secretPlaceholder: 'sk-…',
},
+ {
+ id: 'deepseek',
+ label: 'DeepSeek',
+ group: 'direct',
+ kind: 'endpoint',
+ variants: [
+ {
+ id: 'default',
+ protocol: 'openai-chat',
+ baseUrl: 'https://api.deepseek.com',
+ credentialType: 'api-key',
+ },
+ ],
+ secretPlaceholder: 'sk-…',
+ },
{
id: 'xai',
label: 'xAI (Grok)',
diff --git a/packages/presentation/i18n/src/locales/en.ts b/packages/presentation/i18n/src/locales/en.ts
index 4f58424ff..4f5806656 100644
--- a/packages/presentation/i18n/src/locales/en.ts
+++ b/packages/presentation/i18n/src/locales/en.ts
@@ -1059,6 +1059,7 @@ export const en = {
'chatgpt-sub': 'ChatGPT subscription',
'anthropic-api': 'Anthropic API',
'openai-api': 'OpenAI API',
+ deepseek: 'DeepSeek',
xai: 'xAI (Grok)',
openrouter: 'OpenRouter',
'vercel-gateway': 'Vercel AI Gateway',
@@ -1070,6 +1071,7 @@ export const en = {
'chatgpt-sub': 'Codex login, Plus / Pro plans',
'anthropic-api': 'A key from console.anthropic.com',
'openai-api': 'A key from platform.openai.com',
+ deepseek: 'A DeepSeek API key, OpenAI Chat-compatible',
xai: 'A key from console.x.ai, OpenAI-compatible',
openrouter: 'Aggregation gateway, both protocol shapes',
'vercel-gateway': 'Server-side translation, both protocol shapes',
diff --git a/packages/presentation/i18n/src/locales/zh-cn.ts b/packages/presentation/i18n/src/locales/zh-cn.ts
index 59cf01fd2..c677dc97a 100644
--- a/packages/presentation/i18n/src/locales/zh-cn.ts
+++ b/packages/presentation/i18n/src/locales/zh-cn.ts
@@ -1033,6 +1033,7 @@ export const zhCN = {
'chatgpt-sub': 'ChatGPT 订阅',
'anthropic-api': 'Anthropic API',
'openai-api': 'OpenAI API',
+ deepseek: 'DeepSeek',
xai: 'xAI(Grok)',
openrouter: 'OpenRouter',
'vercel-gateway': 'Vercel AI Gateway',
@@ -1044,6 +1045,7 @@ export const zhCN = {
'chatgpt-sub': 'Codex 登录,Plus / Pro 计划',
'anthropic-api': 'console.anthropic.com 的密钥',
'openai-api': 'platform.openai.com 的密钥',
+ deepseek: 'DeepSeek API 密钥,OpenAI Chat 兼容',
xai: 'console.x.ai 的密钥,OpenAI 兼容',
openrouter: '聚合网关,双协议形态',
'vercel-gateway': '服务端翻译,双协议形态',
From e9609dce43146a254fcfba494de2faacd40aca2e Mon Sep 17 00:00:00 2001
From: lucas77778 <3098274296@qq.com>
Date: Wed, 5 Aug 2026 12:18:56 +0000
Subject: [PATCH 2/2] fix(providers): support DeepSeek protocol variants
Amp-Thread-ID: https://ampcode.com/threads/T-019fd1c6-c339-745d-9223-4c54dd1ac910
---
.../providers/__tests__/add-flow.test.tsx | 9 ++-
.../providers/__tests__/capability.test.ts | 57 +++++++++++++------
.../src/settings/providers/capability.ts | 6 +-
.../src/settings/providers/catalog.ts | 18 +++++-
packages/presentation/i18n/src/locales/en.ts | 2 +-
.../presentation/i18n/src/locales/zh-cn.ts | 2 +-
6 files changed, 64 insertions(+), 30 deletions(-)
diff --git a/packages/client/workbench/src/settings/providers/__tests__/add-flow.test.tsx b/packages/client/workbench/src/settings/providers/__tests__/add-flow.test.tsx
index 9b8f47a9c..747400e88 100644
--- a/packages/client/workbench/src/settings/providers/__tests__/add-flow.test.tsx
+++ b/packages/client/workbench/src/settings/providers/__tests__/add-flow.test.tsx
@@ -165,7 +165,11 @@ describe('non-subscription account creation', () => {
);
});
- it('creates a DeepSeek account with its Chat-compatible endpoint', async () => {
+ it.each([
+ ['openai-chat', 'https://api.deepseek.com', 0],
+ ['openai-responses', 'https://api.deepseek.com', 1],
+ ['anthropic', 'https://api.deepseek.com/anthropic', 2],
+ ])('creates a DeepSeek account with its %s endpoint', async (protocol, baseUrl, variantIndex) => {
const onSubmit = vi.fn();
render(
{
/>,
);
+ fireEvent.click(screen.getAllByRole('radio').at(variantIndex)!);
fireEvent.change(screen.getByPlaceholderText('sk-…'), { target: { value: 'sk-deepseek' } });
fireEvent.click(screen.getByRole('button', { name: 'form.submit' }));
await waitFor(() =>
@@ -185,7 +190,7 @@ describe('non-subscription account creation', () => {
expect.objectContaining({
service: 'deepseek',
credential: { type: 'api-key', key: 'sk-deepseek' },
- endpoint: { baseUrl: 'https://api.deepseek.com', protocol: 'openai-chat' },
+ endpoint: { baseUrl, protocol },
}),
),
);
diff --git a/packages/client/workbench/src/settings/providers/__tests__/capability.test.ts b/packages/client/workbench/src/settings/providers/__tests__/capability.test.ts
index 8a42c67d3..b92857f32 100644
--- a/packages/client/workbench/src/settings/providers/__tests__/capability.test.ts
+++ b/packages/client/workbench/src/settings/providers/__tests__/capability.test.ts
@@ -59,18 +59,25 @@ describe('bindingAvailability', () => {
});
});
- it('supports DeepSeek through Chat-compatible agents but not Responses-only Codex', () => {
- const deepseek = account({
+ it('routes each DeepSeek protocol through the matching native agent', () => {
+ const chat = account({
service: 'deepseek',
endpoint: { baseUrl: 'https://api.deepseek.com', protocol: 'openai-chat' },
});
- expect(bindingAvailability(deepseek, 'claude-code')).toEqual({ tier: 'translate' });
- expect(bindingAvailability(deepseek, 'opencode')).toEqual({ tier: 'native' });
- expect(bindingAvailability(deepseek, 'pi')).toEqual({ tier: 'native' });
- expect(bindingAvailability(deepseek, 'codex')).toEqual({
- tier: 'unavailable',
- reason: 'protocol-unsupported',
+ const responses = account({
+ service: 'deepseek',
+ endpoint: { baseUrl: 'https://api.deepseek.com', protocol: 'openai-responses' },
+ });
+ const anthropic = account({
+ service: 'deepseek',
+ endpoint: { baseUrl: 'https://api.deepseek.com/anthropic', protocol: 'anthropic' },
});
+ expect(bindingAvailability(chat, 'claude-code')).toEqual({ tier: 'translate' });
+ expect(bindingAvailability(chat, 'codex')).toEqual({ tier: 'native' });
+ expect(bindingAvailability(chat, 'opencode')).toEqual({ tier: 'native' });
+ expect(bindingAvailability(chat, 'pi')).toEqual({ tier: 'native' });
+ expect(bindingAvailability(responses, 'codex')).toEqual({ tier: 'native' });
+ expect(bindingAvailability(anthropic, 'claude-code')).toEqual({ tier: 'native' });
});
it('binds Grok Build to xAI catalog accounts', () => {
@@ -144,9 +151,9 @@ describe('catalog helpers', () => {
it('implies a protocol from the service when the endpoint is absent', () => {
expect(accountProtocol(account({ service: 'anthropic-api' }))).toBe('anthropic');
- expect(accountProtocol(account({ service: 'deepseek' }))).toBe('openai-chat');
expect(accountProtocol(account({ service: 'xai' }))).toBe('openai-chat');
- // Dual-protocol gateways imply nothing without an endpoint.
+ // Multi-protocol services imply nothing without an endpoint.
+ expect(accountProtocol(account({ service: 'deepseek' }))).toBeUndefined();
expect(accountProtocol(account({ service: 'openrouter' }))).toBeUndefined();
expect(accountProtocol(account({}))).toBeUndefined();
// An explicit endpoint always wins.
@@ -160,19 +167,33 @@ describe('catalog helpers', () => {
).toBe('anthropic');
});
- it('seeds the official DeepSeek Chat-compatible endpoint', () => {
+ it('seeds all official DeepSeek protocol endpoints', () => {
const deepseek = serviceById('deepseek');
expect(deepseek).toMatchObject({
id: 'deepseek',
group: 'direct',
kind: 'endpoint',
- variants: [
- {
- protocol: 'openai-chat',
- baseUrl: 'https://api.deepseek.com',
- credentialType: 'api-key',
- },
- ],
});
+ if (deepseek?.kind !== 'endpoint') throw new Error('deepseek descriptor missing');
+ expect(deepseek.variants).toEqual([
+ {
+ id: 'openai',
+ protocol: 'openai-chat',
+ baseUrl: 'https://api.deepseek.com',
+ credentialType: 'api-key',
+ },
+ {
+ id: 'responses',
+ protocol: 'openai-responses',
+ baseUrl: 'https://api.deepseek.com',
+ credentialType: 'api-key',
+ },
+ {
+ id: 'anthropic',
+ protocol: 'anthropic',
+ baseUrl: 'https://api.deepseek.com/anthropic',
+ credentialType: 'api-key',
+ },
+ ]);
});
});
diff --git a/packages/client/workbench/src/settings/providers/capability.ts b/packages/client/workbench/src/settings/providers/capability.ts
index 90489f887..c7e2069ca 100644
--- a/packages/client/workbench/src/settings/providers/capability.ts
+++ b/packages/client/workbench/src/settings/providers/capability.ts
@@ -7,8 +7,7 @@ import { accountProtocol } from './catalog';
* adapters, packages/host/engine `translator.ts`):
* - claude-code speaks Anthropic natively (`ANTHROPIC_*` env); an `openai-chat` endpoint is
* reachable through the local aigateway translator, which needs a base URL AND a key.
- * - codex reaches Responses endpoints via `OPENAI_BASE_URL` + `CODEX_API_KEY`; existing generic
- * Chat-compatible services remain accepted, except known Chat-only services such as DeepSeek.
+ * - codex reaches Responses and compatible Chat endpoints via `OPENAI_BASE_URL` + `CODEX_API_KEY`.
* - opencode and pi: openai-chat is the verified path; an Anthropic endpoint plausibly works but
* is unverified against the SDK — flip it here once verified live.
* - grok-build only consumes an xAI key as `XAI_API_KEY`; it ignores custom endpoint base URLs.
@@ -53,9 +52,6 @@ export function bindingAvailability(account: Account, kind: AgentKind): BindingA
? { tier: 'native' }
: { tier: 'unavailable', reason: 'protocol-unsupported' };
}
- if (kind === 'codex' && account.service === 'deepseek') {
- return { tier: 'unavailable', reason: 'protocol-unsupported' };
- }
// Unknown protocol (pre-catalog custom account without endpoint): keep it bindable everywhere,
// matching the pre-catalog behavior — the user knows which vendor the bare key belongs to.
if (protocol === undefined) return { tier: 'native' };
diff --git a/packages/client/workbench/src/settings/providers/catalog.ts b/packages/client/workbench/src/settings/providers/catalog.ts
index 09ee80eb2..9ef1b46f6 100644
--- a/packages/client/workbench/src/settings/providers/catalog.ts
+++ b/packages/client/workbench/src/settings/providers/catalog.ts
@@ -17,9 +17,9 @@ import type {
/** Grouping in the add-account catalog grid. */
export type ServiceGroup = 'subscription' | 'direct' | 'gateway' | 'custom';
-/** One endpoint shape a service offers; dual-shape gateways expose one variant per protocol. */
+/** One endpoint shape a service offers; multi-protocol services expose one variant per protocol. */
export interface ServiceVariant {
- id: 'openai' | 'anthropic' | 'default';
+ id: 'openai' | 'responses' | 'anthropic' | 'default';
protocol: AccountProtocol;
/** Endpoint URL, possibly templated with `{placeholder}` segments. */
baseUrl: string;
@@ -82,11 +82,23 @@ export const SERVICE_CATALOG: ServiceDescriptor[] = [
kind: 'endpoint',
variants: [
{
- id: 'default',
+ id: 'openai',
protocol: 'openai-chat',
baseUrl: 'https://api.deepseek.com',
credentialType: 'api-key',
},
+ {
+ id: 'responses',
+ protocol: 'openai-responses',
+ baseUrl: 'https://api.deepseek.com',
+ credentialType: 'api-key',
+ },
+ {
+ id: 'anthropic',
+ protocol: 'anthropic',
+ baseUrl: 'https://api.deepseek.com/anthropic',
+ credentialType: 'api-key',
+ },
],
secretPlaceholder: 'sk-…',
},
diff --git a/packages/presentation/i18n/src/locales/en.ts b/packages/presentation/i18n/src/locales/en.ts
index 4f5806656..e4357f757 100644
--- a/packages/presentation/i18n/src/locales/en.ts
+++ b/packages/presentation/i18n/src/locales/en.ts
@@ -1071,7 +1071,7 @@ export const en = {
'chatgpt-sub': 'Codex login, Plus / Pro plans',
'anthropic-api': 'A key from console.anthropic.com',
'openai-api': 'A key from platform.openai.com',
- deepseek: 'A DeepSeek API key, OpenAI Chat-compatible',
+ deepseek: 'A DeepSeek API key, all three protocol shapes',
xai: 'A key from console.x.ai, OpenAI-compatible',
openrouter: 'Aggregation gateway, both protocol shapes',
'vercel-gateway': 'Server-side translation, both protocol shapes',
diff --git a/packages/presentation/i18n/src/locales/zh-cn.ts b/packages/presentation/i18n/src/locales/zh-cn.ts
index c677dc97a..496abaf1a 100644
--- a/packages/presentation/i18n/src/locales/zh-cn.ts
+++ b/packages/presentation/i18n/src/locales/zh-cn.ts
@@ -1045,7 +1045,7 @@ export const zhCN = {
'chatgpt-sub': 'Codex 登录,Plus / Pro 计划',
'anthropic-api': 'console.anthropic.com 的密钥',
'openai-api': 'platform.openai.com 的密钥',
- deepseek: 'DeepSeek API 密钥,OpenAI Chat 兼容',
+ deepseek: 'DeepSeek API 密钥,三种协议形态',
xai: 'console.x.ai 的密钥,OpenAI 兼容',
openrouter: '聚合网关,双协议形态',
'vercel-gateway': '服务端翻译,双协议形态',