feat(web): workflow add opening_statement
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-03-13 17:27:52
|
* @Date: 2026-03-13 17:27:52
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-31 16:04:15
|
* @Last Modified time: 2026-04-02 17:58:07
|
||||||
*/
|
*/
|
||||||
import { type FC, useState, useRef, useEffect } from 'react'
|
import { type FC, useState, useRef, useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@@ -63,6 +63,12 @@ interface NodeData {
|
|||||||
state: Record<string, any>;
|
state: Record<string, any>;
|
||||||
status?: 'completed' | 'failed';
|
status?: 'completed' | 'failed';
|
||||||
audio_url?: string;
|
audio_url?: string;
|
||||||
|
citations?: {
|
||||||
|
document_id: string;
|
||||||
|
file_name: string;
|
||||||
|
knowledge_id: string;
|
||||||
|
score: string;
|
||||||
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const TestChat: FC<TestChatProps> = ({
|
const TestChat: FC<TestChatProps> = ({
|
||||||
@@ -111,8 +117,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let initVariables: Variable[] = []
|
let initVariables: Variable[] = []
|
||||||
|
|
||||||
switch (application.type) {
|
switch (application.type) {
|
||||||
@@ -162,7 +167,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateAssistantMessage = (content: string, audio_url?: string, audio_status?: string, citations?: any[]) => {
|
const updateAssistantMessage = (content: string, audio_url?: string, audio_status?: string, citations?: NodeData['citations']) => {
|
||||||
setChatList(prev => {
|
setChatList(prev => {
|
||||||
const newList = [...prev]
|
const newList = [...prev]
|
||||||
const lastMsg = newList[newList.length - 1]
|
const lastMsg = newList[newList.length - 1]
|
||||||
@@ -281,12 +286,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
data.map(item => {
|
data.map(item => {
|
||||||
const { conversation_id, content, message_length, audio_url, citations } = item.data as {
|
const { conversation_id, content, message_length, audio_url, citations } = item.data as {
|
||||||
conversation_id: string, content: string, message_length: number; audio_url?: string;
|
conversation_id: string, content: string, message_length: number; audio_url?: string;
|
||||||
citations?: {
|
citations?: NodeData['citations']
|
||||||
document_id: string;
|
|
||||||
file_name: string;
|
|
||||||
knowledge_id: string;
|
|
||||||
score: string;
|
|
||||||
}[]
|
|
||||||
};
|
};
|
||||||
switch (item.event) {
|
switch (item.event) {
|
||||||
case 'start':
|
case 'start':
|
||||||
@@ -344,15 +344,15 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleWorkflowSend = () => {
|
const handleWorkflowSend = (msg?: string) => {
|
||||||
if (loading || !application || !message || !message?.trim()) return
|
if (loading || !application || !((message && message?.trim() !== '') || (msg && msg?.trim() !== ''))) return
|
||||||
const files = (toolbarRef.current?.getFiles() || []).filter(item => !['uploading', 'error'].includes(item.status))
|
const files = (toolbarRef.current?.getFiles() || []).filter(item => !['uploading', 'error'].includes(item.status))
|
||||||
const variables = toolbarRef.current?.getVariables() || []
|
const variables = toolbarRef.current?.getVariables() || []
|
||||||
const { isCanSend, params } = buildVariableParams(variables)
|
const { isCanSend, params } = buildVariableParams(variables)
|
||||||
if (!isCanSend) return
|
if (!isCanSend) return
|
||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
addUserMessage(message, files)
|
addUserMessage((msg || message) as string, files)
|
||||||
addAssistantMessage()
|
addAssistantMessage()
|
||||||
toolbarRef.current?.setFiles([])
|
toolbarRef.current?.setFiles([])
|
||||||
setFileList([])
|
setFileList([])
|
||||||
@@ -361,7 +361,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
|
|
||||||
draftRun(
|
draftRun(
|
||||||
application.id,
|
application.id,
|
||||||
formatParams(message, conversationId, files, params),
|
formatParams((msg || message) as string, conversationId, files, params),
|
||||||
handleWorkflowStreamMessage
|
handleWorkflowStreamMessage
|
||||||
)
|
)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -383,7 +383,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
|
|
||||||
const handleWorkflowStreamMessage = (data: SSEMessage[]) => {
|
const handleWorkflowStreamMessage = (data: SSEMessage[]) => {
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
const { content, conversation_id } = item.data as NodeData;
|
const { content, conversation_id, citations } = item.data as NodeData;
|
||||||
switch (item.event) {
|
switch (item.event) {
|
||||||
// Append streaming text chunks to assistant message
|
// Append streaming text chunks to assistant message
|
||||||
case 'message':
|
case 'message':
|
||||||
@@ -412,6 +412,9 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
// Mark workflow as complete
|
// Mark workflow as complete
|
||||||
case 'workflow_end':
|
case 'workflow_end':
|
||||||
updateWorkflowEndMessage(item.data as NodeData)
|
updateWorkflowEndMessage(item.data as NodeData)
|
||||||
|
if (citations && citations.length > 0) {
|
||||||
|
updateWorkflowEndMessage(item.data as NodeData, citations)
|
||||||
|
}
|
||||||
setStreamLoading(false)
|
setStreamLoading(false)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
break
|
break
|
||||||
@@ -536,7 +539,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateWorkflowEndMessage = (data: NodeData) => {
|
const updateWorkflowEndMessage = (data: NodeData, citations?: NodeData['citations']) => {
|
||||||
const { error, status } = data;
|
const { error, status } = data;
|
||||||
setChatList(prev => {
|
setChatList(prev => {
|
||||||
const newList = [...prev]
|
const newList = [...prev]
|
||||||
@@ -547,6 +550,10 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
status,
|
status,
|
||||||
error,
|
error,
|
||||||
content: newList[lastIndex].content === '' ? null : newList[lastIndex].content,
|
content: newList[lastIndex].content === '' ? null : newList[lastIndex].content,
|
||||||
|
meta_data: {
|
||||||
|
...newList[lastIndex].meta_data || {},
|
||||||
|
citations
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newList
|
return newList
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 16:27:56
|
* @Date: 2026-02-03 16:27:56
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-27 17:32:10
|
* @Last Modified time: 2026-04-02 17:49:51
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Copy Application Modal
|
* Copy Application Modal
|
||||||
@@ -116,24 +116,24 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
>
|
>
|
||||||
<Flex vertical gap={12}>
|
<Flex vertical gap={12}>
|
||||||
|
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||||
|
<SwitchFormItem
|
||||||
|
title={t('application.opening_statement')}
|
||||||
|
name={['opening_statement', "enabled"]}
|
||||||
|
desc={values?.opening_statement?.enabled ? undefined : t('application.opening_statement_desc')}
|
||||||
|
/>
|
||||||
|
{values?.opening_statement?.enabled && (() => {
|
||||||
|
const statement = values.opening_statement?.statement
|
||||||
|
return statement && statement.trim() !== '' ? <>
|
||||||
|
<div className="rb:bg-white rb:rounded-lg rb:py-1 rb:px-3 rb:mb-1">
|
||||||
|
{statement}
|
||||||
|
</div>
|
||||||
|
<Button block onClick={handleOpenStatementSettings}>{t('application.editOpeningStatement')}</Button>
|
||||||
|
</> : <Button block onClick={handleOpenStatementSettings}>{t('application.editOpeningStatement')}</Button>
|
||||||
|
})()}
|
||||||
|
<Form.Item name="opening_statement" hidden />
|
||||||
|
</div>
|
||||||
{source !== 'workflow' && <>
|
{source !== 'workflow' && <>
|
||||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
|
||||||
<SwitchFormItem
|
|
||||||
title={t('application.opening_statement')}
|
|
||||||
name={['opening_statement', "enabled"]}
|
|
||||||
desc={values?.opening_statement?.enabled ? undefined : t('application.opening_statement_desc')}
|
|
||||||
/>
|
|
||||||
{values?.opening_statement?.enabled && (() => {
|
|
||||||
const statement = values.opening_statement?.statement
|
|
||||||
return statement && statement.trim() !== '' ? <>
|
|
||||||
<div className="rb:bg-white rb:rounded-lg rb:py-1 rb:px-3 rb:mb-1">
|
|
||||||
{statement}
|
|
||||||
</div>
|
|
||||||
<Button block onClick={handleOpenStatementSettings}>{t('application.editOpeningStatement')}</Button>
|
|
||||||
</> : <Button block onClick={handleOpenStatementSettings}>{t('application.editOpeningStatement')}</Button>
|
|
||||||
})()}
|
|
||||||
<Form.Item name="opening_statement" hidden />
|
|
||||||
</div>
|
|
||||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||||
<SwitchFormItem
|
<SwitchFormItem
|
||||||
title={t(`memoryConversation.web_search`)}
|
title={t(`memoryConversation.web_search`)}
|
||||||
@@ -148,14 +148,14 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
|
|||||||
desc={t('application.text_to_speech_desc')}
|
desc={t('application.text_to_speech_desc')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
|
||||||
<SwitchFormItem
|
|
||||||
title={t(`application.citation`)}
|
|
||||||
name={['citation', "enabled"]}
|
|
||||||
desc={t('application.citation_desc')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>}
|
</>}
|
||||||
|
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||||
|
<SwitchFormItem
|
||||||
|
title={t(`application.citation`)}
|
||||||
|
name={['citation', "enabled"]}
|
||||||
|
desc={t('application.citation_desc')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||||
<SwitchFormItem
|
<SwitchFormItem
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-06 21:10:56
|
* @Date: 2026-02-06 21:10:56
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-27 17:30:47
|
* @Last Modified time: 2026-04-02 18:01:09
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Workflow Chat Component
|
* Workflow Chat Component
|
||||||
@@ -66,6 +66,17 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
*/
|
*/
|
||||||
const handleOpen = () => {
|
const handleOpen = () => {
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
|
|
||||||
|
if (features?.opening_statement?.statement && features?.opening_statement?.statement.trim() !== '') {
|
||||||
|
setChatList(prev => [...prev, {
|
||||||
|
role: 'assistant',
|
||||||
|
created_at: Date.now(),
|
||||||
|
content: features?.opening_statement?.statement,
|
||||||
|
meta_data: {
|
||||||
|
suggested_questions: features?.opening_statement?.suggested_questions || []
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -164,7 +175,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
*/
|
*/
|
||||||
const handleStreamMessage = (data: SSEMessage[]) => {
|
const handleStreamMessage = (data: SSEMessage[]) => {
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
const { content, conversation_id, node_id, cycle_id, cycle_idx, input, output, error, elapsed_time, status } = item.data as {
|
const { content, conversation_id, node_id, cycle_id, cycle_idx, input, output, error, elapsed_time, status, citations } = item.data as {
|
||||||
content: string;
|
content: string;
|
||||||
conversation_id: string | null;
|
conversation_id: string | null;
|
||||||
cycle_id: string;
|
cycle_id: string;
|
||||||
@@ -177,7 +188,13 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
elapsed_time?: string;
|
elapsed_time?: string;
|
||||||
error?: any;
|
error?: any;
|
||||||
state: Record<string, any>;
|
state: Record<string, any>;
|
||||||
status?: 'completed' | 'failed'
|
status?: 'completed' | 'failed',
|
||||||
|
citations?: {
|
||||||
|
document_id: string;
|
||||||
|
file_name: string;
|
||||||
|
knowledge_id: string;
|
||||||
|
score: string;
|
||||||
|
}[]
|
||||||
};
|
};
|
||||||
|
|
||||||
const node = graphRef.current?.getNodes().find(n => n.id === node_id);
|
const node = graphRef.current?.getNodes().find(n => n.id === node_id);
|
||||||
@@ -312,6 +329,10 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
status,
|
status,
|
||||||
error,
|
error,
|
||||||
content: newList[lastIndex].content === '' ? null : newList[lastIndex].content,
|
content: newList[lastIndex].content === '' ? null : newList[lastIndex].content,
|
||||||
|
meta_data: {
|
||||||
|
...newList[lastIndex].meta_data || {},
|
||||||
|
citations
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newList
|
return newList
|
||||||
@@ -421,6 +442,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
renderRuntime={(item, index) => {
|
renderRuntime={(item, index) => {
|
||||||
return <Runtime item={item} index={index} />
|
return <Runtime item={item} index={index} />
|
||||||
}}
|
}}
|
||||||
|
onSend={handleSend}
|
||||||
/>
|
/>
|
||||||
<Flex align="center" gap={10} className="rb:relative rb:m-4! rb:mb-1!">
|
<Flex align="center" gap={10} className="rb:relative rb:m-4! rb:mb-1!">
|
||||||
<ChatInput
|
<ChatInput
|
||||||
|
|||||||
Reference in New Issue
Block a user