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:
|
||||
execution_log = run.execution_log or []
|
||||
last_log = next(
|
||||
(log for log in reversed(execution_log) if isinstance(log, dict)),
|
||||
{},
|
||||
)
|
||||
return {
|
||||
"id": run.id,
|
||||
"workflow_id": run.workflow_id,
|
||||
"workflow_name": workflow_name,
|
||||
"status": run.status or "pending",
|
||||
"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_tokens": run.total_tokens or 0,
|
||||
"elapsed_time": run.elapsed_time or 0,
|
||||
|
||||
@@ -124,6 +124,12 @@ class WorkflowRunListResponse(BaseModel):
|
||||
workflow_name: str = ""
|
||||
status: str = "pending"
|
||||
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_tokens: int = 0
|
||||
elapsed_time: int = 0
|
||||
|
||||
@@ -289,6 +289,12 @@ export interface WorkflowRunListItem {
|
||||
workflow_name: string;
|
||||
status: 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_tokens: number;
|
||||
elapsed_time: number;
|
||||
|
||||
@@ -107,6 +107,13 @@ export function useZqTableColumns(): Column[] {
|
||||
title: $t('ai-platform.workflowRuns.columns.workflow'),
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
key: 'task_summary',
|
||||
title: '任务',
|
||||
minWidth: 240,
|
||||
showOverflowTooltip: true,
|
||||
slots: { default: 'cell-task_summary' },
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
title: $t('ai-platform.workflowRuns.columns.status'),
|
||||
@@ -135,6 +142,20 @@ export function useZqTableColumns(): Column[] {
|
||||
width: 90,
|
||||
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',
|
||||
title: $t('ai-platform.workflowRuns.columns.duration'),
|
||||
|
||||
@@ -82,6 +82,40 @@ const [Grid] = useZqTable({
|
||||
function openDetail(row: WorkflowRunListItem) {
|
||||
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>
|
||||
|
||||
<template>
|
||||
@@ -102,6 +136,28 @@ function openDetail(row: WorkflowRunListItem) {
|
||||
</ElTag>
|
||||
</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 }">
|
||||
{{ formatDuration(row.elapsed_time) }}
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user