From 2534a8036fc0a06fe485984a6db566c77117789c Mon Sep 17 00:00:00 2001 From: Gilad Ivry Date: Mon, 12 Jan 2026 16:10:26 +0200 Subject: [PATCH 1/3] added dockerfile and agentcore required apis --- Dockerfile | 34 +++++++++++++++++++++++++++++ rogue/server/api/agentcore.py | 26 ++++++++++++++++++++++ rogue/server/api/evaluation.py | 40 +++++++++++++++++++++++----------- rogue/server/main.py | 2 ++ 4 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 Dockerfile create mode 100644 rogue/server/api/agentcore.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..b255d731 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM python:3.11-slim + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 + +WORKDIR /app + +# curl is used for the container healthcheck +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl \ + && rm -rf /var/lib/apt/lists/* + +# Rogue uses uv + uv.lock for reproducible installs +RUN pip install --no-cache-dir uv + +# Copy dependency manifests first for better caching +COPY pyproject.toml uv.lock ./ +COPY .python-version* ./ + +# Copy the repo +COPY . . + +# Install deps from lockfile +RUN uv sync --locked --no-dev + +# AgentCore HTTP runtime expects the service to listen on 8080 +EXPOSE 8080 + +# Optional but recommended: container-level healthcheck uses AgentCore /ping +HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ + CMD curl -fsS http://localhost:8080/ping || exit 1 + +# Start the existing server on the required host/port +CMD ["uv", "run", "python", "-m", "rogue.run_server", "--host", "0.0.0.0", "--port", "8080"] \ No newline at end of file diff --git a/rogue/server/api/agentcore.py b/rogue/server/api/agentcore.py new file mode 100644 index 00000000..f900ff4d --- /dev/null +++ b/rogue/server/api/agentcore.py @@ -0,0 +1,26 @@ +from fastapi import APIRouter, BackgroundTasks, Depends +from rogue_sdk.types import EvaluationRequest, EvaluationResponse +from .evaluation import get_evaluation_service, enqueue_evaluation +from ..services.evaluation_service import EvaluationService + +router = APIRouter(tags=["agentcore"]) + + +@router.get("/ping") +def ping(): + return {"status": "Healthy"} + + +@router.post("/invocations", response_model=EvaluationResponse) +async def invocations( + request: EvaluationRequest, + background_tasks: BackgroundTasks, + evaluation_service: EvaluationService = Depends(get_evaluation_service), +): + # different entrypoint to /evaluations to meet agentcore convention + return await enqueue_evaluation( + request=request, + background_tasks=background_tasks, + evaluation_service=evaluation_service, + endpoint="/invocations", + ) diff --git a/rogue/server/api/evaluation.py b/rogue/server/api/evaluation.py index 7cea63eb..56f0ce27 100644 --- a/rogue/server/api/evaluation.py +++ b/rogue/server/api/evaluation.py @@ -24,11 +24,11 @@ def get_evaluation_service(): return EvaluationService() -@router.post("", response_model=EvaluationResponse) -async def create_evaluation( - request: EvaluationRequest, - background_tasks: BackgroundTasks, - evaluation_service: EvaluationService = Depends(get_evaluation_service), +async def enqueue_evaluation( + request: EvaluationRequest, + background_tasks: BackgroundTasks, + evaluation_service: EvaluationService, + endpoint: str, ): job_id = str(uuid.uuid4()) @@ -82,12 +82,26 @@ async def create_evaluation( ) +@router.post("", response_model=EvaluationResponse) +async def create_evaluation( + request: EvaluationRequest, + background_tasks: BackgroundTasks, + evaluation_service: EvaluationService = Depends(get_evaluation_service), +): + return await enqueue_evaluation( + request=request, + background_tasks=background_tasks, + evaluation_service=evaluation_service, + endpoint="/evaluations", + ) + + @router.get("", response_model=JobListResponse) async def list_evaluations( - status: Optional[EvaluationStatus] = None, - limit: int = 50, - offset: int = 0, - evaluation_service: EvaluationService = Depends(get_evaluation_service), + status: Optional[EvaluationStatus] = None, + limit: int = 50, + offset: int = 0, + evaluation_service: EvaluationService = Depends(get_evaluation_service), ): jobs = await evaluation_service.get_jobs( status=status, @@ -101,8 +115,8 @@ async def list_evaluations( @router.get("/{job_id}", response_model=EvaluationJob) async def get_evaluation( - job_id: str, - evaluation_service: EvaluationService = Depends(get_evaluation_service), + job_id: str, + evaluation_service: EvaluationService = Depends(get_evaluation_service), ): job = await evaluation_service.get_job(job_id) logger.info(f"Job: {job}") @@ -113,8 +127,8 @@ async def get_evaluation( @router.delete("/{job_id}") async def cancel_evaluation( - job_id: str, - evaluation_service: EvaluationService = Depends(get_evaluation_service), + job_id: str, + evaluation_service: EvaluationService = Depends(get_evaluation_service), ): success = await evaluation_service.cancel_job(job_id) if not success: diff --git a/rogue/server/main.py b/rogue/server/main.py index bd1a4c65..0c4b372f 100644 --- a/rogue/server/main.py +++ b/rogue/server/main.py @@ -28,6 +28,7 @@ from .api.llm import router as llm_router from .api.red_team import router as red_team_router from .websocket.manager import websocket_router +from .api.agentcore import router as ac_router logger = get_logger(__name__) @@ -67,6 +68,7 @@ def create_app() -> FastAPI: app.include_router(llm_router, prefix="/api/v1") app.include_router(interview_router, prefix="/api/v1") app.include_router(websocket_router, prefix="/api/v1") + app.include_router(ac_router, prefix="") return app From 59cf6642cc4ec96489d1859f8bcc61f44c761476 Mon Sep 17 00:00:00 2001 From: Gilad Ivry Date: Mon, 12 Jan 2026 16:25:12 +0200 Subject: [PATCH 2/3] black --- rogue/server/api/agentcore.py | 6 +++--- rogue/server/api/evaluation.py | 30 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/rogue/server/api/agentcore.py b/rogue/server/api/agentcore.py index f900ff4d..55f10866 100644 --- a/rogue/server/api/agentcore.py +++ b/rogue/server/api/agentcore.py @@ -13,9 +13,9 @@ def ping(): @router.post("/invocations", response_model=EvaluationResponse) async def invocations( - request: EvaluationRequest, - background_tasks: BackgroundTasks, - evaluation_service: EvaluationService = Depends(get_evaluation_service), + request: EvaluationRequest, + background_tasks: BackgroundTasks, + evaluation_service: EvaluationService = Depends(get_evaluation_service), ): # different entrypoint to /evaluations to meet agentcore convention return await enqueue_evaluation( diff --git a/rogue/server/api/evaluation.py b/rogue/server/api/evaluation.py index 56f0ce27..da72aa1c 100644 --- a/rogue/server/api/evaluation.py +++ b/rogue/server/api/evaluation.py @@ -25,10 +25,10 @@ def get_evaluation_service(): async def enqueue_evaluation( - request: EvaluationRequest, - background_tasks: BackgroundTasks, - evaluation_service: EvaluationService, - endpoint: str, + request: EvaluationRequest, + background_tasks: BackgroundTasks, + evaluation_service: EvaluationService, + endpoint: str, ): job_id = str(uuid.uuid4()) @@ -84,9 +84,9 @@ async def enqueue_evaluation( @router.post("", response_model=EvaluationResponse) async def create_evaluation( - request: EvaluationRequest, - background_tasks: BackgroundTasks, - evaluation_service: EvaluationService = Depends(get_evaluation_service), + request: EvaluationRequest, + background_tasks: BackgroundTasks, + evaluation_service: EvaluationService = Depends(get_evaluation_service), ): return await enqueue_evaluation( request=request, @@ -98,10 +98,10 @@ async def create_evaluation( @router.get("", response_model=JobListResponse) async def list_evaluations( - status: Optional[EvaluationStatus] = None, - limit: int = 50, - offset: int = 0, - evaluation_service: EvaluationService = Depends(get_evaluation_service), + status: Optional[EvaluationStatus] = None, + limit: int = 50, + offset: int = 0, + evaluation_service: EvaluationService = Depends(get_evaluation_service), ): jobs = await evaluation_service.get_jobs( status=status, @@ -115,8 +115,8 @@ async def list_evaluations( @router.get("/{job_id}", response_model=EvaluationJob) async def get_evaluation( - job_id: str, - evaluation_service: EvaluationService = Depends(get_evaluation_service), + job_id: str, + evaluation_service: EvaluationService = Depends(get_evaluation_service), ): job = await evaluation_service.get_job(job_id) logger.info(f"Job: {job}") @@ -127,8 +127,8 @@ async def get_evaluation( @router.delete("/{job_id}") async def cancel_evaluation( - job_id: str, - evaluation_service: EvaluationService = Depends(get_evaluation_service), + job_id: str, + evaluation_service: EvaluationService = Depends(get_evaluation_service), ): success = await evaluation_service.cancel_job(job_id) if not success: From d4975e1ecfdf40c44bc6e637aa533aa4f71c43e8 Mon Sep 17 00:00:00 2001 From: Gilad Ivry Date: Tue, 13 Jan 2026 09:59:15 +0200 Subject: [PATCH 3/3] log endpoint --- rogue/server/api/evaluation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rogue/server/api/evaluation.py b/rogue/server/api/evaluation.py index da72aa1c..57e7c22c 100644 --- a/rogue/server/api/evaluation.py +++ b/rogue/server/api/evaluation.py @@ -46,7 +46,7 @@ async def enqueue_evaluation( # Build extra logging info extra_info = { - "endpoint": "/evaluations", + "endpoint": endpoint, "method": "POST", "agent_url": str(request.agent_config.evaluated_agent_url), "scenario_count": scenario_count,