feat: support batch repetition and selective case import

This commit is contained in:
chenmingxuan committed 2026-10-09 16:29:16 +08:00
1 parent df66897ff4
commit 4d6560dbd4
16 files changed
+296 -37

No files matched your search

@@ -9,7 +9,7 @@ from uuid import UUID
from apps.admin_console.core.state import state
from apps.admin_console.services.automation_store import RECOVERY_ERRORS, store, new_id
from apps.admin_console.services.task_import import case_goal, case_title
from apps.admin_console.services.task_import import case_goal, execution_title
from artemis.config.settings import settings
from artemis.runtime.task_limits import task_timeout
@@ -297,7 +297,7 @@ async def tick_batches():
runtime_overrides=options,
session_id=job["session_id"],
ingress="batch",
display_name=case_title(job["case"]),
display_name=execution_title(job),
)
if response.get("status") == "rejected" or not response.get("tasks"):
raise ValueError(response.get("error") or "队列拒绝接收任务")
@@ -330,6 +330,15 @@ def has_legacy_replay(batch_id: str) -> bool:
def create_batch(payload: dict) -> dict:
repeat_count = payload.get("repeat_count", 1)
if type(repeat_count) is not int or repeat_count < 1:
raise ValueError("循环次数必须为正整数")
cases = payload["cases"]
total = len(cases) * repeat_count
if not cases or total > settings.ARTEMIS_BATCH_MAX_EXECUTIONS:
raise ValueError(
f"请选择用例;用例数 × 循环次数不能超过 {settings.ARTEMIS_BATCH_MAX_EXECUTIONS} 次执行"
)
bid = new_id()
batch = {
"id": bid,
@@ -342,21 +351,28 @@ def create_batch(payload: dict) -> dict:
"timeout_seconds": payload.get("timeout_seconds") or settings.ARTEMIS_TASK_TIMEOUT_SECONDS,
"stop_on_failure": payload.get("stop_on_failure", False),
"request_id": payload.get("request_id"),
"repeat_count": repeat_count,
"case_count": len(cases),
"total_executions": total,
}
jobs = []
for number, case in enumerate(payload["cases"], 1):
jobs.append(
{
"session_id": new_id(),
"batch_id": bid,
"position": number,
"case": case,
"status": "pending",
"formats": batch["formats"],
"created_at": time.time(),
"device_serial": batch["device_serial"],
}
)
for iteration in range(1, repeat_count + 1):
for number, case in enumerate(cases, 1):
jobs.append(
{
"session_id": new_id(),
"batch_id": bid,
"position": len(jobs) + 1,
"iteration": iteration,
"repeat_count": repeat_count,
"case_position": number,
"case": case,
"status": "pending",
"formats": batch["formats"],
"created_at": batch["created_at"],
"device_serial": batch["device_serial"],
}
)
return store.create_batch(batch, jobs, payload)
@@ -412,7 +428,13 @@ def batch_action(batch_id: str, action: str) -> dict:
if not failed:
raise ValueError("没有可重跑的失败或中断任务")
revised = create_batch(
{**batch, "name": batch["name"] + " · 失败项重跑", "cases": [j["case"] for j in failed]}
{
**batch,
"name": batch["name"] + " · 失败项重跑",
"cases": [j["case"] for j in failed],
"repeat_count": 1,
"request_id": None,
}
)
return revised
else:
+4 -2
View File
@@ -22,7 +22,7 @@ from artemis.config import IMAGES_DIR, TRACES_PATH, WORKSPACE_ROOT
from apps.admin_console.database.repositories.session_repository import SessionRepository
from apps.admin_console.database.repositories.step_repository import StepRepository
from apps.admin_console.services.automation_store import store, identifier
from apps.admin_console.services.task_import import case_title
from apps.admin_console.services.task_import import execution_title
EXPORT_ROOT = WORKSPACE_ROOT / "artifacts" / "reports"
EXPORT_LOCK = threading.RLock()
@@ -190,7 +190,9 @@ def session_report(session_id: str, db_path=None) -> dict:
)
return {
"id": session_id,
"name": case_title(case) or session.get("initial_goal", session_id)[:120],
"name": execution_title(meta) or session.get("initial_goal", session_id)[:120],
"iteration": meta.get("iteration", 1),
"repeat_count": meta.get("repeat_count", 1),
"goal": case.get("goal") or session.get("initial_goal", ""),
"expected": case.get("expected", ""),
"status": status,
@@ -189,3 +189,11 @@ def case_title(case: dict) -> str:
case_id = str(case.get("case_id") or "").strip()
name = str(case.get("name") or "").strip()
return " ".join(part for part in (case_id, name) if part)
def execution_title(job: dict) -> str:
"""Distinguish repeated executions without altering the original case or prompt."""
title = case_title(job.get("case") or {})
if job.get("repeat_count", 1) > 1:
title += f" · 第 {job['iteration']} / {job['repeat_count']} 轮"
return title