mager-bench

Llama 3.3 70B × async-fetch

5.7

Async concurrency patterns + retry/timeout handling

correctness 4.0quality 6.0documentation 7.01760ms
$ cat async-fetch.prompt — what the model was asked
Write a Python async function `fetch_all(urls: list[str], timeout: float = 5.0, retries: int = 2) -> list[dict]` that fetches all URLs concurrently using `aiohttp` and returns a list of result dicts.

Each result dict must have:
  - `url`: the original URL
  - `status`: HTTP status code (int), or None on error
  - `body`: response text (str), or None on error
  - `error`: error message string, or None on success

Requirements:
1. All URLs must be fetched concurrently (not sequentially)
2. Each fetch must respect the `timeout` (per-request, not total)
3. Retry up to `retries` times on network errors or 5xx responses (exponential backoff: 0.5s, 1s, ...)
4. Never raise — errors go into the result dict
5. Type-hint everything; add a docstring
6. Show a `__main__` block that fetches 3 URLs and prints results

run 1 of 1

5.7
correctness 4.0quality 6.0documentation 7.01760ms

# judge: Critical bug: timeouts raise asyncio.TimeoutError which is not caught by the except aiohttp.ClientError clause, and gather() lacks return_exceptions=True, so the function can still raise on timeout, violating the 'never raise' requirement; also timeout is passed as a raw float rather than an explicit aiohttp.ClientTimeout object as the rubric expects, though backoff and session reuse are implemented correctly.

$ cat llama-3.3-70b.async-fetch.r1.response
```python
import aiohttp
import asyncio
from typing import List, Dict

async def fetch_url(session: aiohttp.ClientSession, url: str, timeout: float, retries: int) -> Dict:
    """Fetch a single URL with retries and timeout."""
    for attempt in range(retries + 1):
        try:
            async with session.get(url, timeout=timeout) as response:
                status = response.status
                body = await response.text()
                if 500 <= status < 600:  # 5xx response
                    if attempt < retries:
                        await asyncio.sleep(2 ** attempt * 0.5)  # exponential backoff
                        continue
                return {
                    'url': url,
                    'status': status,
                    'body': body,
                    'error': None,
                }
        except aiohttp.ClientError as e:
            if attempt < retries:
                await asyncio.sleep(2 ** attempt * 0.5)  # exponential backoff
                continue
            return {
                'url': url,
                'status': None,
                'body': None,
                'error': str(e),
            }
    # This should not happen, as we already handled all attempts
    return {
        'url': url,
        'status': None,
        'body': None,
        'error': 'Failed after all retries',
    }

async def fetch_all(urls: List[str], timeout: float = 5.0, retries: int = 2) -> List[Dict]:
    """
    Fetch all URLs concurrently using aiohttp and return a list of result dicts.

    Args:
    - urls: List of URLs to fetch
    - timeout: Per-request timeout (default: 5.0 seconds)
    - retries: Number of retries on network errors or 5xx responses (default: 2)

    Returns:
    - A list of dicts, each containing:
      - 'url': The original URL
      - 'status': HTTP status code (int), or None on error
      - 'body': Response text (str), or None on error
      - 'error': Error message string, or None on success
    """
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url, timeout, retries) for url in urls]
        return await asyncio.gather(*tasks)

if __name__ == '__main__':
    async def main():
        urls = ['http://example.com', 'http://example.org', 'http://example.net']
        results = await fetch_all(urls)
        for result in results:
            print(f"URL: {result['url']}, Status: {result['status']}, Error: {result['error']}")
            if result['error'] is None:
                print(f"Body: {result['body'][:50]}...")

    asyncio.run(main())
```
In this code, the `fetch_url` function fetches a single URL with retries and timeout. It returns a dict containing the URL, status, body, and error (if any). The `fetch_all` function creates a list of tasks to fetch all URLs concurrently and uses `asyncio.gather` to wait for all tasks to complete.

The `__main__` block demonstrates how to use the `fetch_all` function to fetch 3 URLs and print the results.