mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-26 19:21:52 +02:00
Add files via upload
This commit is contained in:
@@ -4739,6 +4739,10 @@ html[data-theme="dark"] .new-chat-btn:focus-visible {
|
|||||||
color: #10b981;
|
color: #10b981;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-channel-status-label.complete {
|
||||||
|
color: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
.ai-channel-status-label.testing {
|
.ai-channel-status-label.testing {
|
||||||
color: #3b82f6;
|
color: #3b82f6;
|
||||||
}
|
}
|
||||||
@@ -4760,6 +4764,10 @@ html[data-theme="dark"] .new-chat-btn:focus-visible {
|
|||||||
background: #10b981;
|
background: #10b981;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-channel-status-dot.complete {
|
||||||
|
background: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
.ai-channel-status-dot.draft {
|
.ai-channel-status-dot.draft {
|
||||||
background: #f59e0b;
|
background: #f59e0b;
|
||||||
}
|
}
|
||||||
@@ -4808,18 +4816,60 @@ html[data-theme="dark"] .new-chat-btn:focus-visible {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ai-channel-editor-head h5 {
|
.ai-channel-editor-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text-color, #2d3748);
|
|
||||||
font-size: 21px;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai-channel-editor-head p {
|
|
||||||
margin: 4px 0 0;
|
|
||||||
color: var(--text-muted, #718096);
|
color: var(--text-muted, #718096);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-channel-editor-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-channel-editor-chip {
|
||||||
|
max-width: 220px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--border-color, #e2e8f0) 70%, transparent);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: color-mix(in srgb, var(--card-bg, #fff) 92%, var(--primary-color, #3182ce));
|
||||||
|
color: var(--text-muted, #718096);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 7px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-channel-editor-chip.default,
|
||||||
|
.ai-channel-editor-chip.complete {
|
||||||
|
border-color: rgba(49, 130, 206, 0.22);
|
||||||
|
background: rgba(49, 130, 206, 0.1);
|
||||||
|
color: var(--primary-color, #3182ce);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-channel-editor-chip.ready {
|
||||||
|
border-color: rgba(16, 185, 129, 0.22);
|
||||||
|
background: rgba(16, 185, 129, 0.1);
|
||||||
|
color: #059669;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-channel-editor-chip.testing {
|
||||||
|
border-color: rgba(214, 158, 46, 0.24);
|
||||||
|
background: rgba(214, 158, 46, 0.1);
|
||||||
|
color: #b7791f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ai-channel-editor-chip.failed,
|
||||||
|
.ai-channel-editor-chip.draft {
|
||||||
|
border-color: rgba(229, 62, 62, 0.2);
|
||||||
|
background: rgba(229, 62, 62, 0.08);
|
||||||
|
color: var(--error-color, #e53e3e);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ai-channel-editor-actions {
|
.ai-channel-editor-actions {
|
||||||
@@ -5121,6 +5171,38 @@ html[data-theme="dark"] .ai-channel-editor-head {
|
|||||||
border-bottom-color: rgba(71, 85, 105, 0.34);
|
border-bottom-color: rgba(71, 85, 105, 0.34);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .ai-channel-editor-chip {
|
||||||
|
border-color: rgba(71, 85, 105, 0.46);
|
||||||
|
background: rgba(15, 23, 42, 0.7);
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .ai-channel-editor-chip.default,
|
||||||
|
html[data-theme="dark"] .ai-channel-editor-chip.complete {
|
||||||
|
border-color: rgba(96, 165, 250, 0.34);
|
||||||
|
background: rgba(96, 165, 250, 0.12);
|
||||||
|
color: #93c5fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .ai-channel-editor-chip.ready {
|
||||||
|
border-color: rgba(52, 211, 153, 0.28);
|
||||||
|
background: rgba(52, 211, 153, 0.1);
|
||||||
|
color: #6ee7b7;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .ai-channel-editor-chip.testing {
|
||||||
|
border-color: rgba(251, 191, 36, 0.28);
|
||||||
|
background: rgba(251, 191, 36, 0.1);
|
||||||
|
color: #fbbf24;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .ai-channel-editor-chip.failed,
|
||||||
|
html[data-theme="dark"] .ai-channel-editor-chip.draft {
|
||||||
|
border-color: rgba(248, 113, 113, 0.28);
|
||||||
|
background: rgba(248, 113, 113, 0.1);
|
||||||
|
color: #fca5a5;
|
||||||
|
}
|
||||||
|
|
||||||
html[data-theme="dark"] .ai-channel-form-section,
|
html[data-theme="dark"] .ai-channel-form-section,
|
||||||
html[data-theme="dark"] .ai-channel-advanced-section {
|
html[data-theme="dark"] .ai-channel-advanced-section {
|
||||||
border-color: rgba(71, 85, 105, 0.34);
|
border-color: rgba(71, 85, 105, 0.34);
|
||||||
@@ -5238,6 +5320,10 @@ html[data-theme="dark"] .ai-channel-editor-form select {
|
|||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-channel-editor-meta {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.ai-channel-switcher {
|
.ai-channel-switcher {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -2607,12 +2607,15 @@
|
|||||||
"aiChannelSavedList": "Saved channels",
|
"aiChannelSavedList": "Saved channels",
|
||||||
"aiChannelListAria": "AI channel list",
|
"aiChannelListAria": "AI channel list",
|
||||||
"aiChannelEditing": "Editing",
|
"aiChannelEditing": "Editing",
|
||||||
|
"aiChannelFormContext": "Editing the selected channel",
|
||||||
|
"aiChannelFormContextHint": "Saving the form updates this channel configuration.",
|
||||||
"aiChannelBulkProbe": "Probe all",
|
"aiChannelBulkProbe": "Probe all",
|
||||||
"aiChannelBulkProbeTitle": "Check whether all complete channels are available",
|
"aiChannelBulkProbeTitle": "Check whether all complete channels are available",
|
||||||
"aiChannelSelectAria": "Select {name}",
|
"aiChannelSelectAria": "Select {name}",
|
||||||
"aiChannelDefaultBadge": "Default",
|
"aiChannelDefaultBadge": "Default",
|
||||||
"aiChannelReady": "Ready",
|
"aiChannelReady": "Ready",
|
||||||
"aiChannelReadyWithLatency": "Ready{latency}",
|
"aiChannelReadyWithLatency": "Ready{latency}",
|
||||||
|
"aiChannelComplete": "Complete",
|
||||||
"aiChannelDraft": "Incomplete",
|
"aiChannelDraft": "Incomplete",
|
||||||
"aiChannelDefaultMeta": "Default channel",
|
"aiChannelDefaultMeta": "Default channel",
|
||||||
"aiChannelCustomMeta": "Custom channel",
|
"aiChannelCustomMeta": "Custom channel",
|
||||||
@@ -2812,7 +2815,7 @@
|
|||||||
"visionTimeout": "Timeout (seconds)",
|
"visionTimeout": "Timeout (seconds)",
|
||||||
"visionTestFillRequired": "Enter vision model and ensure API Key is available (or reuse OpenAI)",
|
"visionTestFillRequired": "Enter vision model and ensure API Key is available (or reuse OpenAI)",
|
||||||
"testConnection": "Test Connection",
|
"testConnection": "Test Connection",
|
||||||
"testFillRequired": "Please fill in API Key and Model first",
|
"testFillRequired": "Please fill in Base URL, API Key, and Model first",
|
||||||
"testing": "Testing connection...",
|
"testing": "Testing connection...",
|
||||||
"testSuccess": "Connection successful",
|
"testSuccess": "Connection successful",
|
||||||
"testFailed": "Connection failed",
|
"testFailed": "Connection failed",
|
||||||
|
|||||||
@@ -2595,12 +2595,15 @@
|
|||||||
"aiChannelSavedList": "已保存通道",
|
"aiChannelSavedList": "已保存通道",
|
||||||
"aiChannelListAria": "AI 通道列表",
|
"aiChannelListAria": "AI 通道列表",
|
||||||
"aiChannelEditing": "正在编辑",
|
"aiChannelEditing": "正在编辑",
|
||||||
|
"aiChannelFormContext": "编辑当前选择的通道",
|
||||||
|
"aiChannelFormContextHint": "表单保存后会更新该通道配置。",
|
||||||
"aiChannelBulkProbe": "批量探活",
|
"aiChannelBulkProbe": "批量探活",
|
||||||
"aiChannelBulkProbeTitle": "检测全部完整通道是否可用",
|
"aiChannelBulkProbeTitle": "检测全部完整通道是否可用",
|
||||||
"aiChannelSelectAria": "选择 {name}",
|
"aiChannelSelectAria": "选择 {name}",
|
||||||
"aiChannelDefaultBadge": "默认",
|
"aiChannelDefaultBadge": "默认",
|
||||||
"aiChannelReady": "可用",
|
"aiChannelReady": "可用",
|
||||||
"aiChannelReadyWithLatency": "可用{latency}",
|
"aiChannelReadyWithLatency": "可用{latency}",
|
||||||
|
"aiChannelComplete": "配置完整",
|
||||||
"aiChannelDraft": "待完善",
|
"aiChannelDraft": "待完善",
|
||||||
"aiChannelDefaultMeta": "默认通道",
|
"aiChannelDefaultMeta": "默认通道",
|
||||||
"aiChannelCustomMeta": "自定义通道",
|
"aiChannelCustomMeta": "自定义通道",
|
||||||
@@ -2800,7 +2803,7 @@
|
|||||||
"visionTimeout": "超时(秒)",
|
"visionTimeout": "超时(秒)",
|
||||||
"visionTestFillRequired": "请填写视觉模型,并确保 API Key 可用(可复用 OpenAI)",
|
"visionTestFillRequired": "请填写视觉模型,并确保 API Key 可用(可复用 OpenAI)",
|
||||||
"testConnection": "测试连接",
|
"testConnection": "测试连接",
|
||||||
"testFillRequired": "请先填写 API Key 和模型",
|
"testFillRequired": "请先填写 Base URL、API Key 和模型",
|
||||||
"testing": "测试中...",
|
"testing": "测试中...",
|
||||||
"testSuccess": "连接成功",
|
"testSuccess": "连接成功",
|
||||||
"testFailed": "连接失败",
|
"testFailed": "连接失败",
|
||||||
|
|||||||
+106
-26
@@ -2571,6 +2571,7 @@ function writeAIChannelToMainForm(id) {
|
|||||||
if (allowEl) allowEl.checked = r.allow_client_reasoning !== false;
|
if (allowEl) allowEl.checked = r.allow_client_reasoning !== false;
|
||||||
syncModelListFetchButtons();
|
syncModelListFetchButtons();
|
||||||
syncAIChannelEditorPreview();
|
syncAIChannelEditorPreview();
|
||||||
|
syncConnectionTestResultForSelectedAIChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayAIChannelName(id, ch) {
|
function displayAIChannelName(id, ch) {
|
||||||
@@ -2618,8 +2619,10 @@ function updateAIChannelSelectOption(id) {
|
|||||||
delete opt.dataset.probeStatus;
|
delete opt.dataset.probeStatus;
|
||||||
delete opt.dataset.probeMessage;
|
delete opt.dataset.probeMessage;
|
||||||
}
|
}
|
||||||
select.value = channelId;
|
if (channelId === selectedAIChannelId) {
|
||||||
select.selectedIndex = opt.index;
|
select.value = channelId;
|
||||||
|
select.selectedIndex = opt.index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (typeof syncSettingsCustomSelect === 'function') {
|
if (typeof syncSettingsCustomSelect === 'function') {
|
||||||
syncSettingsCustomSelect(select);
|
syncSettingsCustomSelect(select);
|
||||||
@@ -2630,12 +2633,18 @@ function syncSelectedAIChannelUI() {
|
|||||||
updateAIChannelSelectOption(selectedAIChannelId);
|
updateAIChannelSelectOption(selectedAIChannelId);
|
||||||
updateAIChannelEditorChrome(selectedAIChannelId);
|
updateAIChannelEditorChrome(selectedAIChannelId);
|
||||||
renderAIChannelList();
|
renderAIChannelList();
|
||||||
|
syncConnectionTestResultForSelectedAIChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncAIChannelEditorPreview() {
|
function syncAIChannelEditorPreview() {
|
||||||
if (!currentConfig?.ai?.channels || !selectedAIChannelId || !currentConfig.ai.channels[selectedAIChannelId]) return;
|
if (!currentConfig?.ai?.channels || !selectedAIChannelId || !currentConfig.ai.channels[selectedAIChannelId]) return;
|
||||||
const id = normalizeAIChannelId(selectedAIChannelId);
|
const id = normalizeAIChannelId(selectedAIChannelId);
|
||||||
|
const prev = currentConfig.ai.channels[id] || {};
|
||||||
const next = readAIChannelFromMainForm(id);
|
const next = readAIChannelFromMainForm(id);
|
||||||
|
const connectionChanged = ['provider', 'base_url', 'api_key', 'model'].some((key) => String(prev[key] || '') !== String(next[key] || ''));
|
||||||
|
if (connectionChanged) {
|
||||||
|
delete aiChannelProbeResults[id];
|
||||||
|
}
|
||||||
currentConfig.ai.channels[id] = next;
|
currentConfig.ai.channels[id] = next;
|
||||||
syncSelectedAIChannelUI();
|
syncSelectedAIChannelUI();
|
||||||
}
|
}
|
||||||
@@ -2644,6 +2653,7 @@ function bindAIChannelEditorPreviewSync() {
|
|||||||
const ids = [
|
const ids = [
|
||||||
'ai-channel-name',
|
'ai-channel-name',
|
||||||
'openai-provider',
|
'openai-provider',
|
||||||
|
'openai-api-key',
|
||||||
'openai-base-url',
|
'openai-base-url',
|
||||||
'openai-model'
|
'openai-model'
|
||||||
];
|
];
|
||||||
@@ -2739,9 +2749,9 @@ function renderAIChannelList(ids) {
|
|||||||
const displayName = displayAIChannelName(id, ch);
|
const displayName = displayAIChannelName(id, ch);
|
||||||
const defaultBadge = isDefault ? `<span class="ai-channel-badge">${escapeAIChannelHtml(settingsT('settingsBasic.aiChannelDefaultBadge', '默认'))}</span>` : '';
|
const defaultBadge = isDefault ? `<span class="ai-channel-badge">${escapeAIChannelHtml(settingsT('settingsBasic.aiChannelDefaultBadge', '默认'))}</span>` : '';
|
||||||
let statusText = isComplete
|
let statusText = isComplete
|
||||||
? settingsT('settingsBasic.aiChannelReady', '可用')
|
? settingsT('settingsBasic.aiChannelComplete', '配置完整')
|
||||||
: settingsT('settingsBasic.aiChannelDraft', '待完善');
|
: settingsT('settingsBasic.aiChannelDraft', '待完善');
|
||||||
let statusClass = isComplete ? 'ready' : 'draft';
|
let statusClass = isComplete ? 'complete' : 'draft';
|
||||||
if (probe) {
|
if (probe) {
|
||||||
statusText = probe.message || statusText;
|
statusText = probe.message || statusText;
|
||||||
statusClass = probe.status || statusClass;
|
statusClass = probe.status || statusClass;
|
||||||
@@ -2778,19 +2788,36 @@ function updateAIChannelEditorChrome(id) {
|
|||||||
const ai = ensureAIConfigShape(currentConfig || {});
|
const ai = ensureAIConfigShape(currentConfig || {});
|
||||||
const channelId = normalizeAIChannelId(id || ai.default_channel || 'default');
|
const channelId = normalizeAIChannelId(id || ai.default_channel || 'default');
|
||||||
const ch = ai.channels[channelId] || {};
|
const ch = ai.channels[channelId] || {};
|
||||||
|
const isDefault = channelId === ai.default_channel;
|
||||||
|
const isComplete = !validateSelectedAIChannelPayload(ch);
|
||||||
|
const probe = aiChannelProbeResults[channelId] || null;
|
||||||
const title = document.getElementById('ai-channel-editor-title');
|
const title = document.getElementById('ai-channel-editor-title');
|
||||||
const meta = document.getElementById('ai-channel-editor-meta');
|
const meta = document.getElementById('ai-channel-editor-meta');
|
||||||
if (title) title.textContent = displayAIChannelName(channelId, ch);
|
if (title) {
|
||||||
|
title.textContent = settingsT('settingsBasic.aiChannelFormContextHint', '表单保存后会更新该通道配置。');
|
||||||
|
}
|
||||||
if (meta) {
|
if (meta) {
|
||||||
const parts = [
|
const provider = ch.provider === 'claude' ? 'Claude' : settingsT('settingsBasic.aiChannelOpenAICompat', 'OpenAI 兼容');
|
||||||
channelId === ai.default_channel
|
const statusText = probe?.message || (isComplete
|
||||||
? settingsT('settingsBasic.aiChannelDefaultMeta', '默认通道')
|
? settingsT('settingsBasic.aiChannelComplete', '配置完整')
|
||||||
: settingsT('settingsBasic.aiChannelCustomMeta', '自定义通道'),
|
: settingsT('settingsBasic.aiChannelDraft', '待完善'));
|
||||||
ch.provider === 'claude' ? 'Claude' : settingsT('settingsBasic.aiChannelOpenAICompat', 'OpenAI 兼容'),
|
const statusClass = probe?.status || (isComplete ? 'complete' : 'draft');
|
||||||
ch.model || settingsT('settingsBasic.aiChannelModelMissing', '未填写模型'),
|
const chips = [
|
||||||
channelHostLabel(ch.base_url)
|
{
|
||||||
].filter(Boolean);
|
label: isDefault
|
||||||
meta.textContent = parts.join(' / ');
|
? settingsT('settingsBasic.aiChannelDefaultMeta', '默认通道')
|
||||||
|
: settingsT('settingsBasic.aiChannelCustomMeta', '自定义通道'),
|
||||||
|
className: isDefault ? 'default' : ''
|
||||||
|
},
|
||||||
|
{ label: provider },
|
||||||
|
{ label: ch.model || settingsT('settingsBasic.aiChannelModelMissing', '未填写模型') },
|
||||||
|
{ label: channelHostLabel(ch.base_url) },
|
||||||
|
{ label: statusText, className: statusClass }
|
||||||
|
].filter((chip) => chip.label);
|
||||||
|
meta.innerHTML = chips.map((chip) => {
|
||||||
|
const className = chip.className ? ` ${escapeAIChannelHtml(chip.className)}` : '';
|
||||||
|
return `<span class="ai-channel-editor-chip${className}" title="${escapeAIChannelHtml(chip.label)}">${escapeAIChannelHtml(chip.label)}</span>`;
|
||||||
|
}).join('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3536,10 +3563,10 @@ async function testHitlAuditModelConnection() {
|
|||||||
const resultEl = document.getElementById('test-hitl-audit-model-result');
|
const resultEl = document.getElementById('test-hitl-audit-model-result');
|
||||||
const cfg = collectHitlAuditModelEffectiveConfig();
|
const cfg = collectHitlAuditModelEffectiveConfig();
|
||||||
|
|
||||||
if (!cfg.api_key || !cfg.model) {
|
if (!cfg.base_url || !cfg.api_key || !cfg.model) {
|
||||||
if (resultEl) {
|
if (resultEl) {
|
||||||
resultEl.style.color = 'var(--danger-color, #e53e3e)';
|
resultEl.style.color = 'var(--danger-color, #e53e3e)';
|
||||||
resultEl.textContent = typeof window.t === 'function' ? window.t('settingsBasic.testFillRequired') : '请先填写 API Key 和模型';
|
resultEl.textContent = typeof window.t === 'function' ? window.t('settingsBasic.testFillRequired') : '请先填写 Base URL、API Key 和模型';
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3604,6 +3631,29 @@ function setConnectionTestResult(resultEl, state, message, title) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncConnectionTestResultForSelectedAIChannel() {
|
||||||
|
const resultEl = document.getElementById('test-openai-result');
|
||||||
|
if (!resultEl) return;
|
||||||
|
const channelId = normalizeAIChannelId(selectedAIChannelId || currentConfig?.ai?.default_channel || 'default');
|
||||||
|
const probe = aiChannelProbeResults[channelId];
|
||||||
|
if (!probe) {
|
||||||
|
setConnectionTestResult(resultEl, '', '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const state = probe.status === 'ready'
|
||||||
|
? 'is-success'
|
||||||
|
: probe.status === 'failed'
|
||||||
|
? 'is-error'
|
||||||
|
: 'is-muted';
|
||||||
|
let message = probe.message || '';
|
||||||
|
const fillRequiredMessage = settingsT('settingsBasic.testFillRequired', '请先填写 Base URL、API Key 和模型');
|
||||||
|
const failedPrefix = (typeof window.t === 'function' ? window.t('settingsBasic.testFailed') : '连接失败') + ': ';
|
||||||
|
if (probe.status === 'failed' && message && message !== fillRequiredMessage && !message.startsWith(failedPrefix)) {
|
||||||
|
message = failedPrefix + message;
|
||||||
|
}
|
||||||
|
setConnectionTestResult(resultEl, state, message);
|
||||||
|
}
|
||||||
|
|
||||||
function showConnectionTestFailure(resultEl, errorText) {
|
function showConnectionTestFailure(resultEl, errorText) {
|
||||||
if (!resultEl) return;
|
if (!resultEl) return;
|
||||||
const formatted = formatConnectionTestError(errorText);
|
const formatted = formatConnectionTestError(errorText);
|
||||||
@@ -3624,15 +3674,29 @@ async function testOpenAIConnection() {
|
|||||||
const baseUrl = document.getElementById('openai-base-url').value.trim();
|
const baseUrl = document.getElementById('openai-base-url').value.trim();
|
||||||
const apiKey = document.getElementById('openai-api-key').value.trim();
|
const apiKey = document.getElementById('openai-api-key').value.trim();
|
||||||
const model = document.getElementById('openai-model').value.trim();
|
const model = document.getElementById('openai-model').value.trim();
|
||||||
|
const channelId = normalizeAIChannelId(selectedAIChannelId || currentConfig?.ai?.default_channel || 'default');
|
||||||
|
const isTestingSameChannel = () => normalizeAIChannelId(selectedAIChannelId || currentConfig?.ai?.default_channel || 'default') === channelId;
|
||||||
|
|
||||||
if (!apiKey || !model) {
|
if (!baseUrl || !apiKey || !model) {
|
||||||
setConnectionTestResult(resultEl, 'is-error', typeof window.t === 'function' ? window.t('settingsBasic.testFillRequired') : '请先填写 API Key 和模型');
|
const message = typeof window.t === 'function' ? window.t('settingsBasic.testFillRequired') : '请先填写 Base URL、API Key 和模型';
|
||||||
|
aiChannelProbeResults[channelId] = { status: 'failed', message };
|
||||||
|
if (isTestingSameChannel()) {
|
||||||
|
setConnectionTestResult(resultEl, 'is-error', message);
|
||||||
|
}
|
||||||
|
syncSelectedAIChannelUI();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.style.pointerEvents = 'none';
|
if (btn) {
|
||||||
btn.style.opacity = '0.5';
|
btn.style.pointerEvents = 'none';
|
||||||
setConnectionTestResult(resultEl, 'is-muted', typeof window.t === 'function' ? window.t('settingsBasic.testing') : '测试中...');
|
btn.style.opacity = '0.5';
|
||||||
|
}
|
||||||
|
const testingMessage = settingsT('settingsBasic.testing', '测试中...');
|
||||||
|
aiChannelProbeResults[channelId] = { status: 'testing', message: testingMessage };
|
||||||
|
if (isTestingSameChannel()) {
|
||||||
|
setConnectionTestResult(resultEl, 'is-muted', testingMessage);
|
||||||
|
}
|
||||||
|
syncSelectedAIChannelUI();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch('/api/config/test-openai', {
|
const response = await apiFetch('/api/config/test-openai', {
|
||||||
@@ -3651,15 +3715,31 @@ async function testOpenAIConnection() {
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
const latency = result.latency_ms ? ` (${result.latency_ms}ms)` : '';
|
const latency = result.latency_ms ? ` (${result.latency_ms}ms)` : '';
|
||||||
const modelInfo = result.model ? ` [${result.model}]` : '';
|
const modelInfo = result.model ? ` [${result.model}]` : '';
|
||||||
setConnectionTestResult(resultEl, 'is-success', (typeof window.t === 'function' ? window.t('settingsBasic.testSuccess') : '连接成功') + modelInfo + latency);
|
const message = (typeof window.t === 'function' ? window.t('settingsBasic.testSuccess') : '连接成功') + modelInfo + latency;
|
||||||
|
aiChannelProbeResults[channelId] = { status: 'ready', message };
|
||||||
|
if (isTestingSameChannel()) {
|
||||||
|
setConnectionTestResult(resultEl, 'is-success', message);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showConnectionTestFailure(resultEl, result.error || '未知错误');
|
const message = formatConnectionTestError(result.error || '未知错误').message;
|
||||||
|
aiChannelProbeResults[channelId] = { status: 'failed', message };
|
||||||
|
if (isTestingSameChannel()) {
|
||||||
|
showConnectionTestFailure(resultEl, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showConnectionTestFailure(resultEl, error.message || '测试出错');
|
const message = formatConnectionTestError(error.message || '测试出错').message;
|
||||||
|
aiChannelProbeResults[channelId] = { status: 'failed', message };
|
||||||
|
if (isTestingSameChannel()) {
|
||||||
|
showConnectionTestFailure(resultEl, message);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
btn.style.pointerEvents = '';
|
updateAIChannelSelectOption(channelId);
|
||||||
btn.style.opacity = '';
|
syncSelectedAIChannelUI();
|
||||||
|
if (btn) {
|
||||||
|
btn.style.pointerEvents = '';
|
||||||
|
btn.style.opacity = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3470,10 +3470,10 @@
|
|||||||
<div class="ai-channel-editor">
|
<div class="ai-channel-editor">
|
||||||
<div class="ai-channel-editor-head">
|
<div class="ai-channel-editor-head">
|
||||||
<div>
|
<div>
|
||||||
<span class="ai-channel-editor-kicker" data-i18n="settingsBasic.aiChannelEditing">正在编辑</span>
|
<span class="ai-channel-editor-kicker" data-i18n="settingsBasic.aiChannelFormContext">编辑当前选择的通道</span>
|
||||||
<h5 id="ai-channel-editor-title">-</h5>
|
<p id="ai-channel-editor-title" class="ai-channel-editor-title">-</p>
|
||||||
<p id="ai-channel-editor-meta">-</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="ai-channel-editor-meta" class="ai-channel-editor-meta" aria-live="polite"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-form ai-channel-editor-form">
|
<div class="settings-form ai-channel-editor-form">
|
||||||
<section class="ai-channel-form-section">
|
<section class="ai-channel-form-section">
|
||||||
|
|||||||
Reference in New Issue
Block a user