feat: add prompt-first batches and pausable task guidance
This commit is contained in:
1 parent
cec9898e4c
commit
057f0c3050
44 files changed
+1080
-191
No files matched your search
@@ -146,8 +146,13 @@ def report(session_id: str):
|
||||
|
||||
|
||||
@router.get("/reports/{session_id}/{fmt}")
|
||||
async def download_report(session_id: str, fmt: Literal["docx", "xlsx"]):
|
||||
async def download_report(
|
||||
session_id: str, fmt: Literal["docx", "xlsx", "screenshots", "video", "all"]
|
||||
):
|
||||
try:
|
||||
if fmt in {"screenshots", "video", "all"}:
|
||||
path = await asyncio.to_thread(report_export.export_session_media, session_id, fmt)
|
||||
return FileResponse(path, filename=f"任务{session_id[:8]}-{fmt}.zip")
|
||||
paths = await asyncio.to_thread(report_export.export_session, session_id, [fmt])
|
||||
return FileResponse(paths[0], filename=f"任务报告-{session_id[:8]}.{fmt}")
|
||||
except ValueError as exc:
|
||||
@@ -167,21 +172,29 @@ def history(
|
||||
query = """WITH history AS (
|
||||
SELECT s.session_id, s.initial_goal, s.start_time, s.end_time,
|
||||
COALESCE(j.status,s.status) status, COALESCE(json_extract(j.payload,'$.case.name'),s.initial_goal) name,
|
||||
COALESCE(json_extract(j.payload,'$.case.tags'),'') tags
|
||||
COALESCE(json_extract(j.payload,'$.case.tags'),'') tags,
|
||||
COALESCE(json_extract(j.payload,'$.case.case_id'),'') case_id,
|
||||
COALESCE(json_extract(j.payload,'$.case.category'),'') category,
|
||||
COALESCE(json_extract(j.payload,'$.case.subcategory'),'') subcategory,
|
||||
COALESCE(json_extract(j.payload,'$.case.preconditions'),'') preconditions
|
||||
FROM sessions s LEFT JOIN automation_jobs j ON s.session_id=j.session_id
|
||||
UNION ALL
|
||||
SELECT j.session_id,json_extract(j.payload,'$.case.goal'),
|
||||
COALESCE(json_extract(j.payload,'$.started_at'),json_extract(j.payload,'$.created_at')),
|
||||
json_extract(j.payload,'$.ended_at'),j.status,json_extract(j.payload,'$.case.name'),
|
||||
COALESCE(json_extract(j.payload,'$.case.tags'),'')
|
||||
COALESCE(json_extract(j.payload,'$.case.tags'),''),
|
||||
COALESCE(json_extract(j.payload,'$.case.case_id'),''),
|
||||
COALESCE(json_extract(j.payload,'$.case.category'),''),
|
||||
COALESCE(json_extract(j.payload,'$.case.subcategory'),''),
|
||||
COALESCE(json_extract(j.payload,'$.case.preconditions'),'')
|
||||
FROM automation_jobs j WHERE NOT EXISTS (SELECT 1 FROM sessions s WHERE s.session_id=j.session_id)
|
||||
) """
|
||||
where = " WHERE status IN ('completed','success','failed','cancelled','interrupted','skipped')"
|
||||
args = []
|
||||
if q:
|
||||
where += " AND (name LIKE ? ESCAPE '\\' OR initial_goal LIKE ? ESCAPE '\\' OR session_id LIKE ? ESCAPE '\\' OR tags LIKE ? ESCAPE '\\')"
|
||||
where += " AND (name LIKE ? ESCAPE '\\' OR initial_goal LIKE ? ESCAPE '\\' OR session_id LIKE ? ESCAPE '\\' OR tags LIKE ? ESCAPE '\\' OR case_id LIKE ? ESCAPE '\\' OR category LIKE ? ESCAPE '\\' OR subcategory LIKE ? ESCAPE '\\' OR preconditions LIKE ? ESCAPE '\\')"
|
||||
escaped = "%" + q.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_") + "%"
|
||||
args.extend([escaped] * 4)
|
||||
args.extend([escaped] * 8)
|
||||
for condition, value in [
|
||||
("status = ?", status or None),
|
||||
("start_time >= ?", start),
|
||||
|
||||
@@ -18,8 +18,16 @@ import json
|
||||
from typing import Any
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, Field
|
||||
from artemis.core.diagnostics import readiness_engine
|
||||
from artemis.runtime import DeviceExecutionLock, device_pool
|
||||
from artemis.runtime.task_control import (
|
||||
add_instruction,
|
||||
control_dir,
|
||||
read_state,
|
||||
request_pause,
|
||||
request_resume,
|
||||
)
|
||||
|
||||
try:
|
||||
from admin_console.core.state import state
|
||||
@@ -42,6 +50,48 @@ except ImportError:
|
||||
router = APIRouter(tags=["tasks"])
|
||||
|
||||
|
||||
class GuidanceRequest(BaseModel):
|
||||
instruction: str = Field(min_length=1, max_length=10000)
|
||||
|
||||
|
||||
def _live_task(session_id: str) -> dict:
|
||||
from apps.admin_console.services.automation_store import identifier
|
||||
|
||||
identifier(session_id)
|
||||
run = state.active_runs.get(session_id)
|
||||
if not run or run["process"].returncode is not None:
|
||||
raise HTTPException(409, "任务未在执行,无法暂停或指导")
|
||||
return run
|
||||
|
||||
|
||||
@router.post("/api/tasks/{session_id}/pause")
|
||||
def pause_running_task(session_id: str):
|
||||
_live_task(session_id)
|
||||
request_pause(session_id)
|
||||
return {"session_id": session_id, "status": "pausing"}
|
||||
|
||||
|
||||
@router.post("/api/tasks/{session_id}/guide")
|
||||
def guide_running_task(session_id: str, value: GuidanceRequest):
|
||||
_live_task(session_id)
|
||||
add_instruction(session_id, value.instruction, source="web")
|
||||
if (
|
||||
read_state(session_id).get("status") == "task_paused"
|
||||
or (control_dir(session_id) / "pause.request").exists()
|
||||
):
|
||||
request_resume(session_id)
|
||||
return {"session_id": session_id, "status": "guidance_submitted"}
|
||||
|
||||
|
||||
@router.post("/api/tasks/{session_id}/continue")
|
||||
def continue_running_task(session_id: str):
|
||||
_live_task(session_id)
|
||||
if read_state(session_id).get("status") != "task_paused":
|
||||
raise HTTPException(409, "任务尚未暂停")
|
||||
request_resume(session_id)
|
||||
return {"session_id": session_id, "status": "resuming"}
|
||||
|
||||
|
||||
@router.get("/api/tasks/presets")
|
||||
async def get_task_presets(
|
||||
category: str = "recommended",
|
||||
@@ -285,6 +335,17 @@ async def get_status():
|
||||
}
|
||||
for owner in active_owners.values()
|
||||
]
|
||||
for sid, run in state.active_runs.items():
|
||||
if run.get("parked"):
|
||||
active_tasks.append(
|
||||
{
|
||||
"session_id": sid,
|
||||
"device_id": run.get("device_id"),
|
||||
"goal": run.get("goal"),
|
||||
"pid": getattr(run.get("process"), "pid", None),
|
||||
"status": read_state(sid).get("status") or "task_paused",
|
||||
}
|
||||
)
|
||||
|
||||
running_sid = (
|
||||
(global_owner.session_id if global_owner else None)
|
||||
|
||||
Reference in new issue
Block a user