import asyncio from datetime import datetime, timezone import logging import os from typing import Any, Dict import httpx from .redis_jobs import close_redis, dequeue_job, get_job, update_job, wait_for_redis logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s") logger = logging.getLogger("daarion-gateway-worker") ROUTER_BASE_URL = os.getenv("ROUTER_BASE_URL", os.getenv("ROUTER_URL", "http://router:8000")) ROUTER_TIMEOUT_SECONDS = float(os.getenv("ROUTER_WORKER_TIMEOUT", "60")) def _now() -> str: return datetime.now(timezone.utc).isoformat() async def _call_router(agent_id: str, input_payload: Dict[str, Any], metadata: Dict[str, Any]) -> Dict[str, Any]: body: Dict[str, Any] = { "prompt": input_payload.get("prompt", ""), "metadata": metadata or {}, } images = input_payload.get("images") or [] if images: body["images"] = images url = f"{ROUTER_BASE_URL}/v1/agents/{agent_id}/infer" async with httpx.AsyncClient(timeout=ROUTER_TIMEOUT_SECONDS) as client: resp = await client.post(url, json=body) resp.raise_for_status() data = resp.json() return { "response": data.get("response", ""), "model": data.get("model"), "backend": data.get("backend"), "tokens_used": data.get("tokens_used"), } async def run_once(job_id: str) -> None: job = await get_job(job_id) if not job: logger.warning("job_missing: %s", job_id) return await update_job(job_id, {"status": "running", "started_at": _now(), "updated_at": _now()}) agent_id = job.get("agent_id") input_payload = job.get("input") or {} metadata = job.get("metadata") or {} try: result = await _call_router(agent_id, input_payload, metadata) await update_job( job_id, { "status": "done", "result": result, "error": None, "finished_at": _now(), "updated_at": _now(), }, ) logger.info("job_done: %s agent=%s", job_id, agent_id) except Exception as e: await update_job( job_id, { "status": "failed", "error": {"type": e.__class__.__name__, "message": str(e)}, "finished_at": _now(), "updated_at": _now(), }, ) logger.exception("job_failed: %s agent=%s", job_id, agent_id) async def worker_loop() -> None: await wait_for_redis(60) logger.info("worker_started router=%s", ROUTER_BASE_URL) while True: try: job_id = await dequeue_job(block_seconds=10) if not job_id: continue await run_once(job_id) except asyncio.CancelledError: raise except Exception: logger.exception("worker_loop_error") await asyncio.sleep(1) if __name__ == "__main__": try: asyncio.run(worker_loop()) finally: try: asyncio.run(close_redis()) except Exception: pass