827cf6fa01
Issue #54465 established that a same-provider retry after a full-budget timeout costs a second whole `timeout` window before the fallback chain is reached, doubling the user-visible stall, and that compression must not pay it because it sits on a critical path. The guard added for that is spelled `task == "compression"`, so vision — which sits on the interactive path — still retries. The cost is the same and the stall is more visible: the turn holding the image cannot answer, and because turns are serialised the following user messages queue behind it. Two sequential full-budget timeouts on an unhealthy vision provider is a long stall for something the fallback chain could have served immediately. Replaces the string comparison at both retry sites (sync `call_llm` and `async_call_llm`) with `_TIMEOUT_NO_RETRY_TASKS = {"compression", "vision"}`, so the two paths cannot drift again. Behaviour is unchanged for every other task: fast blips (a streaming-close or a 5xx) still retry, and only full-budget timeouts on those two tasks skip straight to fallback. Tests: vision now falls straight through to fallback with the primary tried exactly once, and a non-critical task still gets its one same-provider retry, so the change stays scoped. Reverting the source change fails the vision test and leaves the scoping test green. Not the same as #51513, which fixes five separate defects in the vision fallback chain (capability detection, sync/async client misuse, geo-block and RemoteProtocolError classification, and chain iteration). This is about what happens before that chain is reached. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>