mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-06-18 20:10:13 +02:00
Add files via upload
This commit is contained in:
@@ -1939,6 +1939,13 @@
|
||||
"openaiBaseUrlPlaceholder": "https://api.openai.com/v1",
|
||||
"openaiApiKeyPlaceholder": "Enter OpenAI API Key",
|
||||
"modelPlaceholder": "gpt-4",
|
||||
"fetchModels": "Fetch list",
|
||||
"modelsListFetching": "Fetching model list...",
|
||||
"modelsListSelectPlaceholder": "Select a model",
|
||||
"modelsListSuccess": "Loaded {count} models — use the dropdown on the right, or type in the input",
|
||||
"modelsListFailed": "Failed to fetch model list",
|
||||
"modelsListNeedApiKey": "Please enter API Key first",
|
||||
"modelsListClaudeHint": "Claude does not support auto model list; enter the model name manually",
|
||||
"maxTotalTokens": "Max Context Tokens",
|
||||
"maxTotalTokensPlaceholder": "120000",
|
||||
"maxTotalTokensHint": "Shared by memory compression and attack chain building. Default: 120000",
|
||||
|
||||
@@ -1927,6 +1927,13 @@
|
||||
"openaiBaseUrlPlaceholder": "https://api.openai.com/v1",
|
||||
"openaiApiKeyPlaceholder": "输入OpenAI API Key",
|
||||
"modelPlaceholder": "gpt-4",
|
||||
"fetchModels": "获取列表",
|
||||
"modelsListFetching": "正在获取模型列表...",
|
||||
"modelsListSelectPlaceholder": "请选择模型",
|
||||
"modelsListSuccess": "已加载 {count} 个模型,请用右侧下拉框选择,或继续在左侧输入",
|
||||
"modelsListFailed": "获取模型列表失败",
|
||||
"modelsListNeedApiKey": "请先填写 API Key",
|
||||
"modelsListClaudeHint": "Claude 不支持自动获取模型列表,请手动填写",
|
||||
"maxTotalTokens": "最大上下文 Token 数",
|
||||
"maxTotalTokensPlaceholder": "120000",
|
||||
"maxTotalTokensHint": "内存压缩和攻击链构建共用此配置,默认 120000",
|
||||
|
||||
+207
-1
@@ -299,6 +299,7 @@ async function loadConfig(loadTools = true) {
|
||||
}
|
||||
|
||||
fillVisionConfigFromCurrent(currentConfig.vision || {});
|
||||
initModelListControls();
|
||||
|
||||
// 填充FOFA配置
|
||||
const fofa = currentConfig.fofa || {};
|
||||
@@ -1569,9 +1570,214 @@ function syncVisionFormEnabled() {
|
||||
if (panel) {
|
||||
panel.style.opacity = enabled ? '1' : '0.55';
|
||||
panel.querySelectorAll('input, select, textarea, a').forEach(el => {
|
||||
if (el.id === 'test-vision-btn') return;
|
||||
if (el.id === 'test-vision-btn' || el.id === 'fetch-vision-models-btn' || el.id === 'vision-model-select') return;
|
||||
el.disabled = !enabled;
|
||||
});
|
||||
syncModelListFetchButtons();
|
||||
}
|
||||
}
|
||||
|
||||
function initModelListControls() {
|
||||
const providerEl = document.getElementById('openai-provider');
|
||||
if (providerEl && !providerEl.dataset.modelListBound) {
|
||||
providerEl.dataset.modelListBound = '1';
|
||||
providerEl.addEventListener('change', syncModelListFetchButtons);
|
||||
}
|
||||
const visionProv = document.getElementById('vision-provider');
|
||||
if (visionProv && !visionProv.dataset.modelListBound) {
|
||||
visionProv.dataset.modelListBound = '1';
|
||||
visionProv.addEventListener('change', syncModelListFetchButtons);
|
||||
}
|
||||
bindModelSelect('openai');
|
||||
bindModelSelect('vision');
|
||||
syncModelListFetchButtons();
|
||||
}
|
||||
|
||||
function modelSelectIds(scope) {
|
||||
if (scope === 'vision') {
|
||||
return { selectId: 'vision-model-select', inputId: 'vision-model' };
|
||||
}
|
||||
return { selectId: 'openai-model-select', inputId: 'openai-model' };
|
||||
}
|
||||
|
||||
function bindModelSelect(scope) {
|
||||
const { selectId, inputId } = modelSelectIds(scope);
|
||||
const select = document.getElementById(selectId);
|
||||
if (!select || select.dataset.bound) return;
|
||||
select.dataset.bound = '1';
|
||||
select.addEventListener('change', function () {
|
||||
if (!select.value) return;
|
||||
const input = document.getElementById(inputId);
|
||||
if (input) input.value = select.value;
|
||||
});
|
||||
}
|
||||
|
||||
function resolveModelListCredentials(scope) {
|
||||
if (scope === 'vision') {
|
||||
const vp = (document.getElementById('vision-provider')?.value || '').trim();
|
||||
const provider = vp || document.getElementById('openai-provider')?.value || 'openai';
|
||||
const baseUrl = (document.getElementById('vision-base-url')?.value || '').trim()
|
||||
|| (document.getElementById('openai-base-url')?.value || '').trim();
|
||||
const apiKey = (document.getElementById('vision-api-key')?.value || '').trim()
|
||||
|| (document.getElementById('openai-api-key')?.value || '').trim();
|
||||
return { provider, base_url: baseUrl, api_key: apiKey };
|
||||
}
|
||||
return {
|
||||
provider: document.getElementById('openai-provider')?.value || 'openai',
|
||||
base_url: (document.getElementById('openai-base-url')?.value || '').trim(),
|
||||
api_key: (document.getElementById('openai-api-key')?.value || '').trim()
|
||||
};
|
||||
}
|
||||
|
||||
function syncModelListFetchButtons() {
|
||||
const tFn = typeof window.t === 'function' ? window.t : (k) => k;
|
||||
const openaiProv = document.getElementById('openai-provider')?.value || 'openai';
|
||||
const openaiBtn = document.getElementById('fetch-openai-models-btn');
|
||||
const openaiHint = document.getElementById('fetch-openai-models-hint');
|
||||
const openaiSelect = document.getElementById('openai-model-select');
|
||||
const isClaudeOpenai = openaiProv === 'claude';
|
||||
if (openaiBtn) {
|
||||
openaiBtn.style.display = isClaudeOpenai ? 'none' : '';
|
||||
}
|
||||
if (openaiSelect && isClaudeOpenai) {
|
||||
openaiSelect.style.display = 'none';
|
||||
}
|
||||
if (openaiHint) {
|
||||
if (isClaudeOpenai) {
|
||||
openaiHint.textContent = tFn('settingsBasic.modelsListClaudeHint');
|
||||
openaiHint.style.display = '';
|
||||
} else {
|
||||
openaiHint.textContent = '';
|
||||
openaiHint.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
const vp = (document.getElementById('vision-provider')?.value || '').trim();
|
||||
const visionEffectiveProv = vp || openaiProv;
|
||||
const visionBtn = document.getElementById('fetch-vision-models-btn');
|
||||
const visionHint = document.getElementById('fetch-vision-models-hint');
|
||||
const visionSelect = document.getElementById('vision-model-select');
|
||||
const isClaudeVision = visionEffectiveProv === 'claude';
|
||||
if (visionBtn) {
|
||||
visionBtn.style.display = isClaudeVision ? 'none' : '';
|
||||
}
|
||||
if (visionSelect && isClaudeVision) {
|
||||
visionSelect.style.display = 'none';
|
||||
}
|
||||
if (visionHint) {
|
||||
if (isClaudeVision) {
|
||||
visionHint.textContent = tFn('settingsBasic.modelsListClaudeHint');
|
||||
visionHint.style.display = '';
|
||||
} else {
|
||||
visionHint.textContent = '';
|
||||
visionHint.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function populateModelSelect(scope, models, currentValue) {
|
||||
const { selectId, inputId } = modelSelectIds(scope);
|
||||
const select = document.getElementById(selectId);
|
||||
const input = document.getElementById(inputId);
|
||||
if (!select) return;
|
||||
const tFn = typeof window.t === 'function' ? window.t : (k) => k;
|
||||
select.innerHTML = '';
|
||||
const placeholder = document.createElement('option');
|
||||
placeholder.value = '';
|
||||
placeholder.disabled = true;
|
||||
placeholder.textContent = tFn('settingsBasic.modelsListSelectPlaceholder');
|
||||
select.appendChild(placeholder);
|
||||
|
||||
const seen = new Set();
|
||||
const addOption = (id) => {
|
||||
const val = (id || '').trim();
|
||||
if (!val || seen.has(val)) return;
|
||||
seen.add(val);
|
||||
const opt = document.createElement('option');
|
||||
opt.value = val;
|
||||
opt.textContent = val;
|
||||
select.appendChild(opt);
|
||||
};
|
||||
(models || []).forEach(addOption);
|
||||
const cur = (currentValue || (input && input.value) || '').trim();
|
||||
if (cur && seen.has(cur)) {
|
||||
select.value = cur;
|
||||
} else {
|
||||
select.value = '';
|
||||
}
|
||||
select.style.display = select.options.length > 1 ? '' : 'none';
|
||||
}
|
||||
|
||||
async function fetchModelList(scope) {
|
||||
const tFn = typeof window.t === 'function' ? window.t : (k) => k;
|
||||
const creds = resolveModelListCredentials(scope);
|
||||
const btnId = scope === 'vision' ? 'fetch-vision-models-btn' : 'fetch-openai-models-btn';
|
||||
const resultId = scope === 'vision' ? 'fetch-vision-models-result' : 'fetch-openai-models-result';
|
||||
const inputId = scope === 'vision' ? 'vision-model' : 'openai-model';
|
||||
const btn = document.getElementById(btnId);
|
||||
const resultEl = document.getElementById(resultId);
|
||||
const inputEl = document.getElementById(inputId);
|
||||
|
||||
if (creds.provider === 'claude') {
|
||||
if (resultEl) {
|
||||
resultEl.textContent = tFn('settingsBasic.modelsListClaudeHint');
|
||||
resultEl.style.color = 'var(--text-muted, #718096)';
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!creds.api_key) {
|
||||
if (resultEl) {
|
||||
resultEl.textContent = tFn('settingsBasic.modelsListNeedApiKey');
|
||||
resultEl.style.color = 'var(--error-color, #e53e3e)';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (btn) {
|
||||
btn.style.pointerEvents = 'none';
|
||||
btn.style.opacity = '0.5';
|
||||
}
|
||||
if (resultEl) {
|
||||
resultEl.textContent = tFn('settingsBasic.modelsListFetching');
|
||||
resultEl.style.color = 'var(--text-muted, #718096)';
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await apiFetch('/api/config/list-models', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(creds)
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(result.error || '请求失败');
|
||||
}
|
||||
if (!result.success) {
|
||||
if (resultEl) {
|
||||
resultEl.textContent = (result.supported === false
|
||||
? tFn('settingsBasic.modelsListClaudeHint')
|
||||
: tFn('settingsBasic.modelsListFailed')) + ': ' + (result.error || '');
|
||||
resultEl.style.color = 'var(--error-color, #e53e3e)';
|
||||
}
|
||||
return;
|
||||
}
|
||||
const currentValue = inputEl ? inputEl.value.trim() : '';
|
||||
populateModelSelect(scope, result.models || [], currentValue);
|
||||
if (resultEl) {
|
||||
const count = result.count != null ? result.count : (result.models || []).length;
|
||||
resultEl.textContent = tFn('settingsBasic.modelsListSuccess').replace('{count}', String(count));
|
||||
resultEl.style.color = 'var(--success-color, #38a169)';
|
||||
}
|
||||
} catch (error) {
|
||||
if (resultEl) {
|
||||
resultEl.textContent = tFn('settingsBasic.modelsListFailed') + ': ' + error.message;
|
||||
resultEl.style.color = 'var(--error-color, #e53e3e)';
|
||||
}
|
||||
} finally {
|
||||
if (btn) {
|
||||
btn.style.pointerEvents = '';
|
||||
btn.style.opacity = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2408,7 +2408,15 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="openai-model"><span data-i18n="settingsBasic.model">模型</span> <span style="color: red;">*</span></label>
|
||||
<input type="text" id="openai-model" data-i18n="settingsBasic.modelPlaceholder" data-i18n-attr="placeholder" placeholder="gpt-4" required />
|
||||
<div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap;">
|
||||
<input type="text" id="openai-model" data-i18n="settingsBasic.modelPlaceholder" data-i18n-attr="placeholder" placeholder="gpt-4" required style="flex: 1; min-width: 140px;" />
|
||||
<select id="openai-model-select" class="model-pick-select" style="display: none; min-width: 160px; max-width: 240px;" title="">
|
||||
<option value="" disabled data-i18n="settingsBasic.modelsListSelectPlaceholder">请选择模型</option>
|
||||
</select>
|
||||
<a href="javascript:void(0)" id="fetch-openai-models-btn" onclick="fetchModelList('openai')" style="font-size: 0.8125rem; color: var(--accent-color, #3182ce); text-decoration: none; cursor: pointer; user-select: none; white-space: nowrap;" data-i18n="settingsBasic.fetchModels">获取列表</a>
|
||||
</div>
|
||||
<small id="fetch-openai-models-hint" class="form-hint" style="display: none; font-size: 0.75rem; margin-top: 4px;"></small>
|
||||
<span id="fetch-openai-models-result" style="font-size: 0.75rem; margin-top: 2px; display: block;"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="openai-max-total-tokens"><span data-i18n="settingsBasic.maxTotalTokens">最大上下文 Token 数</span></label>
|
||||
@@ -2486,7 +2494,15 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vision-model"><span data-i18n="settingsBasic.visionModel">视觉模型</span> <span style="color: red;">*</span></label>
|
||||
<input type="text" id="vision-model" data-i18n="settingsBasic.visionModelPlaceholder" data-i18n-attr="placeholder" placeholder="qwen-vl-max" />
|
||||
<div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap;">
|
||||
<input type="text" id="vision-model" data-i18n="settingsBasic.visionModelPlaceholder" data-i18n-attr="placeholder" placeholder="qwen-vl-max" style="flex: 1; min-width: 140px;" />
|
||||
<select id="vision-model-select" class="model-pick-select" style="display: none; min-width: 160px; max-width: 240px;">
|
||||
<option value="" disabled data-i18n="settingsBasic.modelsListSelectPlaceholder">请选择模型</option>
|
||||
</select>
|
||||
<a href="javascript:void(0)" id="fetch-vision-models-btn" onclick="fetchModelList('vision')" style="font-size: 0.8125rem; color: var(--accent-color, #3182ce); text-decoration: none; cursor: pointer; user-select: none; white-space: nowrap;" data-i18n="settingsBasic.fetchModels">获取列表</a>
|
||||
</div>
|
||||
<small id="fetch-vision-models-hint" class="form-hint" style="display: none; font-size: 0.75rem; margin-top: 4px;"></small>
|
||||
<span id="fetch-vision-models-result" style="font-size: 0.75rem; margin-top: 2px; display: block;"></span>
|
||||
</div>
|
||||
<details style="margin-top: 8px;">
|
||||
<summary style="cursor: pointer; font-size: 0.875rem; color: var(--accent-color, #3182ce);" data-i18n="settingsBasic.visionAdvanced">高级:预处理与限制</summary>
|
||||
|
||||
Reference in New Issue
Block a user