"""Proxy server to OpenHands Cloud API""" import os from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware import httpx app = FastAPI(title="OpenHands Backend", version="1.0.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) OPENHANDS_API_KEY = os.getenv("OPENHANDS_API_KEY", "") OPENHANDS_BASE_URL = os.getenv("OPENHANDS_BASE_URL", "https://app.all-hands.dev") def get_headers(): return {"Authorization": f"Bearer {OPENHANDS_API_KEY}", "Content-Type": "application/json"} @app.get("/health") async def health(): return {"status": "ok", "backend": "cloud-proxy", "version": "1.0.0"} @app.get("/ready") async def ready(): return {"status": "ready", "service": "openhands-cloud-proxy", "api_key_configured": bool(OPENHANDS_API_KEY)} @app.get("/server_info") async def server_info(): return { "version": "1.0.0", "llm_proxy": os.getenv("LLM_PROXY_URL", "https://llm-proxy.app.all-hands.dev"), "e2b_enabled": bool(os.getenv("E2B_API_KEY")), "redis_enabled": False, "supabase_enabled": False, "openhands_cloud": True, } @app.get("/api/conversations") async def list_conversations(): if not OPENHANDS_API_KEY: # Return mock data for demo return {"conversations": [], "mock": True} async with httpx.AsyncClient(timeout=30.0) as client: resp = await client.get( f"{OPENHANDS_BASE_URL}/api/conversations", headers=get_headers() ) resp.raise_for_status() return resp.json() @app.post("/api/conversations") async def create_conversation(data: dict): if not OPENHANDS_API_KEY: # Return mock for demo import uuid return {"conversation_id": str(uuid.uuid4()), "message": "Mock response - configure API key for real agent", "mock": True} async with httpx.AsyncClient(timeout=60.0) as client: resp = await client.post( f"{OPENHANDS_BASE_URL}/api/conversations", headers=get_headers(), json=data ) resp.raise_for_status() return resp.json() @app.get("/api/conversations/{conv_id}") async def get_conversation(conv_id: str): if not OPENHANDS_API_KEY: return {"id": conv_id, "messages": [], "mock": True} async with httpx.AsyncClient(timeout=30.0) as client: resp = await client.get( f"{OPENHANDS_BASE_URL}/api/conversations/{conv_id}", headers=get_headers() ) resp.raise_for_status() return resp.json() @app.websocket("/ws/conversations/{conv_id}/events") async def websocket_events(conv_id: str, websocket): await websocket.accept() if not OPENHANDS_API_KEY: await websocket.send_json({"type": "error", "message": "API key not configured"}) await websocket.close() return # Proxy to OpenHands Cloud WebSocket async with httpx.AsyncClient(timeout=httpx.Timeout(300.0)) as client: async with client.stream( "GET", f"{OPENHANDS_BASE_URL}/ws/conversations/{conv_id}/events", headers={"Authorization": f"Bearer {OPENHANDS_API_KEY}"} ) as resp: async for line in resp.aiter_lines(): if line: await websocket.send_text(line)