Fix: UserFillUp interactive forms not working in agent explore mode (#14589)

## Summary

- **Backend**: `_iter_session_completion_events` in `agent_api.py` was
filtering out `user_inputs` and `workflow_finished` SSE events, causing
agents with UserFillUp components to silently fail in explore mode — the
interactive form never appeared, while the same agent worked correctly
in run (editor) mode.
- **Frontend**: `SessionChat` component in explore mode was missing
`DebugContent` children rendering inside `MessageItem`, so even if the
backend forwarded the events, the form UI would not render. Added
`DebugContent`, `MarkdownContent`, `useAwaitCompentData` hook, and
input-disabling logic to match the run mode's `chat/box.tsx` behavior.

## What was changed

### Backend (`api/apps/restful_apis/agent_api.py`)
- Line 266: Added `"user_inputs"` and `"workflow_finished"` to the
allowed event filter in `_iter_session_completion_events`

### Frontend (`web/src/pages/agent/explore/components/session-chat.tsx`)
- Added imports: `DebugContent`, `MarkdownContent`,
`useAwaitCompentData`, `useParams`
- Added `sendFormMessage` from `useSendSessionMessage()` hook
- Added `useAwaitCompentData` hook for form state management
- Added `DebugContent` as `MessageItem` children for the latest
assistant message (renders UserFillUp form)
- Added `MarkdownContent` + submitted values display for previous
assistant messages
- Updated `NextMessageInput` disabled states to respect `isWaitting`
(form submission in progress)

## Test plan

- [x] Agent with UserFillUp component (e.g., email draft with
send/edit/cancel options) shows interactive form in **explore mode**
- [x] Same agent continues to work correctly in **run (editor) mode**
- [x] Form submission sends data back to the agent and workflow
continues
- [x] Input field is disabled while waiting for form submission
- [ ] Agents without UserFillUp components are unaffected in explore
mode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Zhichang Yu <yuzhichang@gmail.com>
This commit is contained in:
Tim Wang
2026-06-28 21:57:57 +08:00
committed by yzc
parent 212429bf9d
commit f0f10b6092
18 changed files with 330 additions and 196 deletions

View File

@@ -132,6 +132,8 @@ function MessageItem({
return null;
}
const hasCustomChildren = item.data && !!children;
return (
<div
className={cn({
@@ -142,7 +144,7 @@ function MessageItem({
})}
dir={getDirAttribute(messageContent.replace(citationMarkerReg, ''))}
>
{item.data ? (
{hasCustomChildren ? (
children
) : sendLoading && isEmpty(messageContent) ? (
<>{!isShare && t('common.running')}</>

View File

@@ -1,5 +1,6 @@
import { FileUploadProps } from '@/components/file-upload';
import { NextMessageInput } from '@/components/message-input/next';
import MarkdownContent from '@/components/next-markdown-content';
import MessageItem from '@/components/next-message-item';
import PdfSheet from '@/components/pdf-drawer';
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
@@ -8,6 +9,8 @@ import { useUploadAgentFileWithProgress } from '@/hooks/use-agent-request';
import { useFetchUserInfo } from '@/hooks/use-user-setting-request';
import { IAgentLogResponse } from '@/interfaces/database/agent';
import { IMessage } from '@/interfaces/database/chat';
import DebugContent from '@/pages/agent/debug-content';
import { useAwaitComponentData } from '@/pages/agent/hooks/use-chat-logic';
import { BeginQuery } from '@/pages/agent/interface';
import { ParameterDialog } from '@/pages/agent/share/parameter-dialog';
import { buildMessageUuidWithRole } from '@/utils/chat';
@@ -37,6 +40,7 @@ export function SessionChat({ session }: SessionChatProps) {
handleInputChange,
handlePressEnter,
stopOutputMessage,
sendFormMessage,
canvasInfo,
findReferenceByMessageId,
appendUploadResponseList,
@@ -47,6 +51,11 @@ export function SessionChat({ session }: SessionChatProps) {
shouldShowParameterDialog,
setDerivedMessages,
} = useSendSessionMessage();
const { buildInputList, handleOk, isWaiting } = useAwaitComponentData({
derivedMessages,
sendFormMessage,
});
const hasActiveSession = Boolean(
sessionId || isNew || hasLocalMessageRef.current,
);
@@ -122,26 +131,58 @@ export function SessionChat({ session }: SessionChatProps) {
</div>
) : (
<div className="w-full pr-5">
{derivedMessages.map((message, i) => (
<MessageItem
loading={
message.role === MessageType.Assistant &&
sendLoading &&
derivedMessages.length - 1 === i
}
key={buildMessageUuidWithRole(message)}
item={message}
nickname={userInfo.nickname}
avatar={userInfo.avatar}
avatarDialog={canvasInfo?.avatar || ''}
reference={findReferenceByMessageId(message.id)}
clickDocumentButton={clickDocumentButton}
index={i}
showLikeButton={false}
sendLoading={sendLoading}
showLog={false}
/>
))}
{derivedMessages.map((message, i) => {
const inputList = buildInputList(message);
const hasUserFillUpInputs =
message.role === MessageType.Assistant &&
inputList.length > 0;
return (
<MessageItem
loading={
message.role === MessageType.Assistant &&
sendLoading &&
derivedMessages.length - 1 === i
}
key={buildMessageUuidWithRole(message)}
item={message}
nickname={userInfo.nickname}
avatar={userInfo.avatar}
avatarDialog={canvasInfo?.avatar || ''}
reference={findReferenceByMessageId(message.id)}
clickDocumentButton={clickDocumentButton}
index={i}
showLikeButton={false}
sendLoading={sendLoading}
showLog={false}
>
{hasUserFillUpInputs &&
derivedMessages.length - 1 === i && (
<DebugContent
parameters={inputList}
message={message}
ok={handleOk(message)}
isNext={false}
btnText={t('common.submit')}
></DebugContent>
)}
{hasUserFillUpInputs &&
derivedMessages.length - 1 !== i && (
<div>
<MarkdownContent
content={message?.data?.tips}
loading={false}
></MarkdownContent>
<div>
{inputList.map((item) => (
<div key={item.key}>{item.value}</div>
))}
</div>
</div>
)}
</MessageItem>
);
})}
</div>
)}
<div ref={scrollRef} />
@@ -151,9 +192,9 @@ export function SessionChat({ session }: SessionChatProps) {
<NextMessageInput
value={value}
sendLoading={sendLoading}
disabled={false}
sendDisabled={sendLoading}
isUploading={isUploading}
disabled={isWaiting}
sendDisabled={sendLoading || isWaiting}
isUploading={isUploading || isWaiting}
onPressEnter={handleSessionPressEnter}
onInputChange={handleInputChange}
stopOutputMessage={stopOutputMessage}

View File

@@ -40,14 +40,15 @@ const useAwaitComponentData = (props: IAwaitCompentData) => {
const isWaiting = useMemo(() => {
const temp = derivedMessages?.some((message, i) => {
const hasInputs = Object.keys(getInputs(message)).length > 0;
const flag =
message.role === MessageType.Assistant &&
derivedMessages.length - 1 === i &&
message.data;
hasInputs;
return flag;
});
return temp;
}, [derivedMessages]);
}, [derivedMessages, getInputs]);
return { getInputs, buildInputList, handleOk, isWaiting };
};