Skip to content

Phase 3: provider abstraction + retry/timeout#6

Merged
froggychips merged 1 commit into
mainfrom
review/phase-3-provider-abstraction
May 19, 2026
Merged

Phase 3: provider abstraction + retry/timeout#6
froggychips merged 1 commit into
mainfrom
review/phase-3-provider-abstraction

Conversation

@froggychips

Copy link
Copy Markdown
Owner

Summary

callOpenAI / callGrok / callGemini были тремя почти одинаковыми функциями с голым fetch без retry/timeout. Свёрнуты в registry PROVIDERS + общий aiFetch() с AbortController и экспоненциальным backoff.

Что изменилось

  • aiFetch(url, headers, body, opts) — retry на 429 и 5xx, экспоненциальный backoff ~400ms → 1.6s с jitter ±25%, AbortController-таймаут 30с. Все три провайдера ходят через одну обёртку.
  • PROVIDERS registry{ openai, grok, gemini } с полями keyField / label / buildRequest / parseResponse. Различия в wire-format (chat-completions vs Gemini payload, Authorization vs x-goog-api-key) изолированы в buildRequest.
  • callAI() — тонкий диспатчер без switch'а: registry → request → aiFetch → parse → addUsage.
  • TTA_TEST_KEY handler — три ветки if/else свёрнуты в один callAI(provider, defaultModel, ping).
  • TTA_HEALTH_CHECK handlerprobe() helper тоже идёт через callAI.

Поведенческий апгрейд (бонус)

Gemini теперь проходит через checkBudget() — раньше дневной токен-лимит применялся только к OpenAI/Grok, у Gemini можно было его обойти.

Test plan

  • Все три провайдера отвечают: настройка ключа → диагностика зелёная.
  • Rate limit: симулировать 429 → DevTools Network показывает 3 попытки с растущей задержкой.
  • Timeout: сетевой throttling до Slow 3G → запрос падает через 30с с понятной ошибкой.
  • Token budget: установить лимит 100, исчерпать через Gemini → следующий запрос даёт Daily token budget exceeded.
  • Onboarding key-test работает для всех трёх провайдеров.

🤖 Generated with Claude Code

callOpenAI / callGrok / callGemini были тремя почти одинаковыми функциями
с голым fetch и без retry/timeout. Свёрнуты в registry PROVIDERS + общий
aiFetch().

- aiFetch() обрабатывает retry на 429 и 5xx с экспоненциальным backoff
  (~400ms → 1.6s, jitter ±25%, до 2 ретраев). Все три провайдера ходят
  через одну обёртку с AbortController-таймаутом 30с.
- PROVIDERS — registry { openai, grok, gemini } с keyField / label /
  buildRequest / parseResponse. Различия (chat-completions vs Gemini
  payload, Authorization vs x-goog-api-key) изолированы в buildRequest.
- callAI() стал тонким диспатчером без switch'а: смотрит в registry,
  тянет ключ из prefs, собирает request через buildRequest, гонит через
  aiFetch, парсит через parseResponse, добавляет usage.
- TTA_TEST_KEY (handler) переписан с трёх веток if/else на единый
  callAI('provider', model, ping). Удалена ручная сборка OpenAI-compat
  payload в дублирующем месте.
- TTA_HEALTH_CHECK получил probe() helper, который тоже идёт через
  callAI. Поведение проверок не меняется.
- Поведенческий апгрейд: Gemini теперь тоже проходит checkBudget()
  (раньше budget применялся только к OpenAI/Grok — несогласованность
  исходного кода).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@froggychips froggychips merged commit 5e22f88 into main May 19, 2026
1 check failed
@froggychips froggychips deleted the review/phase-3-provider-abstraction branch May 19, 2026 08:09

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eba4b175f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread background.js
Comment on lines +186 to +189
} catch (e) {
// AbortError (timeout) тоже ретраим как сетевую ошибку.
lastErr = e;
if (attempt === retries) throw lastErr;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop retrying non-retriable HTTP failures

When a provider returns a non-retriable HTTP error such as 400/401/403, the throw lastErr from the try block is immediately caught here, and the loop continues unless this is the final attempt. That makes bad API keys or invalid model names issue three requests and wait through backoff before surfacing the error, despite the intended “retry only 429/5xx” behavior; rethrow non-retriable HTTP errors outside this catch path or tag them so they are not retried.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant