feat: release 2.4.0 with original cases and automatic human review
This commit is contained in:
1 parent
baf069854b
commit
4dc2057d43
66 files changed
+1390
-206
No files matched your search
@@ -21,6 +21,7 @@ HEADERS = (
|
||||
"Artemis提示词",
|
||||
"任务配置(JSON)",
|
||||
)
|
||||
CASE_HEADERS = ("预置条件", "操作步骤", "预期结果")
|
||||
MAX_ROWS = int(os.getenv("ARTEMIS_IMPORT_MAX_ROWS", "1000"))
|
||||
MAX_BYTES = int(os.getenv("ARTEMIS_IMPORT_MAX_BYTES", "10485760"))
|
||||
|
||||
@@ -61,6 +62,15 @@ class TaskOptions(BaseModel):
|
||||
return value.model_dump(exclude_unset=True)
|
||||
|
||||
|
||||
class OriginalCase(BaseModel):
|
||||
"""Imported review material, never appended to the execution prompt."""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
preconditions: str = Field(default="", max_length=10000)
|
||||
steps: str = Field(default="", max_length=10000)
|
||||
expected: str = Field(default="", max_length=10000)
|
||||
|
||||
|
||||
class TaskCase(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
case_id: str = Field(default="", max_length=200)
|
||||
@@ -70,16 +80,29 @@ class TaskCase(BaseModel):
|
||||
preconditions: str = Field(default="", max_length=10000)
|
||||
goal: str = Field(min_length=1, max_length=10000)
|
||||
task_config: TaskOptions = Field(default_factory=TaskOptions)
|
||||
original_case: OriginalCase | None = None
|
||||
|
||||
|
||||
def original_case_enabled() -> bool:
|
||||
from apps.admin_console.services.task_extensions import active_manifests
|
||||
|
||||
return any(
|
||||
panel.kind == "original_case"
|
||||
for manifest in active_manifests()
|
||||
if manifest.workspace is not None
|
||||
for panel in manifest.workspace.panels
|
||||
)
|
||||
|
||||
|
||||
def template() -> bytes:
|
||||
headers = (*HEADERS, *CASE_HEADERS) if original_case_enabled() else HEADERS
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
assert ws is not None
|
||||
ws.title = "任务"
|
||||
ws.append(HEADERS)
|
||||
ws.append(headers)
|
||||
ws.freeze_panes = "A2"
|
||||
ws.auto_filter.ref = "A1:G1"
|
||||
ws.auto_filter.ref = f"A1:{get_column_letter(len(headers))}1"
|
||||
for cell in ws[1]:
|
||||
cell.font = Font(bold=True, color="FFFFFF")
|
||||
cell.fill = PatternFill("solid", fgColor="245C78")
|
||||
@@ -93,7 +116,7 @@ def template() -> bytes:
|
||||
help_sheet.append(["前置约束提取及其他描述列仅作查看、搜索和报告元数据,不追加到提示词。"])
|
||||
help_sheet.append(["任务配置(JSON)可留空,空字段继承批次或全局配置;禁止密钥与未知字段。"])
|
||||
help_sheet.append(["示例仅供参考,不会导入执行。请在“任务”页填写自己的用例。"])
|
||||
help_sheet.append(HEADERS)
|
||||
help_sheet.append(headers)
|
||||
help_sheet.append(
|
||||
[
|
||||
"示例-001",
|
||||
@@ -130,7 +153,18 @@ def template() -> bytes:
|
||||
json.dumps({"profile": "flash", "execution_target": "computer"}, ensure_ascii=False),
|
||||
]
|
||||
)
|
||||
for column in range(1, 8):
|
||||
if len(headers) > len(HEADERS):
|
||||
help_sheet.append(
|
||||
[
|
||||
"人工评审插件附加列:预置条件、操作步骤、预期结果。仅保存原始用例,均不追加到 AI 提示词。"
|
||||
]
|
||||
)
|
||||
for row_number in (6, 7):
|
||||
for column, value in enumerate(
|
||||
("填写原始用例的预置条件", "填写原始用例的操作步骤", "填写原始用例的预期结果"), 8
|
||||
):
|
||||
help_sheet.cell(row_number, column, value)
|
||||
for column in range(1, len(headers) + 1):
|
||||
help_sheet.column_dimensions[get_column_letter(column)].width = 70 if column > 5 else 26
|
||||
for row in help_sheet:
|
||||
for cell in row:
|
||||
@@ -164,7 +198,10 @@ def parse_workbook(data: bytes) -> dict:
|
||||
raise ValueError("缺少“任务”工作表,请使用下载的模板")
|
||||
rows = iter(wb["任务"].iter_rows())
|
||||
headers = [str(cell.value or "").strip() for cell in next(rows, [])]
|
||||
if headers != list(HEADERS):
|
||||
supported_headers = [list(HEADERS)]
|
||||
if original_case_enabled():
|
||||
supported_headers.append([*HEADERS, *CASE_HEADERS])
|
||||
if headers not in supported_headers:
|
||||
raise ValueError("列名或顺序与新版模板不一致,请重新下载模板")
|
||||
tasks, errors = [], []
|
||||
for number, row in enumerate(rows, 2):
|
||||
@@ -190,9 +227,29 @@ def parse_workbook(data: bytes) -> dict:
|
||||
"preconditions": values[4],
|
||||
"goal": values[5],
|
||||
"task_config": config,
|
||||
"original_case": (
|
||||
dict(
|
||||
zip(
|
||||
("preconditions", "steps", "expected"),
|
||||
(
|
||||
str(cell.value if cell.value is not None else "").strip()
|
||||
for cell in row[7:10]
|
||||
),
|
||||
)
|
||||
)
|
||||
if len(headers) == 10
|
||||
else None
|
||||
),
|
||||
}
|
||||
)
|
||||
tasks.append(
|
||||
{
|
||||
"row": number,
|
||||
**case.model_dump(
|
||||
exclude={"original_case"} if case.original_case is None else None
|
||||
),
|
||||
}
|
||||
)
|
||||
tasks.append({"row": number, **case.model_dump()})
|
||||
except (ValueError, TypeError) as exc:
|
||||
errors.append({"row": number, "message": str(exc)[:500]})
|
||||
if not tasks and not errors:
|
||||
|
||||
Reference in new issue
Block a user