mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-29 06:00:52 +02:00
fix: auto-detect deepseek profile in settings
This commit is contained in:
@@ -51,3 +51,12 @@ test('Eino 模型 retry/failover 设置项有中英文文案', () => {
|
||||
assert.ok(en.settingsBasic[key].length > 0, `en ${key} is empty`);
|
||||
});
|
||||
});
|
||||
|
||||
test('AI 通道保存前会自动识别 DeepSeek 官方线路', () => {
|
||||
assert.match(settings, /function\s+isOfficialDeepSeekBaseURL/);
|
||||
assert.match(settings, /api\.deepseek\.com/);
|
||||
assert.match(settings, /profile:\s*'deepseek'/);
|
||||
assert.match(settings, /normalizeAIChannelProviderProfile\(\{/);
|
||||
assert.match(settings, /normalizeAIConfigProviderProfiles\(currentConfig\.ai\)/);
|
||||
assert.match(template, /<option value="deepseek">deepseek<\/option>/);
|
||||
});
|
||||
|
||||
@@ -1985,6 +1985,7 @@ async function applySettings() {
|
||||
const activeChannelId = normalizeAIChannelId(selectedAIChannelId || currentConfig.ai.default_channel || 'default');
|
||||
currentConfig.ai.channels[activeChannelId] = readAIChannelFromMainForm(activeChannelId);
|
||||
currentConfig.ai.default_channel = activeChannelId;
|
||||
currentConfig.ai = normalizeAIConfigProviderProfiles(currentConfig.ai);
|
||||
renderAIChannelSelect();
|
||||
const activeChannel = currentConfig.ai.channels[activeChannelId] || {};
|
||||
const prevOpenai = activeChannel;
|
||||
@@ -1999,7 +2000,7 @@ async function applySettings() {
|
||||
return String(s || '').split(/[\n,,]/).map(v => v.trim()).filter(Boolean);
|
||||
};
|
||||
const config = {
|
||||
ai: currentConfig.ai,
|
||||
ai: normalizeAIConfigProviderProfiles(currentConfig.ai),
|
||||
vision: visionPayload,
|
||||
fofa: {
|
||||
api_key: document.getElementById('fofa-api-key')?.value.trim() || '',
|
||||
@@ -2533,6 +2534,43 @@ function normalizeAIChannelId(name) {
|
||||
return id || 'default';
|
||||
}
|
||||
|
||||
function aiChannelBaseURLHost(baseUrl) {
|
||||
const raw = String(baseUrl || '').trim();
|
||||
if (!raw) return '';
|
||||
try {
|
||||
return new URL(raw).hostname.toLowerCase().replace(/^www\./, '');
|
||||
} catch (e) {
|
||||
try {
|
||||
return new URL(`https://${raw.replace(/^\/+/, '')}`).hostname.toLowerCase().replace(/^www\./, '');
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isOfficialDeepSeekBaseURL(baseUrl) {
|
||||
return aiChannelBaseURLHost(baseUrl) === 'api.deepseek.com';
|
||||
}
|
||||
|
||||
function normalizeAIChannelProviderProfile(channel) {
|
||||
if (!channel || typeof channel !== 'object') return channel;
|
||||
if (isOfficialDeepSeekBaseURL(channel.base_url)) {
|
||||
channel.reasoning = {
|
||||
...(channel.reasoning || {}),
|
||||
profile: 'deepseek'
|
||||
};
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
function normalizeAIConfigProviderProfiles(ai) {
|
||||
if (!ai || typeof ai !== 'object' || !ai.channels || typeof ai.channels !== 'object') return ai;
|
||||
Object.keys(ai.channels).forEach((id) => {
|
||||
ai.channels[id] = normalizeAIChannelProviderProfile(ai.channels[id] || {});
|
||||
});
|
||||
return ai;
|
||||
}
|
||||
|
||||
function escapeAIChannelHtml(value) {
|
||||
return String(value == null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
@@ -2559,13 +2597,13 @@ function ensureAIConfigShape(cfg) {
|
||||
reasoning: oa.reasoning || {}
|
||||
};
|
||||
}
|
||||
return { default_channel: def, channels };
|
||||
return normalizeAIConfigProviderProfiles({ default_channel: def, channels });
|
||||
}
|
||||
|
||||
function readAIChannelFromMainForm(id) {
|
||||
const prev = currentConfig?.ai?.channels?.[id] || {};
|
||||
const maxCompletionTokens = parseInt(document.getElementById('openai-max-completion-tokens')?.value, 10) || 32768;
|
||||
return {
|
||||
return normalizeAIChannelProviderProfile({
|
||||
...prev,
|
||||
name: (document.getElementById('ai-channel-name')?.value || '').trim() || prev.name || id,
|
||||
provider: document.getElementById('openai-provider')?.value || 'openai',
|
||||
@@ -2581,7 +2619,7 @@ function readAIChannelFromMainForm(id) {
|
||||
profile: document.getElementById('openai-reasoning-profile')?.value || 'auto',
|
||||
allow_client_reasoning: document.getElementById('openai-reasoning-allow-client')?.checked !== false
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function writeAIChannelToMainForm(id) {
|
||||
@@ -2611,7 +2649,7 @@ function writeAIChannelToMainForm(id) {
|
||||
const effEl = document.getElementById('openai-reasoning-effort');
|
||||
if (effEl) effEl.value = ['', 'low', 'medium', 'high', 'max', 'xhigh'].includes(String(r.effort || '').toLowerCase()) ? String(r.effort || '').toLowerCase() : '';
|
||||
const profileEl = document.getElementById('openai-reasoning-profile');
|
||||
if (profileEl) profileEl.value = ['auto', 'deepseek_compat', 'openai_compat', 'output_config_effort'].includes(String(r.profile || '').toLowerCase()) ? String(r.profile || '').toLowerCase() : 'auto';
|
||||
if (profileEl) profileEl.value = ['auto', 'deepseek', 'deepseek_compat', 'openai_compat', 'output_config_effort'].includes(String(r.profile || '').toLowerCase()) ? String(r.profile || '').toLowerCase() : 'auto';
|
||||
const allowEl = document.getElementById('openai-reasoning-allow-client');
|
||||
if (allowEl) allowEl.checked = r.allow_client_reasoning !== false;
|
||||
syncModelListFetchButtons();
|
||||
@@ -2943,6 +2981,7 @@ async function persistAIChannelsToServer(successMessage, options = {}) {
|
||||
currentConfig.ai.default_channel = latestAI.default_channel || id;
|
||||
}
|
||||
}
|
||||
currentConfig.ai = normalizeAIConfigProviderProfiles(currentConfig.ai);
|
||||
const updateResponse = await apiFetch('/api/config', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -2971,6 +3010,7 @@ async function persistAIConfigOnlyToServer(successMessage) {
|
||||
if (typeof requirePermission === 'function' && !requirePermission('config:write')) return false;
|
||||
if (!currentConfig) return false;
|
||||
currentConfig.ai = ensureAIConfigShape(currentConfig);
|
||||
currentConfig.ai = normalizeAIConfigProviderProfiles(currentConfig.ai);
|
||||
showAIChannelSaveHint(settingsT('settingsBasic.aiChannelSaving', '正在保存通道...'), true);
|
||||
try {
|
||||
const updateResponse = await apiFetch('/api/config', {
|
||||
|
||||
Reference in New Issue
Block a user