diff --git a/apps/admin_console/routers/automation.py b/apps/admin_console/routers/automation.py
index b880be5..7077e58 100644
--- a/apps/admin_console/routers/automation.py
+++ b/apps/admin_console/routers/automation.py
@@ -13,6 +13,7 @@ from apps.admin_console.services import automation_service as service
from apps.admin_console.services import batch_archive as archive_service
from apps.admin_console.services import report_export, task_import
from artemis.config.settings import settings
+from artemis.runtime.case_result import read_case_result
router = APIRouter(prefix="/api/automation", tags=["automation"])
@@ -122,7 +123,12 @@ def batch_by_request(request_id: str):
@router.get("/batches/{batch_id}")
def batch_detail(batch_id: str):
- return {**require("batch", batch_id), "jobs": store.jobs(batch_id)}
+ jobs = store.jobs(batch_id)
+ for job in jobs:
+ job["test_result"] = read_case_result(
+ job["session_id"], job["status"], job.get("error") or ""
+ )
+ return {**require("batch", batch_id), "jobs": jobs}
@router.delete("/batches/{batch_id}")
@@ -260,6 +266,9 @@ def history(
items = [dict(row) for row in rows]
jobs = store.jobs_for_sessions([item["session_id"] for item in items])
for item in items:
+ item["test_result"] = read_case_result(
+ item["session_id"], item["status"], item.get("error") or ""
+ )
if job := jobs.get(item["session_id"]):
item["name"] = task_import.execution_title(job)
item["iteration"] = job.get("iteration", 1)
diff --git a/apps/admin_console/routers/sessions.py b/apps/admin_console/routers/sessions.py
index 60e7b93..13c511c 100644
--- a/apps/admin_console/routers/sessions.py
+++ b/apps/admin_console/routers/sessions.py
@@ -20,6 +20,7 @@ from fastapi import APIRouter, HTTPException
from artemis.config import DB_PATH, TRACES_PATH
from artemis.runtime import trace_store
+from artemis.runtime.case_result import read_case_result
try:
from admin_console.core.state import state
@@ -130,6 +131,9 @@ def _list_sessions_sync():
else:
row_dict["video_url"] = None
row_dict["recording_status"] = recording_status
+ row_dict["test_result"] = read_case_result(
+ s_id, row_dict.get("status") or "", row_dict.get("error") or "", root=TRACES_PATH
+ )
agent_names = agent_names_by_session.get(s_id, [])
sess_profile = model_service.resolve_session_profile(
@@ -189,7 +193,11 @@ async def get_session_details(session_id: str):
row = session_repo.get_session_by_id(session_id)
if not row:
raise HTTPException(status_code=404, detail=f"Session {session_id} not found")
- return dict(row)
+ result = dict(row)
+ result["test_result"] = read_case_result(
+ session_id, result.get("status") or "", root=TRACES_PATH
+ )
+ return result
@router.get("/api/sessions/{session_id}/usage")
diff --git a/apps/admin_console/services/report_export.py b/apps/admin_console/services/report_export.py
index ca243ab..54fa1c8 100644
--- a/apps/admin_console/services/report_export.py
+++ b/apps/admin_console/services/report_export.py
@@ -23,6 +23,7 @@ from apps.admin_console.database.repositories.session_repository import SessionR
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 execution_title
+from artemis.runtime.case_result import RESULT_LABELS, resolve_case_result
EXPORT_ROOT = WORKSPACE_ROOT / "artifacts" / "reports"
EXPORT_LOCK = threading.RLock()
@@ -62,6 +63,9 @@ def text(value) -> str:
def verdict(outcome: dict | None) -> str:
+ result = (outcome or {}).get("test_result")
+ if isinstance(result, dict) and result.get("status") in RESULT_LABELS:
+ return RESULT_LABELS[result["status"]]
tests = (outcome or {}).get("tests") or {}
if tests.get("failed", 0):
return "验证失败"
@@ -179,6 +183,9 @@ def session_report(session_id: str, db_path=None) -> dict:
case = meta.get("case") or {}
status = meta.get("terminal_status") or meta.get("status") or session.get("status", "unknown")
error = failure_reason(directory, status, meta.get("error"))
+ test_result = resolve_case_result(outcome, status, error)
+ if test_result:
+ outcome = {**outcome, "test_result": test_result.model_dump()}
verification = verdict(outcome)
if status in {"failed", "interrupted"} and verification.startswith("未验证"):
verification = "未验证(任务异常终止)"
@@ -197,6 +204,7 @@ def session_report(session_id: str, db_path=None) -> dict:
"expected": case.get("expected", ""),
"status": status,
"verdict": verification,
+ "test_result": test_result.model_dump() if test_result else None,
"outcome": outcome,
"content": content,
"steps": steps,
@@ -233,6 +241,11 @@ def metadata(report: dict) -> list[tuple]:
("预期结果", report["expected"] or "未指定"),
("执行状态", STATUS.get(report["status"], report["status"])),
("验证结论", report["verdict"]),
+ ("判定依据", (report.get("test_result") or {}).get("reason") or "未记录"),
+ (
+ "验收证据",
+ "\n".join((report.get("test_result") or {}).get("evidence") or []) or "未记录",
+ ),
("开始时间", time_text(report["start"])),
("结束时间", time_text(report["end"])),
("设备", report["device"]),
@@ -434,6 +447,11 @@ def word_report(report: dict, path: Path):
if report["error"]:
doc.add_heading("异常原因", 1)
doc.add_paragraph(text(report["error"]))
+ if result := report.get("test_result"):
+ doc.add_heading("测试结论与依据", 1)
+ doc.add_paragraph(text(result.get("reason") or "未记录"))
+ for evidence in result.get("evidence") or []:
+ doc.add_paragraph(text(evidence), style="List Bullet")
doc.add_heading("任务信息", 1)
details = [
(label, value)
@@ -441,10 +459,11 @@ def word_report(report: dict, path: Path):
if label in {"任务编号", "用例编号", "检测项目", "测试子项", "开始时间", "结束时间", "设备"}
]
word_table(doc, details, (3.2, 13.3))
- doc.add_heading("任务目标", 2)
+ doc.add_heading("任务目标与预期" if not report["expected"] else "任务目标", 2)
doc.add_paragraph(report["goal"] or "未记录")
- doc.add_heading("预期结果", 2)
- doc.add_paragraph(report["expected"] or "未指定")
+ if report["expected"]:
+ doc.add_heading("预期结果", 2)
+ doc.add_paragraph(report["expected"])
if report.get("preconditions"):
doc.add_paragraph("前置约束:" + text(report["preconditions"]))
if report.get("task_config"):
diff --git a/apps/showcase_ui/package-lock.json b/apps/showcase_ui/package-lock.json
index 8a6feeb..0eeba3d 100644
--- a/apps/showcase_ui/package-lock.json
+++ b/apps/showcase_ui/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "artemis-showcase-ui",
- "version": "1.4.0",
+ "version": "1.5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "artemis-showcase-ui",
- "version": "1.4.0",
+ "version": "1.5.0",
"dependencies": {
"@angular/cdk": "^22.1.5",
"@angular/common": "^22.1.4",
diff --git a/apps/showcase_ui/package.json b/apps/showcase_ui/package.json
index a2eae17..a21ddd1 100644
--- a/apps/showcase_ui/package.json
+++ b/apps/showcase_ui/package.json
@@ -1,6 +1,6 @@
{
"name": "artemis-showcase-ui",
- "version": "1.4.0",
+ "version": "1.5.0",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
diff --git a/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html b/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html
index 4676d4d..535beb5 100644
--- a/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html
+++ b/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html
@@ -86,6 +86,7 @@ limitations under the License.
}
{{ getTaskStatus(session) | uiLabel }}
+
{{r.error}}
报告生成失败:{{b.report_error}}
} - @for(j of b.jobs; track j.session_id){{{error()}}
} @if(loading()){正在查询…} @for(group of groups(); track group.key){没有符合条件的历史任务。
}{{result().reason}}
+ @if(result().evidence.length){