Implement Apex Agent MVP scaffold
This commit is contained in:
+109
@@ -0,0 +1,109 @@
|
||||
from typing import Any
|
||||
|
||||
from api.errors import semantic_error
|
||||
from core.device_manager import DEFAULT_MANAGER, DeviceManager
|
||||
from core.models import Task
|
||||
from runtime.executor import Executor, default_tool_registry
|
||||
from runtime.task import TaskRunner
|
||||
from storage.task_metadata import TaskMetadataStore
|
||||
from tools.launch_app import launch_app
|
||||
from tools.screenshot import take_screenshot
|
||||
from tools.tap import tap
|
||||
|
||||
|
||||
def create_app(
|
||||
*,
|
||||
manager: DeviceManager | None = None,
|
||||
task_runner: TaskRunner | None = None,
|
||||
metadata_store: TaskMetadataStore | None = None,
|
||||
) -> Any:
|
||||
from fastapi import BackgroundTasks, FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
device_manager = manager or DEFAULT_MANAGER
|
||||
store = metadata_store or TaskMetadataStore()
|
||||
runner = task_runner or TaskRunner(
|
||||
metadata_store=store,
|
||||
executor=Executor(tools=default_tool_registry(manager=device_manager)),
|
||||
)
|
||||
app = FastAPI(title="Apex Agent API")
|
||||
|
||||
class TapRequest(BaseModel):
|
||||
x: float
|
||||
y: float
|
||||
|
||||
class ScreenshotResponse(BaseModel):
|
||||
image_base64: str
|
||||
mime_type: str = "image/png"
|
||||
|
||||
class LaunchRequest(BaseModel):
|
||||
app_id: str
|
||||
|
||||
class AgentTaskRequest(BaseModel):
|
||||
goal: str
|
||||
device_id: str
|
||||
|
||||
@app.get("/devices")
|
||||
def devices() -> list[dict[str, Any]]:
|
||||
return [device.to_dict() for device in device_manager.list_devices()]
|
||||
|
||||
@app.post("/devices/{device_id}/tap")
|
||||
def tap_device(device_id: str, request: TapRequest) -> dict[str, Any]:
|
||||
return _raise_semantic(
|
||||
lambda: tap(
|
||||
request.x,
|
||||
request.y,
|
||||
device_id=device_id,
|
||||
manager=device_manager,
|
||||
)
|
||||
)
|
||||
|
||||
@app.post("/devices/{device_id}/screenshot")
|
||||
def screenshot_device(device_id: str) -> dict[str, str]:
|
||||
import base64
|
||||
|
||||
image = _raise_semantic(
|
||||
lambda: take_screenshot(device_id, manager=device_manager)
|
||||
)
|
||||
response = ScreenshotResponse(
|
||||
image_base64=base64.b64encode(image).decode("ascii")
|
||||
)
|
||||
return response.model_dump()
|
||||
|
||||
@app.post("/devices/{device_id}/launch")
|
||||
def launch_device(device_id: str, request: LaunchRequest) -> dict[str, Any]:
|
||||
return _raise_semantic(
|
||||
lambda: launch_app(
|
||||
request.app_id,
|
||||
device_id=device_id,
|
||||
manager=device_manager,
|
||||
)
|
||||
)
|
||||
|
||||
@app.post("/agent/task")
|
||||
def start_task(
|
||||
request: AgentTaskRequest,
|
||||
background_tasks: BackgroundTasks,
|
||||
) -> dict[str, str]:
|
||||
task = Task(goal=request.goal, device_id=request.device_id)
|
||||
store.create_task(task)
|
||||
background_tasks.add_task(runner.run, task)
|
||||
return {"task_id": task.id, "status": task.status}
|
||||
|
||||
@app.get("/task/{task_id}")
|
||||
def get_task(task_id: str) -> dict[str, Any]:
|
||||
task = store.get_task(task_id)
|
||||
if task is None:
|
||||
raise HTTPException(status_code=404, detail="task not found")
|
||||
return task
|
||||
|
||||
return app
|
||||
|
||||
|
||||
def _raise_semantic(func: Any) -> Any:
|
||||
from fastapi import HTTPException
|
||||
|
||||
try:
|
||||
return func()
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail=semantic_error(exc)) from exc
|
||||
Reference in New Issue
Block a user