From d9f90696d79069c1225dec03a5d0f0bc775ddca1 Mon Sep 17 00:00:00 2001 From: shubhhh19 Date: Thu, 16 Oct 2025 16:03:39 -0400 Subject: [PATCH 1/2] chore: upgrade httpx-aiohttp from 0.1.8 to 0.1.9 - Update pyproject.toml to require httpx_aiohttp>=0.1.9 - Update requirements.lock and requirements-dev.lock to use httpx-aiohttp==0.1.9 Fixes #2688 --- pyproject.toml | 2 +- requirements-dev.lock | 2 +- requirements.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 43de9882f2..35cfadd9c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ Repository = "https://github.com/openai/openai-python" openai = "openai.cli:main" [project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] realtime = ["websockets >= 13, < 16"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] diff --git a/requirements-dev.lock b/requirements-dev.lock index e2446f2222..b454537b96 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -79,7 +79,7 @@ httpx==0.28.1 # via httpx-aiohttp # via openai # via respx -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.9 # via openai idna==3.4 # via anyio diff --git a/requirements.lock b/requirements.lock index aaca0834db..b047cb3f88 100644 --- a/requirements.lock +++ b/requirements.lock @@ -45,7 +45,7 @@ httpcore==1.0.9 httpx==0.28.1 # via httpx-aiohttp # via openai -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.9 # via openai idna==3.4 # via anyio From 826533d6baa2e7c466e4446534e04f771557b982 Mon Sep 17 00:00:00 2001 From: shubhhh19 Date: Fri, 17 Oct 2025 17:06:28 -0400 Subject: [PATCH 2/2] chore: upgrade httpx-aiohttp from 0.1.8 to 0.1.9 - Update pyproject.toml to require httpx_aiohttp>=0.1.9 - Update requirements.lock and requirements-dev.lock to use httpx-aiohttp==0.1.9 - Add example demonstrating aiohttp backend usage and timeout handling Fixes #2688 --- examples/aiohttp_timeout_demo.py | 179 +++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 examples/aiohttp_timeout_demo.py diff --git a/examples/aiohttp_timeout_demo.py b/examples/aiohttp_timeout_demo.py new file mode 100644 index 0000000000..8bc6df9fb3 --- /dev/null +++ b/examples/aiohttp_timeout_demo.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 + +import asyncio +import os +import time +from typing import Optional + +from openai import AsyncOpenAI, DefaultAioHttpClient +from openai._exceptions import APITimeoutError, APIStatusError + + +async def basic_aiohttp_example(): + """Test basic aiohttp connectivity.""" + print("Basic aiohttp backend example") + + api_key = os.environ.get("OPENAI_API_KEY") + if not api_key: + print("Please set OPENAI_API_KEY environment variable") + return + + async with AsyncOpenAI( + api_key=api_key, + http_client=DefaultAioHttpClient(), + timeout=30.0, + max_retries=2 + ) as client: + try: + models = await client.models.list() + print(f"Connected successfully! Found {len(models.data)} models.") + + except APITimeoutError as e: + print(f"Request timed out: {e}") + except APIStatusError as e: + print(f"API error: {e.status_code} - {e}") + except Exception as e: + print(f"Unexpected error: {e}") + + +async def concurrent_requests_example(): + """Test concurrent requests.""" + print("\nConcurrent requests example") + + api_key = os.environ.get("OPENAI_API_KEY") + if not api_key: + print("Please set OPENAI_API_KEY environment variable") + return + + async with AsyncOpenAI( + api_key=api_key, + http_client=DefaultAioHttpClient(), + timeout=60.0, + max_retries=3 + ) as client: + tasks = [client.models.list() for _ in range(3)] + + try: + start_time = time.time() + results = await asyncio.gather(*tasks, return_exceptions=True) + end_time = time.time() + + successful = sum(1 for r in results if not isinstance(r, Exception)) + failed = len(results) - successful + + print(f"Completed {len(tasks)} concurrent requests in {end_time - start_time:.2f}s") + print(f"Successful: {successful}, Failed: {failed}") + + for i, result in enumerate(results): + if isinstance(result, Exception): + print(f"Request {i} error: {result}") + + except Exception as e: + print(f"Concurrent requests failed: {e}") + + +async def timeout_handling_example(): + """Test timeout handling.""" + print("\nTimeout handling example") + + api_key = os.environ.get("OPENAI_API_KEY") + if not api_key: + print("Please set OPENAI_API_KEY environment variable") + return + + async with AsyncOpenAI( + api_key=api_key, + http_client=DefaultAioHttpClient(), + timeout=1.0, + max_retries=0 + ) as client: + try: + print("Making request with 1-second timeout...") + await client.models.list() + print("Request completed within timeout") + + except APITimeoutError: + print("Timeout properly caught and handled") + except Exception as e: + print(f"Unexpected error: {e}") + + +async def production_ready_example(): + """Test production-ready error handling.""" + print("\nProduction-ready example") + + api_key = os.environ.get("OPENAI_API_KEY") + if not api_key: + print("Please set OPENAI_API_KEY environment variable") + return + + async def make_request_with_retries(client: AsyncOpenAI, max_attempts: int = 3) -> Optional[dict]: + for attempt in range(max_attempts): + try: + response = await client.models.list() + return {"success": True, "data": response.data} + + except APITimeoutError as e: + print(f"Attempt {attempt + 1}: Timeout - {e}") + if attempt < max_attempts - 1: + wait_time = 2 ** attempt + print(f"Waiting {wait_time}s before retry...") + await asyncio.sleep(wait_time) + else: + return {"success": False, "error": "Max retries exceeded", "type": "timeout"} + + except APIStatusError as e: + if e.status_code == 429: + wait_time = 2 ** attempt + print(f"Attempt {attempt + 1}: Rate limited - waiting {wait_time}s") + await asyncio.sleep(wait_time) + else: + return {"success": False, "error": str(e), "type": "api_error"} + + except Exception as e: + return {"success": False, "error": str(e), "type": "unexpected"} + + return {"success": False, "error": "Max attempts exceeded", "type": "unknown"} + + async with AsyncOpenAI( + api_key=api_key, + http_client=DefaultAioHttpClient(), + timeout=30.0, + max_retries=0 + ) as client: + result = await make_request_with_retries(client) + + if result["success"]: + print(f"Production request successful! Found {len(result['data'])} models.") + else: + print(f"Production request failed: {result['error']} ({result['type']})") + + +async def main(): + """Run all examples.""" + print("=" * 60) + print("OpenAI aiohttp Backend Examples (httpx-aiohttp 0.1.9)") + print("=" * 60) + print("This demonstrates the timeout fixes in httpx-aiohttp 0.1.9") + print("") + + try: + import httpx_aiohttp + print("httpx-aiohttp is installed") + except ImportError: + print("httpx-aiohttp not installed. Install with: pip install openai[aiohttp]") + return + + await basic_aiohttp_example() + await concurrent_requests_example() + await timeout_handling_example() + await production_ready_example() + + print("\n" + "=" * 60) + print("All examples completed!") + print("The httpx-aiohttp 0.1.9 upgrade fixes production timeout issues.") + print("=" * 60) + + +if __name__ == "__main__": + asyncio.run(main())