ai-lawyer-agent/api/fetch_api_data.py
2026-03-15 22:45:33 +01:00

66 lines
2.4 KiB
Python

import httpx
import logging
from pydantic import BaseModel
from cachetools import TTLCache
from tenacity import retry, stop_after_attempt, wait_exponential
from api.config import HTTP_TIMEOUT, HTTP_MAX_CONNECTIONS, HTTP_MAX_KEEPALIVE, CACHE_TTL, CACHE_MAX_SIZE
_cache = TTLCache(maxsize=CACHE_MAX_SIZE, ttl=CACHE_TTL)
_client = httpx.AsyncClient(
timeout=httpx.Timeout(HTTP_TIMEOUT),
limits=httpx.Limits(max_connections=HTTP_MAX_CONNECTIONS,
max_keepalive_connections=HTTP_MAX_KEEPALIVE),
)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def docstring_from_model(model: type[BaseModel]):
def decorator(func):
if func.__doc__:
func.__doc__ = func.__doc__.format(
params="\n".join(
f"\t\t- {name}: {field.description or 'No description'}"
for name, field in model.model_fields.items()
)
)
return func
return decorator
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=5))
async def fetch_api_data(icon: str, url: str, params: dict, remove_keys: list = None) -> dict:
try:
cache_key = f"{url}:{sorted(params.items())}"
if cache_key in _cache:
logger.info(f"\n💾 {icon}Cache hit: {cache_key}\n")
return _cache[cache_key]
logger.info(f"\n🔨 {icon} Input parameters: {params}\n")
response = await _client.get(url, params=params)
response.raise_for_status()
logger.debug(f"\n🖇️ {icon} Request URL: {response.url}\n")
data = response.json()
if remove_keys and isinstance(data, dict):
for key in remove_keys:
data.pop(key, None)
logger.info(f"\n{icon} Success: {url}")
_cache[cache_key] = data
return data
except httpx.HTTPStatusError as e:
logger.error(f"\n{icon}HTTP error: {e.response.status_code} - {e.response.text}\n")
return {"error": "http_error", "status_code": e.response.status_code}
except httpx.RequestError as e:
logger.error(f"\n{icon}Request error: {str(e)}\n")
return {"error": "request_error", "status_code": str(e)}
except Exception as e:
logger.critical(f"\n{icon}Unexpected error: {str(e)}\n", exc_info=True)
return {"error": "unexpected_error", "status_code": str(e)}