feat: improve agent workflow observability
This commit is contained in:
@@ -150,6 +150,14 @@ const getStepLabel = (step: ReasoningStep) => {
|
||||
tool_result: '工具结果',
|
||||
knowledge_retrieval: '知识库检索',
|
||||
annotation_reply: '标注回复',
|
||||
parallel_start: '并行中',
|
||||
parallel_complete: '并行完成',
|
||||
waiting_input: '等待输入',
|
||||
error: '失败',
|
||||
loop_iteration_start: '循环中',
|
||||
loop_iteration_complete: '循环完成',
|
||||
loop_iteration_error: '循环失败',
|
||||
loop_complete: '循环完成',
|
||||
};
|
||||
return labels[step.type] || step.type;
|
||||
};
|
||||
@@ -172,6 +180,14 @@ const getStepColor = (step: ReasoningStep) => {
|
||||
tool_result: 'text-teal-500',
|
||||
knowledge_retrieval: 'text-cyan-500',
|
||||
annotation_reply: 'text-emerald-500',
|
||||
parallel_start: 'text-indigo-500',
|
||||
parallel_complete: 'text-emerald-500',
|
||||
waiting_input: 'text-amber-500',
|
||||
error: 'text-red-500',
|
||||
loop_iteration_start: 'text-blue-500',
|
||||
loop_iteration_complete: 'text-emerald-500',
|
||||
loop_iteration_error: 'text-red-500',
|
||||
loop_complete: 'text-emerald-500',
|
||||
};
|
||||
return colors[step.type] || 'text-gray-500';
|
||||
};
|
||||
@@ -301,7 +317,9 @@ const formatVoiceDuration = (seconds: number) => {
|
||||
<span class="step-indicator" :class="getStepColor(step)">
|
||||
{{ getStepLabel(step) }}
|
||||
</span>
|
||||
<span class="step-text">{{ step.content }}</span>
|
||||
<span class="step-text" :title="step.content">
|
||||
{{ step.content }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,13 +41,21 @@ export interface ReasoningStep {
|
||||
type:
|
||||
| 'action'
|
||||
| 'annotation_reply'
|
||||
| 'error'
|
||||
| 'knowledge_retrieval'
|
||||
| 'loop_complete'
|
||||
| 'loop_iteration_complete'
|
||||
| 'loop_iteration_error'
|
||||
| 'loop_iteration_start'
|
||||
| 'node_complete'
|
||||
| 'node_start'
|
||||
| 'observation'
|
||||
| 'parallel_complete'
|
||||
| 'parallel_start'
|
||||
| 'thought'
|
||||
| 'tool_call'
|
||||
| 'tool_result';
|
||||
| 'tool_result'
|
||||
| 'waiting_input';
|
||||
content: string;
|
||||
tool?: string;
|
||||
params?: Record<string, any>;
|
||||
|
||||
@@ -164,6 +164,12 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
return fallbackLabel || nodeType || '';
|
||||
};
|
||||
|
||||
const getItemCount = (value: any): number => {
|
||||
if (Array.isArray(value)) return value.length;
|
||||
if (value && typeof value === 'object') return Object.keys(value).length;
|
||||
return 0;
|
||||
};
|
||||
|
||||
/** 处理流式事件 */
|
||||
const handleStreamEvent = (event: StreamEvent, msgId: string) => {
|
||||
switch (event.type) {
|
||||
@@ -272,9 +278,18 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
}
|
||||
|
||||
case 'error': {
|
||||
currentSteps.value.push({
|
||||
type: 'error',
|
||||
content: event.message || event.content || '执行失败',
|
||||
node_id: event.node_id,
|
||||
node_type: event.node_type,
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
updateAssistantMessage(msgId, {
|
||||
status: 'failed',
|
||||
error_message: event.message || event.content || '执行失败',
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
});
|
||||
running.value = false;
|
||||
break;
|
||||
@@ -322,6 +337,19 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
outputs[outputVar] = loopEvent.loop_results;
|
||||
}
|
||||
|
||||
currentSteps.value.push({
|
||||
type: 'loop_complete',
|
||||
content: '循环执行完成',
|
||||
node_id: loopEvent.node_id,
|
||||
node_type: 'loop',
|
||||
output: outputs,
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
updateAssistantMessage(msgId, {
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
});
|
||||
|
||||
if (props.enableNodeEvents) {
|
||||
emit('node-complete', {
|
||||
node_id: loopEvent.node_id,
|
||||
@@ -336,6 +364,19 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
}
|
||||
|
||||
case 'loop_iteration_complete': {
|
||||
currentSteps.value.push({
|
||||
type: 'loop_iteration_complete',
|
||||
content: `循环第 ${event.iteration || '-'} 次完成`,
|
||||
node_id: event.node_id,
|
||||
node_type: 'loop',
|
||||
output: event.output,
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
updateAssistantMessage(msgId, {
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
});
|
||||
|
||||
if (props.enableNodeEvents) {
|
||||
emit('loop-iteration', {
|
||||
node_id: event.node_id,
|
||||
@@ -349,6 +390,18 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
}
|
||||
|
||||
case 'loop_iteration_error': {
|
||||
currentSteps.value.push({
|
||||
type: 'loop_iteration_error',
|
||||
content: `循环第 ${event.iteration || '-'} 次失败:${event.error || '执行失败'}`,
|
||||
node_id: event.node_id,
|
||||
node_type: 'loop',
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
updateAssistantMessage(msgId, {
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
});
|
||||
|
||||
if (props.enableNodeEvents) {
|
||||
emit('loop-iteration', {
|
||||
node_id: event.node_id,
|
||||
@@ -362,6 +415,19 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
}
|
||||
|
||||
case 'loop_iteration_start': {
|
||||
currentSteps.value.push({
|
||||
type: 'loop_iteration_start',
|
||||
content: `循环第 ${event.iteration || '-'} 次开始`,
|
||||
node_id: event.node_id,
|
||||
node_type: 'loop',
|
||||
params: { item: event.item, total: event.total },
|
||||
status: 'running',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
updateAssistantMessage(msgId, {
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
});
|
||||
|
||||
if (props.enableNodeEvents) {
|
||||
emit('loop-iteration', {
|
||||
node_id: event.node_id,
|
||||
@@ -423,6 +489,60 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'parallel_complete': {
|
||||
const existingIndex = currentSteps.value.findIndex(
|
||||
(s) => s.node_id === event.node_id && s.type === 'parallel_start',
|
||||
);
|
||||
const branchResults = event.branch_results || {};
|
||||
const content = `并行分支完成:${getItemCount(branchResults)} 个分支`;
|
||||
if (existingIndex === -1) {
|
||||
currentSteps.value.push({
|
||||
type: 'parallel_complete',
|
||||
content,
|
||||
node_id: event.node_id,
|
||||
node_type: 'parallel',
|
||||
output: {
|
||||
branch_results: branchResults,
|
||||
total_tokens: event.total_tokens,
|
||||
},
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} else {
|
||||
currentSteps.value[existingIndex] = {
|
||||
...currentSteps.value[existingIndex]!,
|
||||
type: 'parallel_complete',
|
||||
content,
|
||||
output: {
|
||||
branch_results: branchResults,
|
||||
total_tokens: event.total_tokens,
|
||||
},
|
||||
status: 'completed',
|
||||
};
|
||||
}
|
||||
updateAssistantMessage(msgId, {
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'parallel_start': {
|
||||
const branches = event.branches || [];
|
||||
currentSteps.value.push({
|
||||
type: 'parallel_start',
|
||||
content: `并行分支开始:${getItemCount(branches)} 个分支`,
|
||||
node_id: event.node_id,
|
||||
node_type: 'parallel',
|
||||
params: { branches },
|
||||
status: 'running',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
updateAssistantMessage(msgId, {
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'node_event': {
|
||||
const nodeEvent = event.event;
|
||||
if (nodeEvent?.type === 'message') {
|
||||
@@ -493,12 +613,28 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
waitingConfig.value = event.waiting_config || event.config;
|
||||
|
||||
const configData = event.waiting_config || (event as any).config;
|
||||
currentSteps.value.push({
|
||||
type: 'waiting_input',
|
||||
content:
|
||||
configData?.title ||
|
||||
configData?.question ||
|
||||
event.node_label ||
|
||||
'等待用户输入',
|
||||
node_id: event.node_id,
|
||||
node_type: event.node_type,
|
||||
params: configData,
|
||||
status: 'running',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
if (configData?.type === 'design_preview') {
|
||||
handleDesignPreview(event, msgId, configData);
|
||||
} else {
|
||||
handleDialogFlowInteraction(msgId, configData);
|
||||
}
|
||||
updateAssistantMessage(msgId, {
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
});
|
||||
|
||||
running.value = false;
|
||||
break;
|
||||
|
||||
@@ -205,13 +205,6 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
return await generateAccessible(preferences.app.accessMode, {
|
||||
...options,
|
||||
fetchMenuListAsync: async () => {
|
||||
const { ElMessage } =
|
||||
await import('element-plus/es/components/message/index');
|
||||
ElMessage({
|
||||
duration: 1500,
|
||||
message: `${$t('common.loadingMenu')}...`,
|
||||
});
|
||||
|
||||
const appContextStore = useAppContextStore();
|
||||
const menus = appContextStore.appCode
|
||||
? await getAllMenusApi(appContextStore.appCode)
|
||||
|
||||
Reference in New Issue
Block a user