首页工作流表单卡片化闭环:执行进度、输入禁用与取消入口
- socket 节点事件推进表单卡片执行进度,失败详情展示详细原因 - 工作流执行时禁用输入框,取消入口移到卡片 footer,卡片定格可重试 - 工作流 ModelRequestParams 按详情原样透传后端 - 会话内产出文件卡片展示(预览/下载/删除),移除顶部结果卡片区 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,9 @@
|
||||
// {"type":"reasoning_chunk","message":"思考中","data":{"delta":"你"}} 思考内容增量(逐 chunk)
|
||||
// {"type":"answer_chunk","message":"回答中","data":{"delta":"你"}} 回答内容增量(逐 chunk)
|
||||
// {"type":"answer","message":"作答完成","data":{"answer":"..."}} 最终回答
|
||||
// {"type":"error","message":"..."} 执行失败
|
||||
// {"type":"node_start","message":"开始执行(2/4): xxx","data":{"nodeCount":4,"nodeId":"..","nodeIndex":2,"nodeName":".."}} 工作流节点开始(进度推进)
|
||||
// {"type":"node_complete","message":"执行完成(2/4): xxx","data":同上} 工作流节点完成(进度推进)
|
||||
// {"type":"error","message":"...","error":"节点失败详情"} 执行失败(error 字段含详细原因)
|
||||
// 完成/失败判定均以 type 为准,不再用正则猜测文本。
|
||||
|
||||
export interface WsStreamMessage {
|
||||
@@ -14,6 +16,8 @@ export interface WsStreamMessage {
|
||||
message?: string;
|
||||
data?: any;
|
||||
payload?: any;
|
||||
// 失败详情:error 事件携带的详细原因(含节点名与具体失败信息),比 message 更具体
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/** 后端 ReAct WebSocket 事件类型常量 */
|
||||
@@ -24,6 +28,8 @@ export const WsEventType = {
|
||||
Answer: 'answer', // 最终回答
|
||||
AnswerChunk: 'answer_chunk', // 回答内容增量(逐 chunk)
|
||||
ReasoningChunk: 'reasoning_chunk', // 思考内容增量(逐 chunk)
|
||||
NodeStart: 'node_start', // 工作流节点开始(进度推进)
|
||||
NodeComplete: 'node_complete', // 工作流节点完成(进度推进)
|
||||
Error: 'error', // 出错
|
||||
} as const;
|
||||
|
||||
@@ -62,6 +68,21 @@ export function getStepInfo(msg: WsStreamMessage): { step: number; maxStep: numb
|
||||
return { step, maxStep: typeof maxStep === 'number' ? maxStep : 0 };
|
||||
}
|
||||
|
||||
/** 工作流节点进度:node_start / node_complete → { current, total, nodeName },非节点事件返回 null */
|
||||
export function getNodeProgress(msg: WsStreamMessage): { current: number; total: number; nodeName: string } | null {
|
||||
const t = msg?.type;
|
||||
if (t !== WsEventType.NodeStart && t !== WsEventType.NodeComplete) return null;
|
||||
const d = msg?.data;
|
||||
const nodeIndex = d?.nodeIndex;
|
||||
const nodeCount = d?.nodeCount;
|
||||
if (typeof nodeIndex !== 'number' || typeof nodeCount !== 'number') return null;
|
||||
return {
|
||||
current: nodeIndex,
|
||||
total: nodeCount,
|
||||
nodeName: typeof d?.nodeName === 'string' ? d.nodeName : '',
|
||||
};
|
||||
}
|
||||
|
||||
/** tool_call → 工具名称(tool / toolName / name 依次尝试,缺失返回空串) */
|
||||
export function getToolCallName(msg: WsStreamMessage): string {
|
||||
const d = msg?.data;
|
||||
@@ -87,8 +108,10 @@ export function getToolResultText(msg: WsStreamMessage): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** error → 错误文案(message 优先,其次 data.message / data.error / data.msg) */
|
||||
/** error → 错误文案(error 字段为详细原因优先;其次 message;再 data.message / data.error / data.msg) */
|
||||
export function getErrorText(msg: WsStreamMessage): string {
|
||||
// 详细失败原因(含节点名/具体错误),优先展示
|
||||
if (typeof msg?.error === 'string' && msg.error) return msg.error;
|
||||
if (typeof msg?.message === 'string' && msg.message) return msg.message;
|
||||
const d = msg?.data;
|
||||
if (typeof d === 'string') return d;
|
||||
|
||||
Reference in New Issue
Block a user