diff --git a/packages/ai/src/utils/retry.ts b/packages/ai/src/utils/retry.ts index f0b349310fda..a82426b9290a 100644 --- a/packages/ai/src/utils/retry.ts +++ b/packages/ai/src/utils/retry.ts @@ -139,6 +139,19 @@ export function retryDelayMs(policy: Pick, + attempt: number, + random: () => number = Math.random, +): number { + const delay = retryDelayMs(policy, attempt); + return Math.round(delay / 2 + (delay / 2) * random()); +} + /** Optional callbacks emitted by {@link retryAssistantCall} around each retry. */ export interface RetryCallbacks { /** Emitted before the backoff sleep of each retry attempt (1-indexed). */ diff --git a/packages/ai/test/retry.test.ts b/packages/ai/test/retry.test.ts index 3b2be23c11dc..715009d766b7 100644 --- a/packages/ai/test/retry.test.ts +++ b/packages/ai/test/retry.test.ts @@ -1,6 +1,12 @@ import { describe, expect, it, vi } from "vitest"; import { fauxAssistantMessage } from "../src/providers/faux.ts"; -import { isRetryableAssistantError, type RetryPolicy, retryAssistantCall, retryDelayMs } from "../src/utils/retry.ts"; +import { + isRetryableAssistantError, + jitteredRetryDelayMs, + type RetryPolicy, + retryAssistantCall, + retryDelayMs, +} from "../src/utils/retry.ts"; const openAIExplicitRetryMessage = "An error occurred while processing your request. You can retry your request, or contact us through our help center at help.openai.com if the error persists. Please include the request ID req_******** in your message."; @@ -151,6 +157,18 @@ describe("retryDelayMs", () => { }); }); +describe("jitteredRetryDelayMs", () => { + it("keeps the delay between half and all of the unjittered one", () => { + expect(jitteredRetryDelayMs({ baseDelayMs: 2000 }, 3, () => 0)).toBe(4000); + expect(jitteredRetryDelayMs({ baseDelayMs: 2000 }, 3, () => 1)).toBe(8000); + expect(jitteredRetryDelayMs({ baseDelayMs: 2000 }, 3, () => 0.5)).toBe(6000); + }); + + it("still respects the cap", () => { + expect(jitteredRetryDelayMs({ baseDelayMs: 2000, maxAgentDelayMs: 5000 }, 5, () => 1)).toBe(5000); + }); +}); + describe("retryAssistantCall", () => { const disabled: RetryPolicy = { enabled: false, maxRetries: 3, baseDelayMs: 0 }; const enabled: RetryPolicy = { enabled: true, maxRetries: 3, baseDelayMs: 0 };