### What problem does this PR solve? Feat: Use the message_id returned by the interface as the id of the reply message #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| [], | [], | ||||
| ); | ); | ||||
| const addNewestOneQuestion = useCallback((message: Message) => { | |||||
| setDerivedMessages((pre) => { | |||||
| return [ | |||||
| ...pre, | |||||
| { | |||||
| ...message, | |||||
| id: buildMessageUuid(message), // The message id is generated on the front end, | |||||
| // and the message id returned by the back end is the same as the question id, | |||||
| // so that the pair of messages can be deleted together when deleting the message | |||||
| }, | |||||
| ]; | |||||
| }); | |||||
| }, []); | |||||
| // Add the streaming message to the last item in the message list | // Add the streaming message to the last item in the message list | ||||
| const addNewestAnswer = useCallback((answer: IAnswer) => { | const addNewestAnswer = useCallback((answer: IAnswer) => { | ||||
| setDerivedMessages((pre) => { | setDerivedMessages((pre) => { | ||||
| }); | }); | ||||
| }, []); | }, []); | ||||
| // Add the streaming message to the last item in the message list | |||||
| const addNewestOneAnswer = useCallback((answer: IAnswer) => { | |||||
| setDerivedMessages((pre) => { | |||||
| const idx = pre.findIndex((x) => x.id === answer.id); | |||||
| if (idx !== -1) { | |||||
| return pre.map((x) => { | |||||
| if (x.id === answer.id) { | |||||
| return { ...x, content: answer.answer }; | |||||
| } | |||||
| return x; | |||||
| }); | |||||
| } | |||||
| return [ | |||||
| ...(pre ?? []), | |||||
| { | |||||
| role: MessageType.Assistant, | |||||
| content: answer.answer, | |||||
| reference: answer.reference, | |||||
| id: buildMessageUuid({ | |||||
| id: answer.id, | |||||
| role: MessageType.Assistant, | |||||
| }), | |||||
| prompt: answer.prompt, | |||||
| audio_binary: answer.audio_binary, | |||||
| ...omit(answer, 'reference'), | |||||
| }, | |||||
| ]; | |||||
| }); | |||||
| }, []); | |||||
| const removeLatestMessage = useCallback(() => { | const removeLatestMessage = useCallback(() => { | ||||
| setDerivedMessages((pre) => { | setDerivedMessages((pre) => { | ||||
| const nextMessages = pre?.slice(0, -2) ?? []; | const nextMessages = pre?.slice(0, -2) ?? []; | ||||
| addNewestAnswer, | addNewestAnswer, | ||||
| removeLatestMessage, | removeLatestMessage, | ||||
| removeMessageById, | removeMessageById, | ||||
| addNewestOneQuestion, | |||||
| addNewestOneAnswer, | |||||
| removeMessagesAfterCurrentMessage, | removeMessagesAfterCurrentMessage, | ||||
| }; | }; | ||||
| }; | }; |
| <AccordionItem value="item-5"> | <AccordionItem value="item-5"> | ||||
| <AccordionTrigger className="text-xl">Tools</AccordionTrigger> | <AccordionTrigger className="text-xl">Tools</AccordionTrigger> | ||||
| <AccordionContent className="flex flex-col gap-4 text-balance"> | <AccordionContent className="flex flex-col gap-4 text-balance"> | ||||
| <OperatorItemList operators={[Operator.Tavily]}></OperatorItemList> | |||||
| <OperatorItemList | |||||
| operators={[Operator.TavilySearch]} | |||||
| ></OperatorItemList> | |||||
| </AccordionContent> | </AccordionContent> | ||||
| </AccordionItem> | </AccordionItem> | ||||
| </Accordion> | </Accordion> |
| removeLatestMessage, | removeLatestMessage, | ||||
| removeMessageById, | removeMessageById, | ||||
| removeMessagesAfterCurrentMessage, | removeMessagesAfterCurrentMessage, | ||||
| addNewestOneQuestion, | |||||
| addNewestOneAnswer, | |||||
| } = useSelectDerivedMessages(); | } = useSelectDerivedMessages(); | ||||
| return { | return { | ||||
| addNewestAnswer, | addNewestAnswer, | ||||
| removeLatestMessage, | removeLatestMessage, | ||||
| removeMessageById, | removeMessageById, | ||||
| addNewestOneQuestion, | |||||
| addNewestOneAnswer, | |||||
| removeMessagesAfterCurrentMessage, | removeMessagesAfterCurrentMessage, | ||||
| }; | }; | ||||
| }; | }; | ||||
| (x) => x.event === MessageEventType.Message, | (x) => x.event === MessageEventType.Message, | ||||
| ) as IMessageEvent[]; | ) as IMessageEvent[]; | ||||
| return { | return { | ||||
| id: messageEventList[0]?.message_id, | |||||
| id: eventList[0]?.message_id, | |||||
| content: messageEventList.map((x) => x.data.content).join(''), | content: messageEventList.map((x) => x.data.content).join(''), | ||||
| }; | }; | ||||
| } | } | ||||
| addNewestAnswer, | addNewestAnswer, | ||||
| removeLatestMessage, | removeLatestMessage, | ||||
| removeMessageById, | removeMessageById, | ||||
| addNewestOneQuestion, | |||||
| addNewestOneAnswer, | |||||
| } = useSelectNextMessages(); | } = useSelectNextMessages(); | ||||
| const { id: agentId } = useParams(); | const { id: agentId } = useParams(); | ||||
| const { handleInputChange, value, setValue } = useHandleMessageInputChange(); | const { handleInputChange, value, setValue } = useHandleMessageInputChange(); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| const { content, id } = findMessageFromList(answerList); | const { content, id } = findMessageFromList(answerList); | ||||
| if (content) { | |||||
| addNewestAnswer({ | |||||
| if (answerList.length > 0) { | |||||
| addNewestOneAnswer({ | |||||
| answer: content, | answer: content, | ||||
| id: id, | id: id, | ||||
| }); | }); | ||||
| } | } | ||||
| }, [answerList, addNewestAnswer]); | |||||
| }, [answerList, addNewestOneAnswer]); | |||||
| const handlePressEnter = useCallback(() => { | const handlePressEnter = useCallback(() => { | ||||
| if (trim(value) === '') return; | if (trim(value) === '') return; | ||||
| setValue(''); | setValue(''); | ||||
| handleSendMessage({ id, content: value.trim(), role: MessageType.User }); | handleSendMessage({ id, content: value.trim(), role: MessageType.User }); | ||||
| } | } | ||||
| addNewestQuestion({ | |||||
| addNewestOneQuestion({ | |||||
| content: value, | content: value, | ||||
| id, | id, | ||||
| role: MessageType.User, | role: MessageType.User, | ||||
| }); | }); | ||||
| }, [addNewestQuestion, handleSendMessage, done, setValue, value]); | |||||
| }, [value, done, addNewestOneQuestion, setValue, handleSendMessage]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (prologue) { | if (prologue) { | ||||
| addNewestAnswer({ | |||||
| addNewestOneAnswer({ | |||||
| answer: prologue, | answer: prologue, | ||||
| }); | }); | ||||
| } | } | ||||
| }, [addNewestAnswer, prologue]); | |||||
| }, [addNewestOneAnswer, prologue]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| addEventList(answerList); | addEventList(answerList); |
| WaitingDialogue = 'WaitingDialogue', | WaitingDialogue = 'WaitingDialogue', | ||||
| Agent = 'Agent', | Agent = 'Agent', | ||||
| Tool = 'Tool', | Tool = 'Tool', | ||||
| Tavily = 'Tavily', | |||||
| TavilySearch = 'TavilySearch', | |||||
| } | } | ||||
| export const SwitchLogicOperatorOptions = ['and', 'or']; | export const SwitchLogicOperatorOptions = ['and', 'or']; | ||||
| [Operator.Code]: { backgroundColor: '#4c5458' }, | [Operator.Code]: { backgroundColor: '#4c5458' }, | ||||
| [Operator.WaitingDialogue]: { backgroundColor: '#a5d65c' }, | [Operator.WaitingDialogue]: { backgroundColor: '#a5d65c' }, | ||||
| [Operator.Agent]: { backgroundColor: '#a5d65c' }, | [Operator.Agent]: { backgroundColor: '#a5d65c' }, | ||||
| [Operator.Tavily]: { backgroundColor: '#a5d65c' }, | |||||
| [Operator.TavilySearch]: { backgroundColor: '#a5d65c' }, | |||||
| }; | }; | ||||
| export const componentMenuList = [ | export const componentMenuList = [ | ||||
| [Operator.Code]: [Operator.Begin], | [Operator.Code]: [Operator.Begin], | ||||
| [Operator.WaitingDialogue]: [Operator.Begin], | [Operator.WaitingDialogue]: [Operator.Begin], | ||||
| [Operator.Agent]: [Operator.Begin], | [Operator.Agent]: [Operator.Begin], | ||||
| [Operator.TavilySearch]: [Operator.Begin], | |||||
| }; | }; | ||||
| export const NodeMap = { | export const NodeMap = { | ||||
| [Operator.WaitingDialogue]: 'ragNode', | [Operator.WaitingDialogue]: 'ragNode', | ||||
| [Operator.Agent]: 'agentNode', | [Operator.Agent]: 'agentNode', | ||||
| [Operator.Tool]: 'toolNode', | [Operator.Tool]: 'toolNode', | ||||
| [Operator.Tavily]: 'ragNode', | |||||
| [Operator.TavilySearch]: 'ragNode', | |||||
| }; | }; | ||||
| export const LanguageOptions = [ | export const LanguageOptions = [ |
| const currentFormMap = FormConfigMap[operatorName]; | const currentFormMap = FormConfigMap[operatorName]; | ||||
| const OperatorForm = currentFormMap.component ?? EmptyContent; | |||||
| const OperatorForm = currentFormMap?.component ?? EmptyContent; | |||||
| const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ | const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ | ||||
| id: node?.id, | id: node?.id, |
| defaultValues: {}, | defaultValues: {}, | ||||
| schema: z.object({}), | schema: z.object({}), | ||||
| }, | }, | ||||
| [Operator.Tavily]: { | |||||
| [Operator.TavilySearch]: { | |||||
| component: TavilyForm, | component: TavilyForm, | ||||
| defaultValues: {}, | defaultValues: {}, | ||||
| schema: z.object({}), | schema: z.object({}), |
| { | { | ||||
| label: 'Search', | label: 'Search', | ||||
| list: [ | list: [ | ||||
| Operator.Tavily, | |||||
| Operator.TavilySearch, | |||||
| Operator.Google, | Operator.Google, | ||||
| Operator.Bing, | Operator.Bing, | ||||
| Operator.DuckDuckGo, | Operator.DuckDuckGo, |
| [Operator.YahooFinance]: YahooFinanceForm, | [Operator.YahooFinance]: YahooFinanceForm, | ||||
| [Operator.Crawler]: CrawlerForm, | [Operator.Crawler]: CrawlerForm, | ||||
| [Operator.Email]: EmailForm, | [Operator.Email]: EmailForm, | ||||
| [Operator.Tavily]: TavilyForm, | |||||
| [Operator.TavilySearch]: TavilyForm, | |||||
| }; | }; |
| [Operator.Code]: initialCodeValues, | [Operator.Code]: initialCodeValues, | ||||
| [Operator.WaitingDialogue]: initialWaitingDialogueValues, | [Operator.WaitingDialogue]: initialWaitingDialogueValues, | ||||
| [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, | [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, | ||||
| [Operator.Tavily]: initialTavilyValues, | |||||
| [Operator.TavilySearch]: initialTavilyValues, | |||||
| }; | }; | ||||
| }, [llmId]); | }, [llmId]); | ||||
| [Operator.WaitingDialogue]: initialWaitingDialogueValues, | [Operator.WaitingDialogue]: initialWaitingDialogueValues, | ||||
| [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, | [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, | ||||
| [Operator.Tool]: {}, | [Operator.Tool]: {}, | ||||
| [Operator.Tavily]: initialTavilyValues, | |||||
| [Operator.TavilySearch]: initialTavilyValues, | |||||
| }; | }; | ||||
| }, [llmId]); | }, [llmId]); | ||||
| edges: Edge[], | edges: Edge[], | ||||
| nodeId: string, | nodeId: string, | ||||
| isBuildDownstream = true, | isBuildDownstream = true, | ||||
| nodes: Node[], | |||||
| ) => { | ) => { | ||||
| return edges | return edges | ||||
| .filter((y) => y[isBuildDownstream ? 'source' : 'target'] === nodeId) | |||||
| .filter((y) => { | |||||
| const node = nodes.find((x) => x.id === nodeId); | |||||
| let isNotUpstreamTool = true; | |||||
| if (isBuildDownstream && node?.data.label === Operator.Agent) { | |||||
| isNotUpstreamTool = !y.target.startsWith(Operator.Tool); // Exclude the tool operator downstream of the agent operator | |||||
| } | |||||
| return ( | |||||
| y[isBuildDownstream ? 'source' : 'target'] === nodeId && | |||||
| isNotUpstreamTool | |||||
| ); | |||||
| }) | |||||
| .map((y) => y[isBuildDownstream ? 'target' : 'source']); | .map((y) => y[isBuildDownstream ? 'target' : 'source']); | ||||
| }; | }; | ||||
| // initializeOperatorParams(operatorName), // Final processing, for guarantee | // initializeOperatorParams(operatorName), // Final processing, for guarantee | ||||
| ); | ); | ||||
| const ExcludeOperators = [Operator.Note, Operator.Tool]; | |||||
| // construct a dsl based on the node information of the graph | // construct a dsl based on the node information of the graph | ||||
| export const buildDslComponentsByGraph = ( | export const buildDslComponentsByGraph = ( | ||||
| nodes: RAGFlowNodeType[], | nodes: RAGFlowNodeType[], | ||||
| const components: DSLComponents = {}; | const components: DSLComponents = {}; | ||||
| nodes | nodes | ||||
| ?.filter((x) => x.data.label !== Operator.Note) | |||||
| ?.filter((x) => !ExcludeOperators.some((y) => y === x.data.label)) | |||||
| .forEach((x) => { | .forEach((x) => { | ||||
| const id = x.id; | const id = x.id; | ||||
| const operatorName = x.data.label; | const operatorName = x.data.label; | ||||
| x.data.form as Record<string, unknown>, | x.data.form as Record<string, unknown>, | ||||
| ) ?? {}, | ) ?? {}, | ||||
| }, | }, | ||||
| downstream: buildComponentDownstreamOrUpstream(edges, id, true), | |||||
| upstream: buildComponentDownstreamOrUpstream(edges, id, false), | |||||
| downstream: buildComponentDownstreamOrUpstream(edges, id, true, nodes), | |||||
| upstream: buildComponentDownstreamOrUpstream(edges, id, false, nodes), | |||||
| parent_id: x?.parentId, | parent_id: x?.parentId, | ||||
| }; | }; | ||||
| }); | }); |