feat: add INVALID_MODEL_ID retry config + detailed request logging

- Config: new InvalidModelRetries field (default 3, range 0-20)
- Admin API: /admin/api/general GET/POST for general settings
- Admin UI: new "通用设置" card with retry count input
- CallKiroAPI: same-endpoint retry on HTTP 400 INVALID_MODEL_ID
  before falling back to next endpoint
- CallKiroAPI: switched to log.Printf with timestamp, account,
  model, attempt counter, elapsed time, error body truncation
This commit is contained in:
2026-05-11 19:15:49 +08:00
parent 834890f4be
commit 3b791a6926
4 changed files with 214 additions and 48 deletions

View File

@@ -969,6 +969,17 @@
data-i18n-placeholder="settings.apiKeyPlaceholder"><button class="btn btn-sm btn-secondary" onclick="generateApiKey()" data-i18n="settings.generateApiKey"></button></div></div>
<button class="btn btn-primary" onclick="saveSettings()" data-i18n="common.save"></button>
</div>
<div class="card">
<div class="card-header"><span class="card-title" data-i18n="settings.generalSettings"></span></div>
<div class="form-group">
<label data-i18n="settings.invalidModelRetries"></label>
<input type="number" id="invalidModelRetries" min="0" max="20" step="1" placeholder="3">
<small style="color:#64748b;font-size:12px;margin-top:4px;display:block"
data-i18n="settings.invalidModelRetriesHint"></small>
</div>
<button class="btn btn-primary" onclick="saveGeneralConfig()"
data-i18n="settings.saveGeneral"></button>
</div>
<div class="card">
<div class="card-header"><span class="card-title" data-i18n="settings.thinkingSettings"></span></div>
<div class="form-group">
@@ -1123,6 +1134,11 @@
'settings.enableApiKey': '启用 API Key 验证',
'settings.apiKeyPlaceholder': '留空则不验证',
'settings.generateApiKey': '随机生成',
'settings.generalSettings': '通用设置',
'settings.invalidModelRetries': 'INVALID_MODEL_ID 同端点重试次数',
'settings.invalidModelRetriesHint': '当上游返回 INVALID_MODEL_IDHTTP 400先在当前端点重试 N 次后再 fallback 到下一个端点。默认 3范围 0-20',
'settings.saveGeneral': '保存通用设置',
'settings.generalSaved': '通用设置已保存',
'settings.thinkingSettings': 'Thinking 模式设置',
'settings.thinkingSuffix': '触发后缀',
'settings.thinkingSuffixHint': '模型名称加此后缀即启用思考模式,如 claude-sonnet-4.5-thinking',
@@ -1329,6 +1345,11 @@
'settings.enableApiKey': 'Enable API Key Verification',
'settings.apiKeyPlaceholder': 'Leave empty to disable',
'settings.generateApiKey': 'Generate',
'settings.generalSettings': 'General Settings',
'settings.invalidModelRetries': 'INVALID_MODEL_ID same-endpoint retries',
'settings.invalidModelRetriesHint': 'When upstream returns INVALID_MODEL_ID (HTTP 400), retry the current endpoint N times before falling back. Default 3, range 0-20',
'settings.saveGeneral': 'Save General Settings',
'settings.generalSaved': 'General settings saved',
'settings.thinkingSettings': 'Thinking Mode Settings',
'settings.thinkingSuffix': 'Trigger Suffix',
'settings.thinkingSuffixHint': 'Add this suffix to model name to enable thinking mode, e.g. claude-sonnet-4.5-thinking',
@@ -1991,6 +2012,7 @@
document.getElementById('apiKeyInput').value = d.apiKey || '';
loadThinkingConfig();
loadEndpointConfig();
loadGeneralConfig();
}
async function loadThinkingConfig() {
const res = await fetch('/admin/api/thinking', { headers: { 'X-Admin-Password': password } });
@@ -2020,6 +2042,26 @@
const d = await res.json();
if (d.success) { alert(t('settings.endpointSaved')); } else { alert(t('common.saveFailed') + ': ' + d.error); }
}
async function loadGeneralConfig() {
const res = await fetch('/admin/api/general', { headers: { 'X-Admin-Password': password } });
const d = await res.json();
const v = (d && typeof d.invalidModelRetries === 'number') ? d.invalidModelRetries : 3;
document.getElementById('invalidModelRetries').value = v;
}
async function saveGeneralConfig() {
const raw = document.getElementById('invalidModelRetries').value;
const n = parseInt(raw, 10);
if (isNaN(n) || n < 0 || n > 20) {
alert(t('common.saveFailed') + ': 0-20');
return;
}
const res = await fetch('/admin/api/general', {
method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Admin-Password': password },
body: JSON.stringify({ invalidModelRetries: n })
});
const d = await res.json();
if (d.success) { alert(t('settings.generalSaved')); } else { alert(t('common.saveFailed') + ': ' + d.error); }
}
function generateApiKey() {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let key = 'sk-';