164 lines
5.0 KiB
Python
164 lines
5.0 KiB
Python
"""Shared exploration operations; no separate Web execution path."""
|
|
|
|
from contextlib import contextmanager
|
|
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
|
|
from artemis.config.exploration import ExplorationConfig
|
|
from artemis.exploration.models import (
|
|
CreateExploration,
|
|
ExplorationMessage,
|
|
ExplorationRequest,
|
|
SaveCaseRequest,
|
|
ValidateCaseRequest,
|
|
DemonstrationRequest,
|
|
ExtendDemonstration,
|
|
GenerateCaseRequest,
|
|
CaseBatchRequest,
|
|
)
|
|
from artemis.exploration.store import ExplorationConflict
|
|
from apps.admin_console.services.exploration_service import exploration_service as service
|
|
|
|
|
|
router = APIRouter(prefix="/api/explorations", tags=["exploration"])
|
|
|
|
|
|
@contextmanager
|
|
def domain_errors():
|
|
try:
|
|
yield
|
|
except KeyError as exc:
|
|
raise HTTPException(404, str(exc).strip("'")) from exc
|
|
except ExplorationConflict as exc:
|
|
raise HTTPException(409, str(exc)) from exc
|
|
except ValueError as exc:
|
|
# Pydantic may echo sensitive input. Invalid nested fields get a plain error.
|
|
from pydantic import ValidationError
|
|
|
|
raise HTTPException(
|
|
400, "探索参数不合法" if isinstance(exc, ValidationError) else str(exc)
|
|
) from exc
|
|
|
|
|
|
@router.get("/settings")
|
|
def settings():
|
|
return service.settings()
|
|
|
|
|
|
@router.put("/settings")
|
|
def save_settings(value: ExplorationConfig):
|
|
return service.save_settings(value)
|
|
|
|
|
|
@router.get("")
|
|
def list_explorations(limit: int = Query(100, ge=1, le=500)):
|
|
return service.repository.list(limit)
|
|
|
|
|
|
@router.post("")
|
|
def create(value: CreateExploration, source: str = Query("web", pattern="^(web|mcp)$")):
|
|
with domain_errors():
|
|
return service.create(value, source=source)
|
|
|
|
|
|
@router.get("/{session_id}")
|
|
def detail(session_id: str, after: int = Query(0, ge=0)):
|
|
with domain_errors():
|
|
return service.detail(session_id, after=after)
|
|
|
|
|
|
@router.post("/{session_id}/start")
|
|
async def start(
|
|
session_id: str, value: ExplorationRequest, source: str = Query("web", pattern="^(web|mcp)$")
|
|
):
|
|
with domain_errors():
|
|
return await service.start(session_id, value.request_id, source=source)
|
|
|
|
|
|
@router.post("/{session_id}/messages")
|
|
async def message(
|
|
session_id: str, value: ExplorationMessage, source: str = Query("web", pattern="^(web|mcp)$")
|
|
):
|
|
with domain_errors():
|
|
result = service.message(session_id, value.text, value.request_id, source=source)
|
|
if not result.get("run_id"):
|
|
from apps.admin_console.services.automation_service import spawn
|
|
from artemis.exploration.editorial import discuss
|
|
|
|
spawn(discuss(service.repository, session_id, result))
|
|
return result
|
|
|
|
|
|
@router.post("/{session_id}/pause")
|
|
def pause(session_id: str):
|
|
with domain_errors():
|
|
return service.pause(session_id)
|
|
|
|
|
|
@router.post("/{session_id}/stop")
|
|
async def stop(session_id: str):
|
|
with domain_errors():
|
|
return service.stop(session_id)
|
|
|
|
|
|
@router.post("/{session_id}/continue")
|
|
async def resume(
|
|
session_id: str, value: ExplorationRequest, source: str = Query("web", pattern="^(web|mcp)$")
|
|
):
|
|
with domain_errors():
|
|
return await service.resume(session_id, value.request_id, source=source)
|
|
|
|
|
|
@router.get("/{session_id}/case")
|
|
def case(session_id: str, version: int | None = Query(None, ge=1)):
|
|
with domain_errors():
|
|
return service.repository.case(session_id, version)
|
|
|
|
|
|
@router.post("/{session_id}/case")
|
|
def save_case(session_id: str, value: SaveCaseRequest):
|
|
with domain_errors():
|
|
return service.save_case(session_id, value)
|
|
|
|
|
|
@router.post("/{session_id}/validate")
|
|
async def validate(
|
|
session_id: str, value: ValidateCaseRequest, source: str = Query("web", pattern="^(web|mcp)$")
|
|
):
|
|
with domain_errors():
|
|
return await service.validate(session_id, value, source=source)
|
|
|
|
|
|
@router.post("/{session_id}/demonstrations")
|
|
def demonstrate(session_id: str, value: DemonstrationRequest):
|
|
with domain_errors():
|
|
return service.demonstrate(session_id, value.request_id, value.source)
|
|
|
|
|
|
@router.post("/{session_id}/demonstrations/{demo_id}/extend")
|
|
def extend_demonstration(session_id: str, demo_id: str, value: ExtendDemonstration):
|
|
with domain_errors():
|
|
return service.demonstration_command(
|
|
session_id, demo_id, "extend", value.request_id, seconds=value.seconds
|
|
)
|
|
|
|
|
|
@router.post("/{session_id}/demonstrations/{demo_id}/{command}")
|
|
def demonstration_command(session_id: str, demo_id: str, command: str, value: ExplorationRequest):
|
|
if command not in {"finish", "cancel"}:
|
|
raise HTTPException(404, "示范操作不存在")
|
|
with domain_errors():
|
|
return service.demonstration_command(session_id, demo_id, command, value.request_id)
|
|
|
|
|
|
@router.post("/{session_id}/generate")
|
|
async def generate_case(session_id: str, value: GenerateCaseRequest):
|
|
with domain_errors():
|
|
return await service.generate_case(session_id, value)
|
|
|
|
|
|
@router.post("/{session_id}/batch")
|
|
def case_batch(session_id: str, value: CaseBatchRequest):
|
|
with domain_errors():
|
|
return service.create_case_batch(session_id, value)
|