feat: 402 (insufficient balance / лимит токенов) — фейловер на следующий апстрим
402 от провайдера — ошибка аккаунта (баланс/лимит токенов), а не запроса: следующий апстрим в списке модели может быть платёжеспособен. Теперь при 5xx/429/402 освобождаем слот и пробуем следующего свободного; остальные 4xx продолжаем отдавать клиенту как есть. Обновлён CONFIG.md (маршрутизация, лог-секции). Ранее этот правка терялась (3155065 не сохранился) — восстанавливаю.
This commit is contained in:
@@ -41,9 +41,11 @@
|
||||
запросов сейчас идёт на каждый апстрим, и по этим счётчикам решаем, брать запрос
|
||||
или нет. Мы **не полагаемся** на `503`/`429` от самого апстрима, чтобы узнать,
|
||||
что он перегружен: такой ответ от провайдера — это уже сбой, а не способ
|
||||
управления нагрузкой. Если выбранный апстрим всё же вернул `5xx`/`429`, мы
|
||||
управления нагрузкой. Если выбранный апстрим всё же вернул `5xx`/`429`/`402`, мы
|
||||
освобождаем его слот и, при наличии, пробуем **следующий свободный** из списка
|
||||
модели (фейловер); если свободных не осталось — отдаём `503` от прокси.
|
||||
`402` (insufficient balance / исчерпан лимит токенов) — ошибка аккаунта
|
||||
провайдера, но фейловер имеет смысл: следующий апстрим может быть платёжеспособен.
|
||||
|
||||
## Формат файла
|
||||
|
||||
@@ -454,7 +456,7 @@ fun release(u: UpstreamConf) = active.getValue(u.id).decrementAndGet()
|
||||
`upstream.patch` → `model.patch`), каждый через `merge(...)`; слои с `patch
|
||||
== null` пропускаем. Итог — финальное тело запроса;
|
||||
- шлём на `provider.url + /chat/completions` с `Authorization: Bearer provider.key`;
|
||||
- при ответе `5xx`/`429` от провайдера — `release(upstream)` и переходим к
|
||||
- при ответе `5xx`/`429`/`402` от провайдера — `release(upstream)` и переходим к
|
||||
следующему свободному апстриму из списка (фейловер); исчерпали список —
|
||||
отдаём `503` (`all upstreams failed`);
|
||||
- при успехе/отмене клиента — `release(upstream)` в `finally` и выходим.
|
||||
@@ -507,7 +509,7 @@ fun release(u: UpstreamConf) = active.getValue(u.id).decrementAndGet()
|
||||
свободному `upstream=<id>`.
|
||||
- **Завершение** (успех/ошибка/отмена клиента): длительность, статус апстрима,
|
||||
освобождён слот `active[id]=N/limit`.
|
||||
- **Ошибка апстрима** (не `5xx`/`429`, а сетевая/таймаут): как сейчас — лог с
|
||||
- **Ошибка апстрима** (не `5xx`/`429`/`402`, а сетевая/таймаут): как сейчас — лог с
|
||||
сообщением.
|
||||
|
||||
Формат строки лога — один префикс `[llm-proxy]`, как сейчас, чтобы не ломать
|
||||
|
||||
@@ -229,7 +229,7 @@ private suspend fun handleChat(
|
||||
setBody(forwarded.toString())
|
||||
}.execute { resp ->
|
||||
upstreamStatus = resp.status.value
|
||||
if (upstreamStatus >= 500 || upstreamStatus == 429) {
|
||||
if (upstreamStatus >= 500 || upstreamStatus == 429 || upstreamStatus == 402) {
|
||||
log.warn { "[llm-proxy] model=$modelName upstream=${up.id} вернул $upstreamStatus → фейловер" }
|
||||
failed.add(up.id)
|
||||
failover = true
|
||||
|
||||
Reference in New Issue
Block a user