from __future__ import annotations import asyncio, json, time from app.services.storage import async_session_maker from app.models.request_log import RequestLog _buffer: list[dict] = [] _lock: asyncio.Lock = asyncio.Lock() _flush_task: asyncio.Task | None = None FLUSH_INTERVAL = 2.0 FLUSH_BATCH_SIZE = 100 async def log(entry: dict) -> None: async with _lock: _buffer.append(entry) if len(_buffer) >= FLUSH_BATCH_SIZE: asyncio.create_task(flush()) async def flush() -> None: async with _lock: entries = _buffer[:] _buffer.clear() if not entries: return try: async with async_session_maker() as session: for e in entries: session.add(RequestLog( request_id=e.get("request_id", ""), provider=e.get("provider", ""), model=e.get("model", ""), endpoint=e.get("endpoint", ""), key_id=e.get("key_id"), hub_key_id=e.get("hub_key_id"), tokens_in=e.get("tokens_in", 0), tokens_out=e.get("tokens_out", 0), cost=e.get("cost", 0), latency_ms=e.get("latency_ms", 0), status_code=e.get("status_code", 200), error=e.get("error"), cached=e.get("cached", 0), cache_type=e.get("cache_type"), streaming=e.get("streaming", 0), fallback_count=e.get("fallback_count", 0), ip=e.get("ip"), session_id=e.get("session_id"), injection_flagged=e.get("injection_flagged", 0), has_images=e.get("has_images", 0), ab_variant=e.get("ab_variant"), cache_read_tokens=e.get("cache_read_tokens", 0), cache_write_tokens=e.get("cache_write_tokens", 0), )) await session.commit() except Exception as exc: print(f"[batch_logger] Flush error: {exc}") async def _periodic_flush(): while True: await asyncio.sleep(FLUSH_INTERVAL) async with _lock: if _buffer: await flush() def start() -> None: global _flush_task if _flush_task is None or _flush_task.done(): _flush_task = asyncio.create_task(_periodic_flush())