Skip to content

feat(retry): auto-retry Cloudflare 5xx from relays + user-configurable retry-error settings - #609

Merged
su-fen merged 1 commit into
Stack-Cairn:mainfrom
YUZHEthefool:feat/retry-error-config-608
Aug 25, 2026
Merged

feat(retry): auto-retry Cloudflare 5xx from relays + user-configurable retry-error settings#609
su-fen merged 1 commit into
Stack-Cairn:mainfrom
YUZHEthefool:feat/retry-error-config-608

Conversation

@YUZHEthefool

@YUZHEthefool YUZHEthefool commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Relays/proxy stations (中转站) intermittently surface Cloudflare 520 / 521 / 525 and similar transient 5xx. pi-ai's isRetryableAssistantError only covers 429 / 500 / 502 / 503 / 504 / 524, so these errors failed outright instead of retrying — #608.

This layers a LiveAgent retry-error extension on top of pi-ai's classifier, applied to both withStreamRetry and withProviderFailover, so a transient relay 5xx now retries with exponential backoff (matching 524) and can also trigger a provider switch.

  • Zero-config fix: the extension defaults to every Cloudflare preset (520-527) on, so relays self-heal out of the box.
  • User-configurable window (per the issue's "也可考虑支持手动重试恢复" suggestion): a new global Retry Error Config with preset status-code toggles plus free-text custom error keywords, surfaced as a new "Retry & Resilience" settings section. Lets users define which errors should be retried (e.g. add SSL handshake failed for relay wording pi-ai doesn't recognize).
  • Consistent across all paths: settings sync into the runtime via a module-level extension, so every streaming path (agent runs, text mode, titles, compaction, memory, cron, subagents) picks up the classification without per-call plumbing.
  • Hard floors preserved: pi-ai's non-retryable quota/billing guard and the failover ineligible client-error guard (context overflow etc.) stay in effect — the extension only ever adds retryable errors, never overrides a "must fail fast" verdict.

Closes #608.

Changes

Area File Change
Runtime streamRetry.ts RetryErrorExtension + isExtensionRetryableError + setRetryErrorExtension; wired into withStreamRetry
Runtime providerFailover.ts Extension wired into isFailoverEligibleAssistantError + ProviderFailoverStreamOptions
Settings agent-ui/.../settings/types.ts RetryErrorSettings, RETRYABLE_PRESET_HTTP_STATUS_CODES, DEFAULT_RETRY_ERROR_SETTINGS
Settings agent-ui/.../settings/retryError.ts (new) normalizeRetryErrorSettings
Settings agent-ui/.../settings/index.ts wired into getDefaultSettings / normalizeSettings
Persist agent-gui/.../settings/storage.ts retryErrorSettings in localStorage (local-only, not gateway-synced)
Sync agent-gui/src/App.tsx useEffect pushes settings → runtime extension
UI agent-ui/.../RetryErrorSection.tsx (new) Preset toggles + custom-pattern chips
UI SettingsPage.tsx + pages/settings/types.ts Register new section (retryError)
i18n enUSSettings.ts / zhCNSettings.ts New keys

Design decisions

  • Global setting, not per-provider (chosen): relays are usually a single station per setup; a global toggle is the simplest UX and lives alongside the existing failover settings.
  • Local-only storage (localStorage, not gateway-synced): avoids Rust/Go backend changes; the desktop app — the affected surface — is fully covered.
  • OR with pi-ai's classifier, not replace: the non-retryable quota/billing guard and the failover ineligible client-error guard remain hard floors.

Verification

  • tsc --noEmit clean.
  • pnpm --filter liveagent build (tsc && vite build) succeeds.
  • New + existing tests: 61/61 pass across stream-retry, provider-failover, retry-error-normalize, i18n parity.
  • New tests cover: 525/520 retried via extension, custom-substring match, preset toggle off → not retried, quota error still not retried, module-default zero-config fix, failover eligibility (+ client-error still rejected).
  • Built & smoke-ran the release exe (target/release/liveagent.exe, 62.7 MB) — launches and initializes correctly.

Screenshot

image

Test plan

  • Open Settings → Retry & Resilience: presets 520-527 on by default.
  • Toggle a preset off → that code no longer auto-retries; toggle back on → retries resume.
  • Add a custom keyword (e.g. SSL handshake failed) → a matching relay error retries.
  • Against a relay returning intermittent 525, confirm the turn now retries with backoff instead of failing (the [Feature] 中转站偶发 HTTP 520 / 521 / 525 不会自动重试 #608 scenario).
  • Confirm 429/500/502/503/504/524 still retry regardless of toggles (built-in).

Notes

  • Unrelated pre-existing test failures (preset scripts stay in sync, portalled select) exist on main and are not introduced by this PR — verified by stashing all changes and re-running.

@StackCairn
StackCairn marked this pull request as draft August 25, 2026 04:17
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

PR governance checks passed. Awaiting human review.

@YUZHEthefool
YUZHEthefool force-pushed the feat/retry-error-config-608 branch 2 times, most recently from bdba180 to 10a9ad6 Compare August 25, 2026 05:11
@YUZHEthefool
YUZHEthefool marked this pull request as ready for review August 25, 2026 06:27
@su-fen

su-fen commented Aug 25, 2026

Copy link
Copy Markdown
Member

这个好像放设置单开一页不太合适诶,适合放在供应商配置页面设置抽屉里

@YUZHEthefool

YUZHEthefool commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

这个好像放设置单开一页不太合适诶,适合放在供应商配置页面设置抽屉里

我去改下🤔
now:
image

…e retry-error settings (Stack-Cairn#608)

Relays/proxy stations intermittently surface Cloudflare 520/521/525 and
similar transient 5xx. pi-ai's isRetryableAssistantError only covers
429/500/502/503/504/524, so these errors failed outright instead of
retrying (Stack-Cairn#608).

This layers a LiveAgent retry-error extension on top of pi-ai's classifier,
applied to both withStreamRetry and withProviderFailover so a transient
relay 5xx now retries with exponential backoff (matching 524) and can also
trigger a provider switch.

- Default the extension to every Cloudflare preset (520-527) on, so relays
  self-heal out of the box with zero configuration.
- Add a global RetryErrorSettings (preset status-code toggles + custom
  substrings) persisted in localStorage. Surfaced as a card inside the
  Providers custom-settings drawer (alongside failover), per maintainer
  review, rather than a standalone settings page.
- Sync settings into the runtime via a module-level extension, so all
  streaming paths (agent runs, text mode, titles, compaction, memory, cron,
  subagents) pick up the classification without per-call plumbing.
- Preserve pi-ai's non-retryable quota/billing guard and the failover
  ineligible client-error guard as hard floors.

Tests:
- stream-retry: extension classifier (preset code match, word-boundary
  guard so "520" != "5200", case-insensitive substring, empty/undefined
  message, multi-code alternation, whitespace-only pattern dropped),
  withStreamRetry retrying 525/custom patterns via retryExtension and the
  module default (zero-config fix), preset-disabled not retried, quota
  error still not retried, failover eligibility (+ client-error still
  rejected), setRetryErrorExtension/getRetryErrorExtension round-trip.
- retry-error-normalize: defaults, preset validation/dedup, empty-array
  opt-out, legacy fallback, custom-pattern trim/dedup, normalizeSettings
  wiring.
- storage: retryErrorSettings localStorage round-trip — defaults when
  empty, read+normalize on load, legacy fallback to all presets.

Closes Stack-Cairn#608.
@YUZHEthefool
YUZHEthefool force-pushed the feat/retry-error-config-608 branch from 10a9ad6 to 88a6ae2 Compare August 25, 2026 09:34
@su-fen
su-fen merged commit 7a026a5 into Stack-Cairn:main Aug 25, 2026
9 checks passed
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.

[Feature] 中转站偶发 HTTP 520 / 521 / 525 不会自动重试

2 participants