feat(web): 用markdown显示思考过程
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import {ClearOutlined, FileOutlined, UserOutlined} from '@ant-design/icons'
|
||||
import {Bubble, Sender, useXAgent, useXChat, Welcome} from '@ant-design/x'
|
||||
import {fetchEventSource} from '@echofly/fetch-event-source'
|
||||
import {useMount} from 'ahooks'
|
||||
import {Button, Divider, Flex, Popover, Radio, Switch, Tooltip, Typography} from 'antd'
|
||||
import {isEqual, isStrBlank, trim} from 'licia'
|
||||
import markdownIt from 'markdown-it'
|
||||
import {useRef, useState} from 'react'
|
||||
import styled from 'styled-components'
|
||||
@@ -32,7 +34,6 @@ const ConversationDiv = styled.div`
|
||||
border-left: 3px solid;
|
||||
padding-left: 5px;
|
||||
margin-bottom: 10px;
|
||||
white-space: pre-line;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,23 +44,35 @@ const ConversationDiv = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
type ChatMessage = { role: string, content?: string, reason?: string }
|
||||
|
||||
function Conversation() {
|
||||
const abortController = useRef<AbortController | null>(null)
|
||||
const [input, setInput] = useState<string>('')
|
||||
const [think, setThink] = useState<boolean>(true)
|
||||
const [knowledge, setKnowledge] = useState<string>('0')
|
||||
const [knowledgeList, setKnowledgeList] = useState<{ id: string, name: string }[]>([])
|
||||
|
||||
const [agent] = useXAgent<{ role: string, content: string }>({
|
||||
useMount(async () => {
|
||||
let response = await fetch(`${commonInfo.baseAiKnowledgeUrl}/knowledge/list`, {
|
||||
headers: commonInfo.authorizationHeaders,
|
||||
})
|
||||
let items = (await response.json()).data.items
|
||||
setKnowledgeList(items.map((item: { id: string, name: string }) => ({id: item.id, name: item.name})))
|
||||
})
|
||||
|
||||
const [agent] = useXAgent<ChatMessage>({
|
||||
request: async (info, callbacks) => {
|
||||
await fetchEventSource(`${commonInfo.baseAiChatUrl}/chat/async`, {
|
||||
let requestUrl = `${commonInfo.baseAiChatUrl}/chat/async`
|
||||
if (!isEqual('0', knowledge)) {
|
||||
requestUrl = `${requestUrl}?knowledge=${knowledge}`
|
||||
}
|
||||
await fetchEventSource(requestUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers: commonInfo.authorizationHeaders,
|
||||
body: JSON.stringify(info.messages),
|
||||
signal: abortController.current?.signal,
|
||||
onmessage: ev => {
|
||||
console.log(ev)
|
||||
callbacks.onUpdate({
|
||||
id: ev.id,
|
||||
event: 'delta',
|
||||
@@ -73,17 +86,22 @@ function Conversation() {
|
||||
const {onRequest, messages, setMessages} = useXChat({
|
||||
agent,
|
||||
transformMessage: ({originMessage, chunk}) => {
|
||||
let text = ''
|
||||
let content = '', reason = ''
|
||||
try {
|
||||
if (chunk?.data) {
|
||||
text = chunk.data
|
||||
let map = JSON.parse(chunk.data)
|
||||
if (map['content'])
|
||||
content = map['content']
|
||||
if (map['reason'])
|
||||
reason = map['reason']
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
return {
|
||||
content: (originMessage?.content || '') + text,
|
||||
role: 'assistant',
|
||||
content: (originMessage?.content || '') + content,
|
||||
reason: (originMessage?.reason || '') + reason,
|
||||
}
|
||||
},
|
||||
resolveAbortController: controller => {
|
||||
@@ -105,10 +123,17 @@ function Conversation() {
|
||||
background: 'transparent',
|
||||
},
|
||||
},
|
||||
messageRender: content => {
|
||||
messageRender: item => {
|
||||
let content = ''
|
||||
if (!isStrBlank(item['reason'])) {
|
||||
content = `<think>${trim(md.render(item['reason']))}</think>${trim(md.render(item['content']))}`
|
||||
} else {
|
||||
content = trim(md.render(item['content']))
|
||||
}
|
||||
console.log(content)
|
||||
return (
|
||||
<Typography>
|
||||
<div dangerouslySetInnerHTML={{__html: md.render(content)}}/>
|
||||
<div dangerouslySetInnerHTML={{__html: content}}/>
|
||||
</Typography>
|
||||
)
|
||||
},
|
||||
@@ -118,12 +143,20 @@ function Conversation() {
|
||||
avatar: {
|
||||
icon: <UserOutlined/>,
|
||||
},
|
||||
messageRender: item => {
|
||||
return (
|
||||
<Typography>{trim(item['content'])}</Typography>
|
||||
)
|
||||
},
|
||||
},
|
||||
}}
|
||||
items={messages.map(({id, message}) => ({
|
||||
key: id,
|
||||
...message,
|
||||
}))}
|
||||
items={messages.map(({id, message}) => {
|
||||
return {
|
||||
key: id,
|
||||
role: message.role,
|
||||
content: message,
|
||||
}
|
||||
})}
|
||||
/>)
|
||||
: (<div className="conversation-welcome">
|
||||
<Welcome
|
||||
@@ -170,10 +203,12 @@ function Conversation() {
|
||||
flexDirection: 'column',
|
||||
gap: 10,
|
||||
}}
|
||||
disabled={agent.isRequesting()}
|
||||
value={knowledge}
|
||||
onChange={event => setKnowledge(event.target.value)}
|
||||
options={[
|
||||
{value: 1, label: '测试'},
|
||||
{value: 2, label: 'Hudi'},
|
||||
{value: 3, label: 'Apache Hudi'},
|
||||
{value: '0', label: '无'},
|
||||
...knowledgeList.map(k => ({label: k.name, value: k.id})),
|
||||
]}
|
||||
/>}
|
||||
>
|
||||
|
||||
@@ -13,6 +13,10 @@ export const commonInfo = {
|
||||
baseAiChatUrl: 'http://132.126.207.130:35690/hudi_services/ai_chat',
|
||||
baseAiKnowledgeUrl: 'http://132.126.207.130:35690/hudi_services/ai_knowledge',
|
||||
// baseUrl: '/hudi_services/service_web',
|
||||
authorizationHeaders: {
|
||||
'Authorization': 'Basic QXhoRWJzY3dzSkRiWU1IMjpjWXhnM2I0UHRXb1ZENVNqRmF5V3h0blNWc2p6UnNnNA==',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
clusters: {
|
||||
// hudi同步运行集群和yarn队列名称
|
||||
sync: {
|
||||
|
||||
Reference in New Issue
Block a user