feat: clarify workflow run history
This commit is contained in:
@@ -570,12 +570,23 @@ async def list_workflow_runs(
|
|||||||
|
|
||||||
|
|
||||||
def _build_run_list_item(run: AIWorkflowRun, workflow_name: str = "") -> dict:
|
def _build_run_list_item(run: AIWorkflowRun, workflow_name: str = "") -> dict:
|
||||||
|
execution_log = run.execution_log or []
|
||||||
|
last_log = next(
|
||||||
|
(log for log in reversed(execution_log) if isinstance(log, dict)),
|
||||||
|
{},
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"id": run.id,
|
"id": run.id,
|
||||||
"workflow_id": run.workflow_id,
|
"workflow_id": run.workflow_id,
|
||||||
"workflow_name": workflow_name,
|
"workflow_name": workflow_name,
|
||||||
"status": run.status or "pending",
|
"status": run.status or "pending",
|
||||||
"trigger_type": run.trigger_type or "api",
|
"trigger_type": run.trigger_type or "api",
|
||||||
|
"inputs": run.inputs or {},
|
||||||
|
"outputs": run.outputs or {},
|
||||||
|
"current_node_id": run.current_node_id or "",
|
||||||
|
"last_node_id": last_log.get("node_id", ""),
|
||||||
|
"last_node_label": last_log.get("node_label", ""),
|
||||||
|
"last_node_type": last_log.get("node_type", ""),
|
||||||
"total_steps": run.total_steps or 0,
|
"total_steps": run.total_steps or 0,
|
||||||
"total_tokens": run.total_tokens or 0,
|
"total_tokens": run.total_tokens or 0,
|
||||||
"elapsed_time": run.elapsed_time or 0,
|
"elapsed_time": run.elapsed_time or 0,
|
||||||
|
|||||||
@@ -124,6 +124,12 @@ class WorkflowRunListResponse(BaseModel):
|
|||||||
workflow_name: str = ""
|
workflow_name: str = ""
|
||||||
status: str = "pending"
|
status: str = "pending"
|
||||||
trigger_type: str = "api"
|
trigger_type: str = "api"
|
||||||
|
inputs: dict = Field(default_factory=dict)
|
||||||
|
outputs: dict = Field(default_factory=dict)
|
||||||
|
current_node_id: str = ""
|
||||||
|
last_node_id: str = ""
|
||||||
|
last_node_label: str = ""
|
||||||
|
last_node_type: str = ""
|
||||||
total_steps: int = 0
|
total_steps: int = 0
|
||||||
total_tokens: int = 0
|
total_tokens: int = 0
|
||||||
elapsed_time: int = 0
|
elapsed_time: int = 0
|
||||||
|
|||||||
@@ -289,6 +289,12 @@ export interface WorkflowRunListItem {
|
|||||||
workflow_name: string;
|
workflow_name: string;
|
||||||
status: string;
|
status: string;
|
||||||
trigger_type: string;
|
trigger_type: string;
|
||||||
|
inputs: Record<string, any>;
|
||||||
|
outputs: Record<string, any>;
|
||||||
|
current_node_id: string;
|
||||||
|
last_node_id: string;
|
||||||
|
last_node_label: string;
|
||||||
|
last_node_type: string;
|
||||||
total_steps: number;
|
total_steps: number;
|
||||||
total_tokens: number;
|
total_tokens: number;
|
||||||
elapsed_time: number;
|
elapsed_time: number;
|
||||||
|
|||||||
@@ -107,6 +107,13 @@ export function useZqTableColumns(): Column[] {
|
|||||||
title: $t('ai-platform.workflowRuns.columns.workflow'),
|
title: $t('ai-platform.workflowRuns.columns.workflow'),
|
||||||
minWidth: 160,
|
minWidth: 160,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'task_summary',
|
||||||
|
title: '任务',
|
||||||
|
minWidth: 240,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
slots: { default: 'cell-task_summary' },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'status',
|
key: 'status',
|
||||||
title: $t('ai-platform.workflowRuns.columns.status'),
|
title: $t('ai-platform.workflowRuns.columns.status'),
|
||||||
@@ -135,6 +142,20 @@ export function useZqTableColumns(): Column[] {
|
|||||||
width: 90,
|
width: 90,
|
||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'last_node',
|
||||||
|
title: '最后节点',
|
||||||
|
minWidth: 150,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
slots: { default: 'cell-last_node' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'result_summary',
|
||||||
|
title: '结果摘要',
|
||||||
|
minWidth: 260,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
slots: { default: 'cell-result_summary' },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'elapsed_time',
|
key: 'elapsed_time',
|
||||||
title: $t('ai-platform.workflowRuns.columns.duration'),
|
title: $t('ai-platform.workflowRuns.columns.duration'),
|
||||||
|
|||||||
@@ -82,6 +82,40 @@ const [Grid] = useZqTable({
|
|||||||
function openDetail(row: WorkflowRunListItem) {
|
function openDetail(row: WorkflowRunListItem) {
|
||||||
detailRef.value?.open(row.id);
|
detailRef.value?.open(row.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function previewText(value: any, maxLength = 120) {
|
||||||
|
if (value === undefined || value === null || value === '') return '-';
|
||||||
|
const text =
|
||||||
|
typeof value === 'string'
|
||||||
|
? value
|
||||||
|
: JSON.stringify(value, null, 0);
|
||||||
|
return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTaskSummary(row: WorkflowRunListItem) {
|
||||||
|
const issueId = row.inputs?.issue_id;
|
||||||
|
const request =
|
||||||
|
row.inputs?.request ||
|
||||||
|
row.inputs?.user_input ||
|
||||||
|
row.inputs?.query ||
|
||||||
|
row.inputs?.task;
|
||||||
|
if (issueId && request) return `${issueId}:${request}`;
|
||||||
|
return previewText(request || row.inputs, 160);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLastNode(row: WorkflowRunListItem) {
|
||||||
|
return row.last_node_label || row.last_node_id || row.current_node_id || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResultSummary(row: WorkflowRunListItem) {
|
||||||
|
const output = row.outputs?.output || row.outputs?.final_result;
|
||||||
|
if (output) return previewText(output, 180);
|
||||||
|
|
||||||
|
const nodeValues = Object.entries(row.outputs || {})
|
||||||
|
.filter(([key]) => key.startsWith('_node_'))
|
||||||
|
.map(([, value]) => (value as any)?.output || value);
|
||||||
|
return previewText(nodeValues.at(-1) || row.error_message || row.outputs, 180);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -102,6 +136,28 @@ function openDetail(row: WorkflowRunListItem) {
|
|||||||
</ElTag>
|
</ElTag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #cell-task_summary="{ row }">
|
||||||
|
<span class="text-sm">{{ getTaskSummary(row) }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #cell-last_node="{ row }">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<ElTag size="small" type="info">
|
||||||
|
{{ row.last_node_type || '-' }}
|
||||||
|
</ElTag>
|
||||||
|
<span>{{ getLastNode(row) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #cell-result_summary="{ row }">
|
||||||
|
<span
|
||||||
|
:class="row.status === 'failed' ? 'text-destructive' : 'text-muted-foreground'"
|
||||||
|
class="text-sm"
|
||||||
|
>
|
||||||
|
{{ getResultSummary(row) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #cell-elapsed_time="{ row }">
|
<template #cell-elapsed_time="{ row }">
|
||||||
{{ formatDuration(row.elapsed_time) }}
|
{{ formatDuration(row.elapsed_time) }}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user