feat(core): add retry for transient HTTP and stream-level errors

Add retry mechanism driven by pi settings.json retry.provider config
(timeoutMs, maxRetries, maxRetryDelayMs).

HTTP-level retries handle 429/5xx with exponential backoff and jitter,
respecting Retry-After headers (seconds and HTTP-date formats).

Stream-level retries handle cases where the API returns 200 OK but
sends an error event in the stream body. Retries only when no content
has been emitted yet.

Per-attempt timeout via AbortController with automatic retry. Clean
abort propagation through the retry loop.
This commit is contained in:
jizhejiang
2026-06-02 11:38:10 +08:00
parent f48291f252
commit c3107dbc1c
7 changed files with 610 additions and 1995 deletions
+19
View File
@@ -89,6 +89,23 @@ export interface StreamOptions {
maxTokens?: number
onPayload?: (payload: unknown, model: ModelLike) => unknown | Promise<unknown>
onResponse?: (response: ProviderResponseInfo, model: ModelLike) => void | Promise<void>
/**
* HTTP request timeout in milliseconds.
* Applied per-attempt; on timeout the request is retried if retries remain.
*/
timeoutMs?: number
/**
* Maximum retry attempts for transient HTTP errors (429, 5xx).
* Default: 2.
*/
maxRetries?: number
/**
* Maximum delay in milliseconds to wait for a retry when the server requests
* a long wait via Retry-After. If the server's requested delay exceeds this
* value, the request fails immediately. Default: 60000 (60 seconds).
* Set to 0 to disable the cap.
*/
maxRetryDelayMs?: number
}
export type AssistantMessageEvent =
@@ -153,4 +170,6 @@ export interface CoreDependencies {
now?: () => number
uuid?: () => string
homeDir?: () => string
/** Injectable delay for retry backoff. Defaults to setTimeout. */
delay?: (ms: number, signal: AbortSignal) => Promise<void>
}