Merge pull request 'fix: update DeepSeek direct models to v4' (#33) from codex/fix-deepseek-v4-models into main
Memind CI / Test, build, and release guards (push) Successful in 2m57s

This commit was merged in pull request #33.
This commit is contained in:
2026-07-25 01:23:05 +00:00
3 changed files with 36 additions and 29 deletions
+2 -2
View File
@@ -21,8 +21,8 @@ export const LLM_PROVIDER_CATALOG = [
label: 'DeepSeek', label: 'DeepSeek',
kind: 'builtin', kind: 'builtin',
apiKeyEnv: 'DEEPSEEK_API_KEY', apiKeyEnv: 'DEEPSEEK_API_KEY',
defaultModel: 'deepseek-chat', defaultModel: 'deepseek-v4-pro',
models: ['deepseek-chat', 'deepseek-reasoner'], models: ['deepseek-v4-pro', 'deepseek-v4-flash'],
}, },
{ {
id: 'openai', id: 'openai',
+30 -23
View File
@@ -129,7 +129,7 @@ test('createKey selects first profile and syncs to goosed', async () => {
providerId: 'custom_deepseek', providerId: 'custom_deepseek',
name: 'DeepSeek 主账号', name: 'DeepSeek 主账号',
apiKey: 'sk-deepseek-test', apiKey: 'sk-deepseek-test',
defaultModel: 'deepseek-chat', defaultModel: 'deepseek-v4-pro',
}); });
assert.equal(result.ok, true); assert.equal(result.ok, true);
assert.equal(result.key.isSelected, true); assert.equal(result.key.isSelected, true);
@@ -220,7 +220,7 @@ test('selectKey rejects disabled profile', async () => {
api_key_ciphertext: encryptSecret('sk-x', 'unit-test-secret').ciphertext, api_key_ciphertext: encryptSecret('sk-x', 'unit-test-secret').ciphertext,
api_key_iv: encryptSecret('sk-x', 'unit-test-secret').iv, api_key_iv: encryptSecret('sk-x', 'unit-test-secret').iv,
api_key_tag: encryptSecret('sk-x', 'unit-test-secret').tag, api_key_tag: encryptSecret('sk-x', 'unit-test-secret').tag,
default_model: 'deepseek-chat', default_model: 'deepseek-v4-pro',
status: 'disabled', status: 'disabled',
is_selected: 0, is_selected: 0,
created_at: 1, created_at: 1,
@@ -250,6 +250,13 @@ test('catalog includes custom provider template', () => {
assert.equal(custom.kind, 'custom'); assert.equal(custom.kind, 'custom');
}); });
test('DeepSeek catalog uses the currently supported V4 model names', () => {
const deepseek = LLM_PROVIDER_CATALOG.find((item) => item.id === 'custom_deepseek');
assert.ok(deepseek);
assert.equal(deepseek.defaultModel, 'deepseek-v4-pro');
assert.deepEqual(deepseek.models, ['deepseek-v4-pro', 'deepseek-v4-flash']);
});
test('listExecutorBindings returns all executor placeholders', async () => { test('listExecutorBindings returns all executor placeholders', async () => {
const pool = { const pool = {
async query(sql) { async query(sql) {
@@ -295,7 +302,7 @@ test('testKey checks builtin DeepSeek through OpenAI compatible endpoint', async
api_key_ciphertext: encrypted.ciphertext, api_key_ciphertext: encrypted.ciphertext,
api_key_iv: encrypted.iv, api_key_iv: encrypted.iv,
api_key_tag: encrypted.tag, api_key_tag: encrypted.tag,
default_model: 'deepseek-chat', default_model: 'deepseek-v4-pro',
status: 'active', status: 'active',
is_selected: 1, is_selected: 1,
created_at: 1, created_at: 1,
@@ -324,11 +331,11 @@ test('testKey checks builtin DeepSeek through OpenAI compatible endpoint', async
encryptionKey: 'unit-test-secret', encryptionKey: 'unit-test-secret',
apiFetchImpl: mockFetch, apiFetchImpl: mockFetch,
}); });
const result = await service.testKey('key-deepseek', 'deepseek-chat'); const result = await service.testKey('key-deepseek', 'deepseek-v4-pro');
assert.equal(result.ok, true); assert.equal(result.ok, true);
assert.equal(calls[0].url, 'https://api.deepseek.com/v1/chat/completions'); assert.equal(calls[0].url, 'https://api.deepseek.com/v1/chat/completions');
assert.equal(calls[0].init.headers.Authorization, 'Bearer sk-deepseek-test'); assert.equal(calls[0].init.headers.Authorization, 'Bearer sk-deepseek-test');
assert.equal(calls[0].body.model, 'deepseek-chat'); assert.equal(calls[0].body.model, 'deepseek-v4-pro');
}); });
test('createChatCompletion uses selected OpenAI-compatible provider', async () => { test('createChatCompletion uses selected OpenAI-compatible provider', async () => {
@@ -477,7 +484,7 @@ test('getExecutorRuntimeConfig resolves aider env from binding', async () => {
api_key_ciphertext: encrypted.ciphertext, api_key_ciphertext: encrypted.ciphertext,
api_key_iv: encrypted.iv, api_key_iv: encrypted.iv,
api_key_tag: encrypted.tag, api_key_tag: encrypted.tag,
default_model: 'deepseek-chat', default_model: 'deepseek-v4-pro',
status: 'active', status: 'active',
is_selected: 1, is_selected: 1,
created_at: 1, created_at: 1,
@@ -488,7 +495,7 @@ test('getExecutorRuntimeConfig resolves aider env from binding', async () => {
executor: 'aider', executor: 'aider',
purpose: 'default', purpose: 'default',
provider_key_id: 'key-deepseek', provider_key_id: 'key-deepseek',
model: 'deepseek-reasoner', model: 'deepseek-v4-flash',
enabled: 1, enabled: 1,
created_at: 1, created_at: 1,
updated_at: 1, updated_at: 1,
@@ -510,8 +517,8 @@ test('getExecutorRuntimeConfig resolves aider env from binding', async () => {
}); });
const runtime = await service.getExecutorRuntimeConfig('aider'); const runtime = await service.getExecutorRuntimeConfig('aider');
assert.equal(runtime.ok, true); assert.equal(runtime.ok, true);
assert.equal(runtime.model, 'deepseek-reasoner'); assert.equal(runtime.model, 'deepseek-v4-flash');
assert.equal(runtime.env.AIDER_MODEL, 'deepseek/deepseek-reasoner'); assert.equal(runtime.env.AIDER_MODEL, 'deepseek/deepseek-v4-flash');
assert.equal(runtime.env.DEEPSEEK_API_KEY, '[hidden]'); assert.equal(runtime.env.DEEPSEEK_API_KEY, '[hidden]');
}); });
@@ -522,8 +529,8 @@ test('buildExecutorLaunchPlan creates openhands headless command', () => {
executorLabel: 'OpenHands', executorLabel: 'OpenHands',
purpose: 'default', purpose: 'default',
providerName: 'DeepSeek 主账号', providerName: 'DeepSeek 主账号',
model: 'deepseek-reasoner', model: 'deepseek-v4-flash',
env: { LLM_MODEL: 'deepseek-reasoner', LLM_API_KEY: '[hidden]' }, env: { LLM_MODEL: 'deepseek-v4-flash', LLM_API_KEY: '[hidden]' },
}, { }, {
mode: 'headless', mode: 'headless',
cwd: '/repo/memind', cwd: '/repo/memind',
@@ -543,8 +550,8 @@ test('buildExecutorLaunchPlan creates openhands serve command', () => {
executorLabel: 'OpenHands', executorLabel: 'OpenHands',
purpose: 'default', purpose: 'default',
providerName: 'DeepSeek 主账号', providerName: 'DeepSeek 主账号',
model: 'deepseek-reasoner', model: 'deepseek-v4-flash',
env: { LLM_MODEL: 'deepseek-reasoner', LLM_API_KEY: '[hidden]' }, env: { LLM_MODEL: 'deepseek-v4-flash', LLM_API_KEY: '[hidden]' },
}, { }, {
mode: 'serve', mode: 'serve',
cwd: '/repo/memind', cwd: '/repo/memind',
@@ -561,9 +568,9 @@ test('buildExecutorLaunchPlan adds aider one-shot message when provided', () =>
executorLabel: 'Aider', executorLabel: 'Aider',
purpose: 'default', purpose: 'default',
providerName: 'DeepSeek 主账号', providerName: 'DeepSeek 主账号',
model: 'deepseek-chat', model: 'deepseek-v4-pro',
env: { env: {
AIDER_MODEL: 'deepseek/deepseek-chat', AIDER_MODEL: 'deepseek/deepseek-v4-pro',
OPENAI_API_KEY: '[hidden]', OPENAI_API_KEY: '[hidden]',
TKMIND_EXECUTOR_PROVIDER: 'custom_deepseek', TKMIND_EXECUTOR_PROVIDER: 'custom_deepseek',
}, },
@@ -573,7 +580,7 @@ test('buildExecutorLaunchPlan adds aider one-shot message when provided', () =>
}); });
assert.equal(plan.ok, true); assert.equal(plan.ok, true);
assert.equal(plan.command, 'aider'); assert.equal(plan.command, 'aider');
assert.deepEqual(plan.args.slice(0, 2), ['--model', 'deepseek/deepseek-chat']); assert.deepEqual(plan.args.slice(0, 2), ['--model', 'deepseek/deepseek-v4-pro']);
assert.ok(plan.args.includes('--message')); assert.ok(plan.args.includes('--message'));
assert.ok(plan.args.includes('fix login button')); assert.ok(plan.args.includes('fix login button'));
}); });
@@ -585,8 +592,8 @@ test('buildExecutorLaunchPlan rejects openhands headless without instruction', (
executorLabel: 'OpenHands', executorLabel: 'OpenHands',
purpose: 'default', purpose: 'default',
providerName: 'DeepSeek 主账号', providerName: 'DeepSeek 主账号',
model: 'deepseek-reasoner', model: 'deepseek-v4-flash',
env: { LLM_MODEL: 'deepseek-reasoner', LLM_API_KEY: '[hidden]' }, env: { LLM_MODEL: 'deepseek-v4-flash', LLM_API_KEY: '[hidden]' },
}, { }, {
mode: 'headless', mode: 'headless',
cwd: '/repo/memind', cwd: '/repo/memind',
@@ -637,7 +644,7 @@ test('launchExecutor returns friendly error when command is missing', async () =
api_key_ciphertext: encryptedKey.ciphertext, api_key_ciphertext: encryptedKey.ciphertext,
api_key_iv: encryptedKey.iv, api_key_iv: encryptedKey.iv,
api_key_tag: encryptedKey.tag, api_key_tag: encryptedKey.tag,
default_model: 'deepseek-chat', default_model: 'deepseek-v4-pro',
status: 'active', status: 'active',
is_selected: 1, is_selected: 1,
created_at: 1, created_at: 1,
@@ -648,7 +655,7 @@ test('launchExecutor returns friendly error when command is missing', async () =
executor: 'aider', executor: 'aider',
purpose: 'default', purpose: 'default',
provider_key_id: 'key-1', provider_key_id: 'key-1',
model: 'deepseek-chat', model: 'deepseek-v4-pro',
enabled: 1, enabled: 1,
created_at: 1, created_at: 1,
updated_at: 1, updated_at: 1,
@@ -735,7 +742,7 @@ test('syncProfileToGoosed writes provider, model and secret keys for builtin', a
{ {
providerKind: 'builtin', providerKind: 'builtin',
providerId: 'custom_deepseek', providerId: 'custom_deepseek',
defaultModel: 'deepseek-chat', defaultModel: 'deepseek-v4-pro',
apiKey: 'sk-test', apiKey: 'sk-test',
}, },
mockFetch, mockFetch,
@@ -755,14 +762,14 @@ test('syncSelectedToGoosed writes selected provider to every configured target',
name: 'DeepSeek', name: 'DeepSeek',
api_url: null, api_url: null,
base_path: null, base_path: null,
models_json: JSON.stringify(['deepseek-chat']), models_json: JSON.stringify(['deepseek-v4-pro']),
goosed_provider_id: null, goosed_provider_id: null,
engine: 'openai', engine: 'openai',
relay_provider: null, relay_provider: null,
api_key_ciphertext: encrypted.ciphertext, api_key_ciphertext: encrypted.ciphertext,
api_key_iv: encrypted.iv, api_key_iv: encrypted.iv,
api_key_tag: encrypted.tag, api_key_tag: encrypted.tag,
default_model: 'deepseek-chat', default_model: 'deepseek-v4-pro',
status: 'active', status: 'active',
is_selected: 1, is_selected: 1,
created_at: 1, created_at: 1,
+4 -4
View File
@@ -40,14 +40,14 @@ const CONFIG_NAME = 'DeepSeek 直连';
async function testDirectDeepSeek() { async function testDirectDeepSeek() {
const dispatcher = new Agent({ connect: { rejectUnauthorized: true } }); const dispatcher = new Agent({ connect: { rejectUnauthorized: true } });
const started = Date.now(); const started = Date.now();
const res = await fetch('https://api.deepseek.com/chat/completions', { const res = await fetch('https://api.deepseek.com/v1/chat/completions', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`, Authorization: `Bearer ${apiKey}`,
}, },
body: JSON.stringify({ body: JSON.stringify({
model: 'deepseek-chat', model: 'deepseek-v4-pro',
messages: [{ role: 'user', content: 'Hello' }], messages: [{ role: 'user', content: 'Hello' }],
stream: false, stream: false,
}), }),
@@ -78,7 +78,7 @@ if (rowId) {
const updated = await svc.updateKey(rowId, { const updated = await svc.updateKey(rowId, {
name: CONFIG_NAME, name: CONFIG_NAME,
apiKey, apiKey,
defaultModel: 'deepseek-chat', defaultModel: 'deepseek-v4-pro',
}); });
if (!updated.ok) { if (!updated.ok) {
console.error('更新 DeepSeek 配置失败:', updated.message); console.error('更新 DeepSeek 配置失败:', updated.message);
@@ -90,7 +90,7 @@ if (rowId) {
providerId: 'custom_deepseek', providerId: 'custom_deepseek',
name: CONFIG_NAME, name: CONFIG_NAME,
apiKey, apiKey,
defaultModel: 'deepseek-chat', defaultModel: 'deepseek-v4-pro',
}); });
if (!created.ok) { if (!created.ok) {
console.error('创建 DeepSeek 配置失败:', created.message); console.error('创建 DeepSeek 配置失败:', created.message);