OpenHands/openhands/app_server/services/httpx_client_injector.py
Tim O'Farrell f292f3a84d
V1 Integration (#11183)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-10-14 02:16:44 +00:00

43 lines
1.6 KiB
Python

from typing import AsyncGenerator
import httpx
from fastapi import Request
from pydantic import BaseModel, Field
from openhands.app_server.services.injector import Injector, InjectorState
HTTPX_CLIENT_ATTR = 'httpx_client'
HTTPX_CLIENT_KEEP_OPEN_ATTR = 'httpx_client_keep_open'
class HttpxClientInjector(BaseModel, Injector[httpx.AsyncClient]):
"""Injector for a httpx client. By keeping a single httpx client alive in the
context of server requests handshakes are minimized while connection pool leaks
are prevented."""
timeout: int = Field(default=15, description='Default timeout on all http requests')
async def inject(
self, state: InjectorState, request: Request | None = None
) -> AsyncGenerator[httpx.AsyncClient, None]:
httpx_client = getattr(state, HTTPX_CLIENT_ATTR, None)
if httpx_client:
yield httpx_client
return
httpx_client = httpx.AsyncClient(timeout=self.timeout)
try:
setattr(state, HTTPX_CLIENT_ATTR, httpx_client)
yield httpx_client
finally:
# If instructed, do not close the httpx client at the end of the request.
if not getattr(state, HTTPX_CLIENT_KEEP_OPEN_ATTR, False):
# Clean up the httpx client from request state
if hasattr(state, HTTPX_CLIENT_ATTR):
delattr(state, HTTPX_CLIENT_ATTR)
await httpx_client.aclose()
def set_httpx_client_keep_open(state: InjectorState, keep_open: bool):
"""Set whether the connection should be kept open after the request terminates."""
setattr(state, HTTPX_CLIENT_KEEP_OPEN_ATTR, keep_open)