Files
Artemis-Eapil/apps/admin_console/routers/exploration.py
chenmingxuan e1ea099592 feat: add official Codex skill and harden task execution
Install and update artemis-use with task diagnostics and plugin guidance. Preserve cross-device file receipts, review canonical reports, guard stage transitions and reduce small history summaries. Improve exploration lifecycle, request recovery and browser live connections.
2026-10-18 14:18:37 +08:00

181 lines
5.6 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"])
@router.get("/{exploration_id}/deletion-preview")
def preview_deletion(exploration_id: str):
from apps.admin_console.services.task_deletion import exploration_deletion_preview
with domain_errors():
return exploration_deletion_preview(exploration_id)
@router.delete("/{exploration_id}")
def delete_conversation(exploration_id: str):
from apps.admin_console.services.exploration_deletion import delete_exploration
with domain_errors():
deleted = delete_exploration(service.repository, exploration_id)
return {"status": "success", "deleted_session_ids": deleted}
@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)