mirror of
https://github.com/zed-industries/zed.git
synced 2026-08-24 16:34:46 +00:00
`LanguageModelCompletionError` had eight near-duplicate variants (PromptTooLarge, RateLimitExceeded, ServerOverloaded, InvalidEncryptedContent, AuthenticationError, PermissionError, ApiEndpointNotFound, PaymentRequired) that each independently encoded a provider-semantic outcome, alongside ProviderRejection for everything else. Providers had to choose between two places to report the same kind of failure, and retry logic had to special-case each variant even though the underlying decision (retry or not, how long) depended only on the HTTP status. This collapses all of those into ProviderRejection, adding a `category: ProviderErrorCategory` field that's derived once, centrally, in `ProviderErrorCategory::classify` (status + code + message in, category out). Provider-specific wire parsing (Anthropic's ApiErrorCode, OpenRouter's ApiErrorCode) stays local, but now maps into this shared category instead of duplicating the same classification. `status`, `code`, `message`, and `retry_after` are still preserved verbatim, including for known categories, so nothing that inspected the raw wire details loses information. While touching OpenRouter's error path, fixed a real bug: `ApiErrorCode:: from_status` defaulted any unmapped HTTP status to `ApiError` (502), silently replacing e.g. a real 500 with a fabricated 502. `ApiError` now carries the real status separately from the (optional) documented code, so unmapped statuses are preserved and classified as `Other` instead of being coerced into a wrong, documented one. Restored the retry-strategy enum in `agent::Thread` (a prior commit on this branch had flattened it into a single-shape struct, losing the distinction between "wait a fixed amount" and "back off exponentially"). ProviderRejection now retries only for the same status set Delta already uses (408, 425, 429, and all 5xx including the unofficial 529): a provider-given `retry_after` becomes a fixed delay, otherwise attempts back off exponentially from BASE_RETRY_DELAY. A rejection with no status (e.g. a content-policy code like `cyber_policy`) or a non-retryable status never retries, since resending the same request would just repeat the same rejection. Testing: - `cargo nextest run` across every touched crate (agent, agent_ui, anthropic, open_ai, open_router, copilot_chat, language_model_core, language_models, language_models_cloud): 1430 passed, 11 skipped (network-gated unit evals). - `cargo check --workspace --all-targets`: clean. - `cargo fmt --check`: clean. - Added unit tests for the new retry rule (status-less and non-retryable rejections don't retry; retryable statuses use exponential backoff without `retry_after` and a fixed delay with it) and for OpenRouter preserving an unmapped status instead of fabricating 502. Environment note: `corgi` cannot build anything depending on `gpui` from this worktree (it rejects gpui's build output for embedding an absolute path); used plain `cargo`/`cargo nextest` for those crates instead, logged in ~/.corgi-feedback per instructions. |
||
|---|---|---|
| .. | ||
| src | ||
| Cargo.toml | ||
| LICENSE-GPL | ||