# agents/core-logic/app.py # OSVauco-NMTMD-GCOS — FastAPI HTTP entrypoint for Cloud Run # Wraps the ADK root_agent with a /run endpoint and a /health check. # Usage: uvicorn app:app --host 0.0.0.0 --port 8080 import os import logging from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException from fastapi.responses import JSONResponse from pydantic import BaseModel logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # ── ADK imports ─────────────────────────────────────────────── try: from google.adk.runners import Runner from google.adk.sessions import InMemorySessionService from google.genai.types import Content, Part from agent import root_agent except ImportError as e: logger.error(f"Failed to import ADK dependencies: {e}") raise # ── Session service (in-memory for stateless Cloud Run) ─────────────── session_service = InMemorySessionService() APP_NAME = os.environ.get("CLOUD_RUN_SERVICE", "gcp-orchestrator") # ── Lifespan ─────────────────────────────────────────────────── @asynccontextmanager async def lifespan(app: FastAPI): logger.info(f"OSVauco agent '{APP_NAME}' starting up") yield logger.info(f"OSVauco agent '{APP_NAME}' shutting down") # ── App ──────────────────────────────────────────────────────── app = FastAPI( title="OSVauco GCP Agent", description="ADK-based multi-agent orchestrator on Cloud Run", version="1.0.0", lifespan=lifespan, ) # ── Models ───────────────────────────────────────────────────── class RunRequest(BaseModel): user_id: str session_id: str message: str class RunResponse(BaseModel): user_id: str session_id: str response: str # ── Routes ───────────────────────────────────────────────────── @app.get("/health") async def health(): """Cloud Run health check endpoint.""" return JSONResponse({"status": "ok", "service": APP_NAME}) @app.post("/run", response_model=RunResponse) async def run(req: RunRequest): """ Run the OSVauco ADK agent for a single turn. Creates (or reuses) a session, sends the user message, and returns the final agent response. """ try: # Create or reuse session try: session = await session_service.get_session( app_name=APP_NAME, user_id=req.user_id, session_id=req.session_id, ) except Exception: session = await session_service.create_session( app_name=APP_NAME, user_id=req.user_id, session_id=req.session_id, ) runner = Runner( agent=root_agent, app_name=APP_NAME, session_service=session_service, ) user_content = Content( role="user", parts=[Part(text=req.message)], ) final_response = "" async for event in runner.run_async( user_id=req.user_id, session_id=req.session_id, new_message=user_content, ): if event.is_final_response() and event.content: for part in event.content.parts: if part.text: final_response += part.text logger.info( f"[{req.user_id}/{req.session_id}] Agent response length: {len(final_response)}" ) return RunResponse( user_id=req.user_id, session_id=req.session_id, response=final_response, ) except Exception as e: logger.error(f"Agent run failed: {e}", exc_info=True) raise HTTPException(status_code=500, detail=str(e))