add sandbox runtime control endpoints
This commit is contained in:
parent
0ca0bac9bf
commit
1b38bcfeab
17 changed files with 1408 additions and 119 deletions
|
|
@ -1,7 +1,7 @@
|
|||
from fastapi import Depends, Request
|
||||
|
||||
from adapter.di.container import AppContainer
|
||||
from usecase.sandbox import CreateSandbox
|
||||
from usecase.sandbox import CreateSandbox, DeleteSandbox
|
||||
|
||||
APP_CONTAINER_STATE = 'container'
|
||||
APP_CONFIG_STATE = 'config'
|
||||
|
|
@ -18,3 +18,9 @@ def get_create_sandbox(
|
|||
container: AppContainer = Depends(get_container),
|
||||
) -> CreateSandbox:
|
||||
return container.usecases.create_sandbox
|
||||
|
||||
|
||||
def get_delete_sandbox(
|
||||
container: AppContainer = Depends(get_container),
|
||||
) -> DeleteSandbox:
|
||||
return container.usecases.delete_sandbox
|
||||
|
|
|
|||
|
|
@ -1,19 +1,30 @@
|
|||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from adapter.di.container import AppContainer
|
||||
from adapter.http.fastapi.dependencies import (
|
||||
get_container,
|
||||
get_create_sandbox,
|
||||
get_delete_sandbox,
|
||||
)
|
||||
from adapter.http.fastapi.schemas import (
|
||||
CreateSandboxRequest,
|
||||
DeleteSandboxResponse,
|
||||
ErrorResponse,
|
||||
HealthResponse,
|
||||
SandboxEndpointResponse,
|
||||
SandboxSessionResponse,
|
||||
)
|
||||
from domain.error import SandboxError, SandboxStartError
|
||||
from domain.error import SandboxConflictError, SandboxError, SandboxStartError
|
||||
from domain.sandbox import SandboxSession
|
||||
from usecase.sandbox import CreateSandbox, CreateSandboxCommand
|
||||
from usecase.sandbox import (
|
||||
CreateSandbox,
|
||||
CreateSandboxCommand,
|
||||
DeleteSandbox,
|
||||
DeleteSandboxCommand,
|
||||
DeleteSandboxResult,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
|
@ -35,6 +46,7 @@ def health(container: AppContainer = Depends(get_container)) -> HealthResponse:
|
|||
'/create',
|
||||
response_model=SandboxSessionResponse,
|
||||
responses={
|
||||
status.HTTP_409_CONFLICT: {'model': ErrorResponse},
|
||||
status.HTTP_503_SERVICE_UNAVAILABLE: {'model': ErrorResponse},
|
||||
status.HTTP_500_INTERNAL_SERVER_ERROR: {'model': ErrorResponse},
|
||||
},
|
||||
|
|
@ -45,7 +57,18 @@ def create_sandbox(
|
|||
usecase: CreateSandbox = Depends(get_create_sandbox),
|
||||
) -> SandboxSessionResponse:
|
||||
try:
|
||||
session = usecase.execute(CreateSandboxCommand(chat_id=request.chat_id))
|
||||
session = usecase.execute(
|
||||
CreateSandboxCommand(
|
||||
chat_id=request.chat_id,
|
||||
agent_id=request.agent_id,
|
||||
volume_host_path=request.volume_host_path,
|
||||
)
|
||||
)
|
||||
except SandboxConflictError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
except SandboxStartError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
|
|
@ -60,11 +83,55 @@ def create_sandbox(
|
|||
return _to_sandbox_session_response(session)
|
||||
|
||||
|
||||
@router.delete(
|
||||
'/sandboxes/{chat_id}',
|
||||
response_model=DeleteSandboxResponse,
|
||||
responses={
|
||||
status.HTTP_500_INTERNAL_SERVER_ERROR: {'model': ErrorResponse},
|
||||
},
|
||||
status_code=status.HTTP_200_OK,
|
||||
)
|
||||
def delete_sandbox(
|
||||
chat_id: UUID,
|
||||
usecase: DeleteSandbox = Depends(get_delete_sandbox),
|
||||
) -> DeleteSandboxResponse:
|
||||
try:
|
||||
result = usecase.execute(DeleteSandboxCommand(chat_id=chat_id))
|
||||
except SandboxError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
|
||||
return _to_delete_sandbox_response(result)
|
||||
|
||||
|
||||
def _to_sandbox_session_response(session: SandboxSession) -> SandboxSessionResponse:
|
||||
if session.endpoint is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail='sandbox_endpoint_unavailable',
|
||||
)
|
||||
|
||||
return SandboxSessionResponse(
|
||||
session_id=session.session_id,
|
||||
chat_id=session.chat_id,
|
||||
agent_id=session.agent_id,
|
||||
volume_host_path=session.volume_host_path,
|
||||
container_id=session.container_id,
|
||||
endpoint=SandboxEndpointResponse(
|
||||
ip=session.endpoint.ip,
|
||||
port=session.endpoint.port,
|
||||
),
|
||||
status=session.status.value,
|
||||
expires_at=session.expires_at,
|
||||
)
|
||||
|
||||
|
||||
def _to_delete_sandbox_response(result: DeleteSandboxResult) -> DeleteSandboxResponse:
|
||||
return DeleteSandboxResponse(
|
||||
chat_id=result.chat_id,
|
||||
result=result.result,
|
||||
session_id=result.session_id,
|
||||
container_id=result.container_id,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic import BaseModel, ConfigDict, field_validator
|
||||
|
||||
|
||||
class HealthResponse(BaseModel):
|
||||
|
|
@ -14,15 +15,47 @@ class CreateSandboxRequest(BaseModel):
|
|||
model_config = ConfigDict(extra='forbid')
|
||||
|
||||
chat_id: UUID
|
||||
agent_id: str
|
||||
volume_host_path: str
|
||||
|
||||
@field_validator('agent_id')
|
||||
@classmethod
|
||||
def validate_agent_id(cls, value: str) -> str:
|
||||
if not value.strip():
|
||||
raise ValueError('invalid agent_id')
|
||||
return value
|
||||
|
||||
@field_validator('volume_host_path')
|
||||
@classmethod
|
||||
def validate_volume_host_path(cls, value: str) -> str:
|
||||
path = Path(value).expanduser()
|
||||
if not path.is_absolute():
|
||||
raise ValueError('invalid volume_host_path')
|
||||
return str(path.resolve(strict=False))
|
||||
|
||||
|
||||
class SandboxEndpointResponse(BaseModel):
|
||||
ip: str
|
||||
port: int
|
||||
|
||||
|
||||
class SandboxSessionResponse(BaseModel):
|
||||
session_id: UUID
|
||||
chat_id: UUID
|
||||
agent_id: str
|
||||
volume_host_path: str
|
||||
container_id: str
|
||||
endpoint: SandboxEndpointResponse
|
||||
status: str
|
||||
expires_at: datetime
|
||||
|
||||
|
||||
class DeleteSandboxResponse(BaseModel):
|
||||
chat_id: UUID
|
||||
result: str
|
||||
session_id: UUID | None = None
|
||||
container_id: str | None = None
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
detail: str
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue