feat(task): 添加任务修订功能和超时处理机制
添加了 `revise_task` 路由动作类型,允许用户修改、纠正或重新执行最新活动任务结果。 实现了工具失败指导原则,防止相同类别工具重复失败。 为任务规划器添加了超时处理机制,避免长时间等待。 BREAKING CHANGE: 任务路由逻辑已更新,新增 `revise_task` 动作类型。 fix(api): 修复任务详情API返回完整流程投影 修复了任务详情API端点,现在会包含过滤后的流程运行、事件和工件信息, 并确保时间戳字段正确序列化。 refactor(engine): 优化任务技能解析器摘要节点处理 改进了任务技能解析器对摘要节点的处理逻辑,对于仅依赖文本生成功能的摘要节 点不再分配具体技能,直接使用依赖项输出进行汇总。 test: 增加任务修订和超时处理测试用例 添加了测试用例验证任务修订输入记录反馈、超时回退到单模式以及 摘要节点技能解析等新功能。
This commit is contained in:
@ -6,6 +6,7 @@ import { Bot, CheckCircle2, ChevronRight, Loader2, Paperclip, RefreshCcw, Thumbs
|
||||
|
||||
import type { ChatMessage, ProcessArtifact, ProcessEvent, ProcessRun } from '@/types';
|
||||
import { getAccessToken, getFileUrl } from '@/lib/api';
|
||||
import { getTaskCardMessageIndexes } from '@/lib/chat-messages';
|
||||
import { AgentTeamBlock } from '@/components/chat-workbench/AgentTeamBlock';
|
||||
import { MarkdownContent } from '@/components/chat-workbench/MarkdownContent';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
@ -40,17 +41,21 @@ function AuthImage({ src, alt, className }: { src: string; alt: string; classNam
|
||||
|
||||
function MessageBubble({
|
||||
message,
|
||||
showTaskCard,
|
||||
canSendFeedback,
|
||||
onFeedback,
|
||||
onRequestRevision,
|
||||
}: {
|
||||
message: ChatMessage;
|
||||
showTaskCard: boolean;
|
||||
canSendFeedback: boolean;
|
||||
onFeedback: (runId: string, feedbackType: 'satisfied' | 'revise' | 'abandon', comment?: string) => void;
|
||||
onRequestRevision: (runId: string) => void;
|
||||
}) {
|
||||
const { locale } = useAppI18n();
|
||||
const isUser = message.role === 'user';
|
||||
const textContent = typeof message.content === 'string' ? message.content : String(message.content || '');
|
||||
const [feedbackMode, setFeedbackMode] = React.useState<'satisfied' | 'revise' | null>(null);
|
||||
const [feedbackMode, setFeedbackMode] = React.useState<'satisfied' | null>(null);
|
||||
const [feedbackComment, setFeedbackComment] = React.useState('');
|
||||
const validationFailed = message.validation_status === 'failed';
|
||||
const validationDetails =
|
||||
@ -118,7 +123,7 @@ function MessageBubble({
|
||||
) : (
|
||||
<MarkdownContent content={textContent} />
|
||||
)}
|
||||
{!isUser && message.task_id && (
|
||||
{!isUser && showTaskCard && message.task_id && (
|
||||
<div className="mt-3 rounded-md border border-border bg-muted/35 p-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
@ -145,7 +150,7 @@ function MessageBubble({
|
||||
<p className="mt-2 text-xs leading-5 text-muted-foreground">{validationDetails}</p>
|
||||
</details>
|
||||
)}
|
||||
{!isUser && canSendFeedback && message.run_id && (
|
||||
{!isUser && (canSendFeedback || message.feedback_state) && message.run_id && (
|
||||
<div className="mt-3 space-y-2 border-t border-border/70 pt-3">
|
||||
{message.feedback_state ? (
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
@ -171,7 +176,7 @@ function MessageBubble({
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setFeedbackMode('revise')}
|
||||
onClick={() => onRequestRevision(message.run_id!)}
|
||||
className="inline-flex h-8 items-center gap-1 rounded-md border border-border px-3 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<RefreshCcw className="h-3.5 w-3.5" />
|
||||
@ -191,11 +196,7 @@ function MessageBubble({
|
||||
<textarea
|
||||
value={feedbackComment}
|
||||
onChange={(event) => setFeedbackComment(event.target.value)}
|
||||
placeholder={
|
||||
feedbackMode === 'revise'
|
||||
? pickAppText(locale, '写下需要修改的地方...', 'Describe what needs to change...')
|
||||
: pickAppText(locale, '可选:补充说明...', 'Optional note...')
|
||||
}
|
||||
placeholder={pickAppText(locale, '可选:补充说明...', 'Optional note...')}
|
||||
className="min-h-20 w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-ring"
|
||||
/>
|
||||
<div className="flex justify-end gap-2">
|
||||
@ -330,6 +331,7 @@ export function MessageList({
|
||||
selectedRunId,
|
||||
onSelectRun,
|
||||
onFeedback,
|
||||
onRequestRevision,
|
||||
}: {
|
||||
messages: ChatMessage[];
|
||||
isThinking: boolean;
|
||||
@ -341,6 +343,7 @@ export function MessageList({
|
||||
selectedRunId: string | null;
|
||||
onSelectRun: (runId: string) => void;
|
||||
onFeedback: (runId: string, feedbackType: 'satisfied' | 'revise' | 'abandon', comment?: string) => void;
|
||||
onRequestRevision: (runId: string) => void;
|
||||
}) {
|
||||
const { locale } = useAppI18n();
|
||||
const visibleMessages = React.useMemo(
|
||||
@ -361,6 +364,7 @@ export function MessageList({
|
||||
sortTime: parseTimelineTime(message.timestamp) ?? Number.MAX_SAFE_INTEGER / 2 + index,
|
||||
order: index,
|
||||
message,
|
||||
messageIndex: index,
|
||||
}));
|
||||
const teamItems = teamGroups.map((group, index) => ({
|
||||
kind: 'team' as const,
|
||||
@ -377,9 +381,18 @@ export function MessageList({
|
||||
return a.order - b.order;
|
||||
});
|
||||
}, [teamGroups, visibleMessages]);
|
||||
const latestAssistantRunId = [...visibleMessages]
|
||||
const taskCardMessageIndexes = React.useMemo(
|
||||
() => getTaskCardMessageIndexes(visibleMessages),
|
||||
[visibleMessages]
|
||||
);
|
||||
const latestFeedbackRunId = [...visibleMessages]
|
||||
.reverse()
|
||||
.find((message) => message.role === 'assistant' && message.run_id && message.task_id)?.run_id;
|
||||
.find((message) =>
|
||||
message.role === 'assistant'
|
||||
&& message.run_id
|
||||
&& message.task_id
|
||||
&& message.task_status === 'awaiting_feedback'
|
||||
)?.run_id;
|
||||
|
||||
return (
|
||||
<ScrollArea className="h-full px-8" viewportRef={viewportRef}>
|
||||
@ -397,8 +410,10 @@ export function MessageList({
|
||||
<MessageBubble
|
||||
key={item.key}
|
||||
message={item.message}
|
||||
canSendFeedback={Boolean(latestAssistantRunId && item.message.run_id === latestAssistantRunId)}
|
||||
showTaskCard={taskCardMessageIndexes.has(item.messageIndex)}
|
||||
canSendFeedback={Boolean(latestFeedbackRunId && item.message.run_id === latestFeedbackRunId)}
|
||||
onFeedback={onFeedback}
|
||||
onRequestRevision={onRequestRevision}
|
||||
/>
|
||||
) : (
|
||||
<AgentTeamBlock
|
||||
|
||||
Reference in New Issue
Block a user