feat(web): 用markdown显示思考过程
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
"@echofly/fetch-event-source": "^3.0.2",
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
"@tinyflow-ai/react": "^0.1.10",
|
||||
"ahooks": "^3.8.5",
|
||||
"amis": "^6.12.0",
|
||||
"antd": "^5.25.3",
|
||||
"axios": "^1.9.0",
|
||||
|
||||
48
service-web/client/pnpm-lock.yaml
generated
48
service-web/client/pnpm-lock.yaml
generated
@@ -26,6 +26,9 @@ importers:
|
||||
'@tinyflow-ai/react':
|
||||
specifier: ^0.1.10
|
||||
version: 0.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.28.2)
|
||||
ahooks:
|
||||
specifier: ^3.8.5
|
||||
version: 3.8.5(react@18.3.1)
|
||||
amis:
|
||||
specifier: ^6.12.0
|
||||
version: 6.12.0(@types/react@18.3.23)(amis-core@6.12.0(@types/react@18.3.23)(amis-formula@6.12.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1))(amis-ui@6.12.0(@types/react@18.3.23)(amis-core@6.12.0(@types/react@18.3.23)(amis-formula@6.12.0)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1))(amis-formula@6.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(office-viewer@0.3.14(echarts@5.5.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -1077,6 +1080,12 @@ packages:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
|
||||
ahooks@3.8.5:
|
||||
resolution: {integrity: sha512-Y+MLoJpBXVdjsnnBjE5rOSPkQ4DK+8i5aPDzLJdIOsCpo/fiAeXcBY1Y7oWgtOK0TpOz0gFa/XcyO1UGdoqLcw==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
ajv@6.12.6:
|
||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||
|
||||
@@ -1863,6 +1872,9 @@ packages:
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
intersection-observer@0.12.2:
|
||||
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
|
||||
|
||||
invariant@2.2.4:
|
||||
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
|
||||
|
||||
@@ -1898,6 +1910,10 @@ packages:
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
js-cookie@3.0.5:
|
||||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
@@ -2659,6 +2675,9 @@ packages:
|
||||
peerDependencies:
|
||||
react: '>= 16.8'
|
||||
|
||||
react-fast-compare@3.2.2:
|
||||
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
|
||||
|
||||
react-hook-form@7.39.0:
|
||||
resolution: {integrity: sha512-rekW5NMBVG0nslE2choOKThy0zxLWQeoew87yTLwb3C9F91LaXwu/dhfFL/D3hdnSMnrTG60gVN/v6rvCrSOTw==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
@@ -2825,6 +2844,10 @@ packages:
|
||||
scheduler@0.23.2:
|
||||
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
|
||||
|
||||
screenfull@5.2.0:
|
||||
resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
scroll-into-view-if-needed@3.1.0:
|
||||
resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==}
|
||||
|
||||
@@ -4262,6 +4285,19 @@ snapshots:
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
ahooks@3.8.5(react@18.3.1):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.3
|
||||
dayjs: 1.11.13
|
||||
intersection-observer: 0.12.2
|
||||
js-cookie: 3.0.5
|
||||
lodash: 4.17.21
|
||||
react: 18.3.1
|
||||
react-fast-compare: 3.2.2
|
||||
resize-observer-polyfill: 1.5.1
|
||||
screenfull: 5.2.0
|
||||
tslib: 2.8.1
|
||||
|
||||
ajv@6.12.6:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
@@ -5299,6 +5335,8 @@ snapshots:
|
||||
|
||||
inherits@2.0.4: {}
|
||||
|
||||
intersection-observer@0.12.2: {}
|
||||
|
||||
invariant@2.2.4:
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
@@ -5326,6 +5364,8 @@ snapshots:
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
js-cookie@3.0.5: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@4.1.0:
|
||||
@@ -5828,7 +5868,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.3
|
||||
'@rc-component/mini-decimal': 1.1.0
|
||||
classnames: 2.3.2
|
||||
classnames: 2.5.1
|
||||
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -5925,7 +5965,7 @@ snapshots:
|
||||
rc-progress@3.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.3
|
||||
classnames: 2.3.2
|
||||
classnames: 2.5.1
|
||||
rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
@@ -6146,6 +6186,8 @@ snapshots:
|
||||
prop-types: 15.8.1
|
||||
react: 18.3.1
|
||||
|
||||
react-fast-compare@3.2.2: {}
|
||||
|
||||
react-hook-form@7.39.0(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
@@ -6362,6 +6404,8 @@ snapshots:
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
|
||||
screenfull@5.2.0: {}
|
||||
|
||||
scroll-into-view-if-needed@3.1.0:
|
||||
dependencies:
|
||||
compute-scroll-into-view: 3.1.1
|
||||
|
||||
@@ -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