feat(assistant): уведомление о failover в админке
callLLMFailover пишет состояние в app_settings.assistant_failover: какой провайдер исчерпан и каким подхвачено (или «все недоступны»); при успехе активного флаг снимается. Админ-раздел показывает баннер «Провайдер X недоступен — работаю на Y». Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -317,15 +317,29 @@ async function callLLM(messages, maxTokens, override) {
|
||||
/* Перебор провайдеров: активный, затем остальные — при лимите/сетевой ошибке.
|
||||
* Останавливаемся на успехе или на «контентной» неудаче (пустой ответ). */
|
||||
const _RETRYABLE = { rate_limit: 1, http: 1, timeout: 1, network: 1 };
|
||||
function _recordFailover(failed, served, reason) {
|
||||
try {
|
||||
db.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES ('assistant_failover', ?)")
|
||||
.run(JSON.stringify({ at: new Date().toISOString(), failedId: failed && failed.id, failedName: failed && failed.name, servedId: served && served.id, servedName: served && served.name, reason: reason || 'error' }));
|
||||
} catch (e) {}
|
||||
}
|
||||
function _clearFailover() { try { db.prepare("DELETE FROM app_settings WHERE key = 'assistant_failover'").run(); } catch (e) {} }
|
||||
|
||||
async function callLLMFailover(messages, maxTokens) {
|
||||
const cfgs = providersOrdered();
|
||||
if (!cfgs.length) return { text: null, error: 'off' };
|
||||
let last = { text: null, error: 'off' };
|
||||
for (const c of cfgs) {
|
||||
last = await callLLM(messages, maxTokens, c);
|
||||
if (last.text) return last;
|
||||
if (!_RETRYABLE[last.error]) break; // не лимит/сеть — нет смысла пробовать другие
|
||||
let last = { text: null, error: 'off' }, firstErr = null;
|
||||
for (let i = 0; i < cfgs.length; i++) {
|
||||
last = await callLLM(messages, maxTokens, cfgs[i]);
|
||||
if (i === 0) firstErr = last.error;
|
||||
if (last.text) {
|
||||
if (i === 0) _clearFailover(); // активный работает — снимаем флаг
|
||||
else _recordFailover(cfgs[0], cfgs[i], firstErr); // активный упал → выручил запасной
|
||||
return last;
|
||||
}
|
||||
if (!_RETRYABLE[last.error]) break; // не лимит/сеть — нет смысла пробовать других
|
||||
}
|
||||
if (cfgs.length && _RETRYABLE[firstErr]) _recordFailover(cfgs[0], null, firstErr); // все недоступны
|
||||
return last;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user