chatbot1-7 Exercise 2: Predicting the Waits ============================================== Predictions (with rng=lambda: 1.0 the jitter picks the longest allowed wait, which is min(max_delay, base_delay x 2^(attempt-1))): 1. base 0.5, cap 8, five failures: 0.5, 1, 2, 4, 8 2. base 1.0, cap 3, five failures: 1, 2, 3, 3, 3 (the cap stops the doubling at 3) 3. Retry-After 2, then a server error: 2 (the server's number is used as it is), then 0.5 x 2^1 = 1.0 4. max_attempts=1: no waits, the first error is raised ex2.py (save in the project root): import asyncio from bot.providers import FakeProvider, RateLimited, ServerError from bot.reliability import RetryingProvider MSG = [{"role": "user", "content": "hi"}] class Sleeps: def __init__(self): self.delays = [] async def __call__(self, s): self.delays.append(s) async def waits(label, script, **kw): sleeps = Sleeps() p = RetryingProvider(FakeProvider(script), sleep=sleeps, rng=lambda: 1.0, **kw) # rng=1.0: always the longest allowed wait try: await p.complete(MSG) end = "answered" except Exception as e: end = f"raised {type(e).__name__}" print(f"{label:<54} waits={sleeps.delays} -> {end}") async def main(): await waits("base 0.5, cap 8, 5 failures then success (6 attempts)", [ServerError("x")] * 5 + ["ok"], max_attempts=6) await waits("base 1.0, cap 3, 5 failures then success", [ServerError("x")] * 5 + ["ok"], max_attempts=6, base_delay=1.0, max_delay=3.0) await waits("Retry-After 2 on the first failure, then ServerError", [RateLimited("x", retry_after=2.0), ServerError("y"), "ok"], max_attempts=4) await waits("max_attempts=1 (retries switched off)", [ServerError("x"), "ok"], max_attempts=1) asyncio.run(main()) Run with: py ex2.py Output: base 0.5, cap 8, 5 failures then success (6 attempts) waits=[0.5, 1.0, 2.0, 4.0, 8.0] -> answered base 1.0, cap 3, 5 failures then success waits=[1.0, 2.0, 3.0, 3.0, 3.0] -> answered Retry-After 2 on the first failure, then ServerError waits=[2.0, 1.0] -> answered max_attempts=1 (retries switched off) waits=[] -> raised ServerError WHY THIS WORKS AS AN ANSWER --------------------------- All four predictions match. The second case shows the cap at work: the doubling would have asked for 1, 2, 4, 8, 16, but no wait ever exceeds 3. The third shows that Retry-After replaces the computed wait for that attempt only; the next attempt goes back to backoff, using the attempt number (so its cap is 0.5 x 2 = 1.0). The fourth shows that a bound of one attempt switches retries off, and the failure is raised unchanged. In real use rng is random, so each wait is a random number between 0 and the values predicted here.