44 lines
1.4 KiB
Python
44 lines
1.4 KiB
Python
"""Shared base for LFX + Langflow async HTTP clients.
|
|
|
|
Consolidates base_url stripping, header building, per-request client
|
|
construction, and HTTPError → domain-error mapping. Subclasses only
|
|
declare their error types and endpoint calls. Transport-level failures
|
|
(connect/timeout, never 4xx) retry with exponential backoff via tenacity.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
from tenacity import (
|
|
retry,
|
|
retry_if_exception_type,
|
|
stop_after_attempt,
|
|
wait_exponential,
|
|
)
|
|
|
|
|
|
class BaseApiClient:
|
|
"""Thin wrapper around httpx.AsyncClient construction."""
|
|
|
|
def __init__(self, base_url: str, timeout: float = 60.0, api_key: str | None = None) -> None:
|
|
self.base_url = base_url.rstrip("/")
|
|
self.timeout = timeout
|
|
self.api_key = api_key or ""
|
|
|
|
def _headers(self) -> dict[str, str]:
|
|
return {"x-api-key": self.api_key} if self.api_key else {}
|
|
|
|
def _client(self) -> httpx.AsyncClient:
|
|
return httpx.AsyncClient(
|
|
base_url=self.base_url, timeout=self.timeout, headers=self._headers()
|
|
)
|
|
|
|
|
|
def transport_retry(func): # type: ignore[no-untyped-def]
|
|
"""Retry transport errors 3x with backoff; HTTP status errors pass through."""
|
|
return retry(
|
|
stop=stop_after_attempt(3),
|
|
wait=wait_exponential(multiplier=0.5, max=4),
|
|
retry=retry_if_exception_type((httpx.TransportError, httpx.TimeoutException)),
|
|
reraise=True,
|
|
)(func)
|