### What problem does this PR solve? feat: add icon to graph nodes #918 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.8.0
| const { conversationId } = useGetChatSearchParams(); | const { conversationId } = useGetChatSearchParams(); | ||||
| const { handleInputChange, value, setValue } = useHandleMessageInputChange(); | const { handleInputChange, value, setValue } = useHandleMessageInputChange(); | ||||
| const fetchConversation = useFetchConversation(); | |||||
| const { handleClickConversation } = useClickConversationCard(); | const { handleClickConversation } = useClickConversationCard(); | ||||
| const { send, answer, done } = useSendMessageWithSse(); | const { send, answer, done } = useSendMessageWithSse(); | ||||
| const sendMessage = useCallback( | const sendMessage = useCallback( | ||||
| async (message: string, id?: string) => { | async (message: string, id?: string) => { | ||||
| const res: Response = await send({ | |||||
| const res = await send({ | |||||
| conversation_id: id ?? conversationId, | conversation_id: id ?? conversationId, | ||||
| messages: [ | messages: [ | ||||
| ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')), | ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')), | ||||
| ], | ], | ||||
| }); | }); | ||||
| if (res.status === 200) { | |||||
| if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) { | |||||
| // cancel loading | |||||
| setValue(message); | |||||
| console.info('removeLatestMessage111'); | |||||
| removeLatestMessage(); | |||||
| } else { | |||||
| if (id) { | if (id) { | ||||
| console.info('111'); | console.info('111'); | ||||
| // new conversation | // new conversation | ||||
| console.info('222'); | console.info('222'); | ||||
| // fetchConversation(conversationId); | // fetchConversation(conversationId); | ||||
| } | } | ||||
| } else { | |||||
| console.info('333'); | |||||
| // cancel loading | |||||
| setValue(message); | |||||
| console.info('removeLatestMessage111'); | |||||
| removeLatestMessage(); | |||||
| } | } | ||||
| console.info('false'); | |||||
| }, | }, | ||||
| [ | [ | ||||
| conversation?.message, | conversation?.message, |
| const sendMessage = useCallback( | const sendMessage = useCallback( | ||||
| async (message: string, id?: string) => { | async (message: string, id?: string) => { | ||||
| const res: Response = await send({ | |||||
| const res = await send({ | |||||
| conversation_id: id ?? conversationId, | conversation_id: id ?? conversationId, | ||||
| quote: false, | quote: false, | ||||
| messages: [ | messages: [ | ||||
| ], | ], | ||||
| }); | }); | ||||
| if (res?.status === 200) { | |||||
| // const data = await fetchConversation(conversationId); | |||||
| // if (data.retcode === 0) { | |||||
| // setCurrentConversation(data.data); | |||||
| // } | |||||
| } else { | |||||
| if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) { | |||||
| // cancel loading | // cancel loading | ||||
| setValue(message); | setValue(message); | ||||
| removeLatestMessage(); | removeLatestMessage(); |
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { Handle, NodeProps, Position } from 'reactflow'; | import { Handle, NodeProps, Position } from 'reactflow'; | ||||
| import { Space } from 'antd'; | |||||
| import { Operator } from '../../constant'; | |||||
| import OperatorIcon from '../../operator-icon'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| export function TextUpdaterNode({ | export function TextUpdaterNode({ | ||||
| selected, | selected, | ||||
| }: NodeProps<{ label: string }>) { | }: NodeProps<{ label: string }>) { | ||||
| return ( | return ( | ||||
| <div | |||||
| <section | |||||
| className={classNames(styles.textUpdaterNode, { | className={classNames(styles.textUpdaterNode, { | ||||
| [styles.selectedNode]: selected, | [styles.selectedNode]: selected, | ||||
| })} | })} | ||||
| > | > | ||||
| {/* <PlusCircleOutlined style={{ fontSize: 10 }} /> */} | {/* <PlusCircleOutlined style={{ fontSize: 10 }} /> */} | ||||
| </Handle> | </Handle> | ||||
| <div>{data.label}</div> | |||||
| </div> | |||||
| <div> | |||||
| <Space> | |||||
| <OperatorIcon | |||||
| name={data.label as Operator} | |||||
| fontSize={12} | |||||
| ></OperatorIcon> | |||||
| {data.label} | |||||
| </Space> | |||||
| </div> | |||||
| </section> | |||||
| ); | ); | ||||
| } | } |
| } | } | ||||
| const res = await send(params); | const res = await send(params); | ||||
| if (res?.response.status !== 200 || res?.data?.retcode !== 0) { | |||||
| if (res && (res?.response.status !== 200 || res?.data?.retcode !== 0)) { | |||||
| antMessage.error(res?.data?.retmsg); | antMessage.error(res?.data?.retmsg); | ||||
| // cancel loading | // cancel loading |
| MergeCellsOutlined, | MergeCellsOutlined, | ||||
| RocketOutlined, | RocketOutlined, | ||||
| SendOutlined, | SendOutlined, | ||||
| SlidersOutlined, | |||||
| } from '@ant-design/icons'; | } from '@ant-design/icons'; | ||||
| export enum Operator { | export enum Operator { | ||||
| Answer = 'Answer', | Answer = 'Answer', | ||||
| } | } | ||||
| export const operatorIconMap = { | |||||
| [Operator.Retrieval]: RocketOutlined, | |||||
| [Operator.Generate]: MergeCellsOutlined, | |||||
| [Operator.Answer]: SendOutlined, | |||||
| [Operator.Begin]: SlidersOutlined, | |||||
| }; | |||||
| export const componentList = [ | export const componentList = [ | ||||
| { name: Operator.Retrieval, icon: <RocketOutlined />, description: '' }, | |||||
| { name: Operator.Generate, icon: <MergeCellsOutlined />, description: '' }, | |||||
| { name: Operator.Answer, icon: <SendOutlined />, description: '' }, | |||||
| { | |||||
| name: Operator.Retrieval, | |||||
| description: '', | |||||
| }, | |||||
| { | |||||
| name: Operator.Generate, | |||||
| description: '', | |||||
| }, | |||||
| { | |||||
| name: Operator.Answer, | |||||
| description: '', | |||||
| }, | |||||
| ]; | ]; | ||||
| export const initialRetrievalValues = { | export const initialRetrievalValues = { |
| import { Avatar, Card, Flex, Layout, Space } from 'antd'; | |||||
| import { Card, Flex, Layout, Space } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { componentList } from '../constant'; | import { componentList } from '../constant'; | ||||
| import { useHandleDrag } from '../hooks'; | import { useHandleDrag } from '../hooks'; | ||||
| import OperatorIcon from '../operator-icon'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| const { Sider } = Layout; | const { Sider } = Layout; | ||||
| onCollapse={(value) => setCollapsed(value)} | onCollapse={(value) => setCollapsed(value)} | ||||
| > | > | ||||
| <Flex vertical gap={10} className={styles.siderContent}> | <Flex vertical gap={10} className={styles.siderContent}> | ||||
| {componentList.map((x) => ( | |||||
| <Card | |||||
| key={x.name} | |||||
| hoverable | |||||
| draggable | |||||
| className={classNames(styles.operatorCard)} | |||||
| onDragStart={handleDragStart(x.name)} | |||||
| > | |||||
| <Flex justify="space-between" align="center"> | |||||
| <Space size={15}> | |||||
| <Avatar icon={x.icon} shape={'square'} /> | |||||
| <section> | |||||
| <b>{x.name}</b> | |||||
| <div>{x.description}</div> | |||||
| </section> | |||||
| </Space> | |||||
| </Flex> | |||||
| </Card> | |||||
| ))} | |||||
| {componentList.map((x) => { | |||||
| return ( | |||||
| <Card | |||||
| key={x.name} | |||||
| hoverable | |||||
| draggable | |||||
| className={classNames(styles.operatorCard)} | |||||
| onDragStart={handleDragStart(x.name)} | |||||
| > | |||||
| <Flex justify="space-between" align="center"> | |||||
| <Space size={15}> | |||||
| <OperatorIcon name={x.name}></OperatorIcon> | |||||
| {/* <Avatar | |||||
| icon={<OperatorIcon name={x.name}></OperatorIcon>} | |||||
| shape={'square'} | |||||
| /> */} | |||||
| <section> | |||||
| <b>{x.name}</b> | |||||
| <div>{x.description}</div> | |||||
| </section> | |||||
| </Space> | |||||
| </Flex> | |||||
| </Card> | |||||
| ); | |||||
| })} | |||||
| </Flex> | </Flex> | ||||
| </Sider> | </Sider> | ||||
| ); | ); |
| .icon { | |||||
| color: rgb(59, 118, 244); | |||||
| font-size: 24px; | |||||
| } |
| import React from 'react'; | |||||
| import { Operator, operatorIconMap } from '../constant'; | |||||
| import styles from './index.less'; | |||||
| interface IProps { | |||||
| name: Operator; | |||||
| fontSize?: number; | |||||
| } | |||||
| const OperatorIcon = ({ name, fontSize }: IProps) => { | |||||
| const Icon = operatorIconMap[name] || React.Fragment; | |||||
| return <Icon className={styles.icon} style={{ fontSize }}></Icon>; | |||||
| }; | |||||
| export default OperatorIcon; |