yt-chat-components 1.2.8 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/YtChatView/chatWidget/chatWindow/chatMessage/index.tsx +59 -18
- package/src/YtChatView/chatWidget/chatWindow/chatPlaceholder/index.tsx +19 -0
- package/src/YtChatView/chatWidget/chatWindow/index.tsx +77 -24
- package/src/YtChatView/chatWidget/chatWindow/types/chatWidget/index.ts +3 -1
- package/src/YtChatView/chatWidget/index.tsx +1 -2437
- package/src/YtChatView/chatWidget/style.js +2463 -0
package/package.json
CHANGED
|
@@ -6,8 +6,8 @@ import remarkGfm from 'remark-gfm';
|
|
|
6
6
|
import rehypeMathjax from 'rehype-mathjax';
|
|
7
7
|
// import './index.module.css';
|
|
8
8
|
import upFilePng from '../../../../assets/aicenter/upfile.png';
|
|
9
|
-
import {Form, Image, Input, message as messageTip, Select,
|
|
10
|
-
import React, {
|
|
9
|
+
import {Button, Collapse, DatePicker, Form, Image, Input, message as messageTip, Select, Skeleton} from 'antd';
|
|
10
|
+
import React, {useRef, useState} from 'react';
|
|
11
11
|
import typePdfPng from '../../../../assets/aicenter/type-pdf.png';
|
|
12
12
|
import typeWordPng from '../../../../assets/aicenter/type-word.png';
|
|
13
13
|
import typeExcelPng from '../../../../assets/aicenter/type-excel.png';
|
|
@@ -18,13 +18,18 @@ import typeRPubPng from '../../../../assets/aicenter/type-rpub.png';
|
|
|
18
18
|
import playPng from '../../../../assets/aicenter/play.png';
|
|
19
19
|
import playRunGif from '../../../../assets/aicenter/play-run.gif';
|
|
20
20
|
import copyPng from '../../../../assets/aicenter/copy.png';
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
23
|
-
import {
|
|
21
|
+
import type {ThoughtChainItem} from '@ant-design/x';
|
|
22
|
+
import {ThoughtChain} from '@ant-design/x';
|
|
23
|
+
import {CheckCircleOutlined, EllipsisOutlined, InfoCircleOutlined, ClockCircleOutlined} from '@ant-design/icons';
|
|
24
|
+
import ChatMessagePlaceholder, {ChatMessagePlaceholderInThought} from "../chatPlaceholder";
|
|
24
25
|
|
|
25
26
|
let speechSynth = window.speechSynthesis;
|
|
26
27
|
let utterance = null;
|
|
27
28
|
|
|
29
|
+
const isEmpty = (o) => {
|
|
30
|
+
return !o || (typeof o === 'object' && Object.keys(o).length === 0) || (typeof o === 'string' && o.trim().length === 0);
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
export default function ChatMessage({
|
|
29
34
|
receivingMessage,
|
|
30
35
|
setReceivingMessage,
|
|
@@ -219,7 +224,7 @@ export default function ChatMessage({
|
|
|
219
224
|
>
|
|
220
225
|
{
|
|
221
226
|
// 其他类型暂不处理
|
|
222
|
-
type == MessageType.echart && (
|
|
227
|
+
(type == MessageType.echart || type == MessageType.form) && (
|
|
223
228
|
<Skeleton.Image active={true} style={{width: '100%', height: '100%'}}/>
|
|
224
229
|
)
|
|
225
230
|
}
|
|
@@ -261,7 +266,7 @@ export default function ChatMessage({
|
|
|
261
266
|
// 渲染 form 组件
|
|
262
267
|
return renderBotFormMessage(form_config);
|
|
263
268
|
} catch (error) {
|
|
264
|
-
return renderLoading(MessageType.
|
|
269
|
+
return renderLoading(MessageType.form, 300);
|
|
265
270
|
}
|
|
266
271
|
}
|
|
267
272
|
// 默认渲染其他代码块
|
|
@@ -281,14 +286,29 @@ export default function ChatMessage({
|
|
|
281
286
|
}
|
|
282
287
|
}
|
|
283
288
|
|
|
289
|
+
const renderThinkTitle = (isThinking) => {
|
|
290
|
+
return <div style={{display: 'flex', alignItems: 'center', color: '#989898', fontSize: 14}}>
|
|
291
|
+
{
|
|
292
|
+
isThinking ? [
|
|
293
|
+
<ClockCircleOutlined style={{color: '#82b6dd'}}/>,
|
|
294
|
+
<span style={{marginLeft: 8}}>正在深度思考</span>
|
|
295
|
+
] : [
|
|
296
|
+
<CheckCircleOutlined style={{color: '#8add82'}} />,
|
|
297
|
+
<span style={{marginLeft: 8}}>已完成深度思考</span>
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
</div>
|
|
301
|
+
}
|
|
302
|
+
|
|
284
303
|
const renderBotTextMessage = () => {
|
|
285
304
|
const items = messageItemList.map((item, index) => {
|
|
286
|
-
const {id, message, type, name, toolCallInfo, rawInfo} = item;
|
|
305
|
+
const {id, thinkMessage, message, loadingMessage, type, name, toolCallInfo, rawInfo} = item;
|
|
306
|
+
const isLatest = index == messageItemList.length - 1;
|
|
287
307
|
let status = 'success'
|
|
288
308
|
// 只有在接收信息的时候有 pending 状态
|
|
289
309
|
if (receivingMessage && isLatest){
|
|
290
310
|
// 最后的信息才为 pending
|
|
291
|
-
if(
|
|
311
|
+
if(isLatest){
|
|
292
312
|
status = 'pending'
|
|
293
313
|
}
|
|
294
314
|
}
|
|
@@ -298,15 +318,36 @@ export default function ChatMessage({
|
|
|
298
318
|
description: toolCallInfo,
|
|
299
319
|
icon: getStatusIcon(status),
|
|
300
320
|
status: status,
|
|
301
|
-
content: <
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
321
|
+
content: <div style={{display:"flex", flexDirection: 'column'}}>
|
|
322
|
+
{
|
|
323
|
+
thinkMessage && <Collapse
|
|
324
|
+
bordered={false}
|
|
325
|
+
style={{marginBottom: 15, color: '#989898'}}
|
|
326
|
+
size="small"
|
|
327
|
+
defaultActiveKey={'1'}
|
|
328
|
+
expandIconPosition={'end'}
|
|
329
|
+
items={[{key: '1', label: renderThinkTitle(isEmpty(message) && isLatest), children: <Markdown
|
|
330
|
+
className={'think markdown-body prose flex flex-col word-break-break-word'}
|
|
331
|
+
remarkPlugins={[remarkGfm]}
|
|
332
|
+
rehypePlugins={[rehypeMathjax]}
|
|
333
|
+
>
|
|
334
|
+
{thinkMessage}
|
|
335
|
+
</Markdown>}]}
|
|
336
|
+
/>
|
|
337
|
+
}
|
|
338
|
+
{/* 有 thinkMessage 的时候没有占位符,否则赞为符输出*/}
|
|
339
|
+
{
|
|
340
|
+
message ? <Markdown
|
|
341
|
+
key={id}
|
|
342
|
+
className={'markdown-body prose flex flex-col word-break-break-word'}
|
|
343
|
+
remarkPlugins={[remarkGfm]}
|
|
344
|
+
rehypePlugins={[rehypeMathjax]}
|
|
345
|
+
components={CustomRender}
|
|
346
|
+
>
|
|
347
|
+
{message}
|
|
348
|
+
</Markdown> : thinkMessage ? "" : <ChatMessagePlaceholderInThought aiStatus={loadingMessage ? loadingMessage : "正在处理数据"} />
|
|
349
|
+
}
|
|
350
|
+
</div>,
|
|
310
351
|
footer: <div key={id} className="msg_operateBox">
|
|
311
352
|
<img src={isPlay ? playRunGif : playPng} onClick={() => playVoice(message)} />
|
|
312
353
|
<img src={copyPng} onClick={() => copyText(message)} />
|
|
@@ -21,3 +21,22 @@ export default function ChatMessagePlaceholder({
|
|
|
21
21
|
</div>
|
|
22
22
|
);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
export function ChatMessagePlaceholderInThought({
|
|
26
|
+
aiStatus
|
|
27
|
+
}: ChatMessagePlaceholderType) {
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
className="cl-chat-message-in-thought cl-justify-start"
|
|
31
|
+
>
|
|
32
|
+
<div className={"cl-bot_message-in-thought"}>
|
|
33
|
+
<div className="cl-animate-pulse">
|
|
34
|
+
<div className="plh_textBox">
|
|
35
|
+
<div className="plh_text">{aiStatus ? aiStatus : "深度思考中"}</div>
|
|
36
|
+
<MoreHorizontal />
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -150,6 +150,7 @@ export default function ChatWindow({
|
|
|
150
150
|
const [inputContainerHeight, setInputContainerHeight] = useState('50px')
|
|
151
151
|
let content_id: string = null
|
|
152
152
|
let content_ns: string = null
|
|
153
|
+
let event_latest: string = null
|
|
153
154
|
const nowAIContentListRef = useRef(nowAIContentList)
|
|
154
155
|
|
|
155
156
|
let voiceChunks = []; // 临时存储录制的语音片段
|
|
@@ -176,7 +177,7 @@ export default function ChatWindow({
|
|
|
176
177
|
if (delayMessageList.length > 0) {
|
|
177
178
|
const data = delayMessageList.shift();
|
|
178
179
|
const {r_id, form_config} = data;
|
|
179
|
-
updateMessageItem("\n```form\n" + JSON.stringify(form_config) + "\n```\n")
|
|
180
|
+
updateMessageItem({chunk:"\n```form\n" + JSON.stringify(form_config) + "\n```\n"})
|
|
180
181
|
|
|
181
182
|
// ?????? done
|
|
182
183
|
// addMessage({
|
|
@@ -193,32 +194,51 @@ export default function ChatWindow({
|
|
|
193
194
|
}
|
|
194
195
|
}
|
|
195
196
|
|
|
196
|
-
const addMessageItem = (chunk, id, name, status = null) => {
|
|
197
|
-
console.log("--- addMessageItem", chunk, status)
|
|
197
|
+
const addMessageItem = ({chunk, id, name, status = null, isThinkChunk = false, loadingMessage = null}) => {
|
|
198
|
+
// console.log("--- addMessageItem", chunk, status, loadingMessage)
|
|
198
199
|
setNowAIContentList((prevState) => {
|
|
200
|
+
const content = {}
|
|
201
|
+
if(isThinkChunk){
|
|
202
|
+
content["thinkMessage"] = chunk
|
|
203
|
+
content["message"] = ""
|
|
204
|
+
}else {
|
|
205
|
+
content["thinkMessage"] = ""
|
|
206
|
+
content["message"] = chunk
|
|
207
|
+
}
|
|
208
|
+
|
|
199
209
|
const messageItem : MessageItem = {
|
|
200
210
|
id,
|
|
201
211
|
name,
|
|
202
|
-
message: chunk,
|
|
203
212
|
type: MessageType.text,
|
|
204
213
|
toolCallInfo: status,
|
|
214
|
+
loadingMessage,
|
|
215
|
+
...content
|
|
205
216
|
}
|
|
206
217
|
nowAIContentListRef.current = [...prevState, messageItem]
|
|
207
218
|
return [...prevState, messageItem]
|
|
208
219
|
})
|
|
209
220
|
}
|
|
210
221
|
|
|
211
|
-
const updateMessageItem = (chunk, status = null) => {
|
|
212
|
-
console.log("--- updateMessageItem", chunk, status)
|
|
222
|
+
const updateMessageItem = ({chunk, status = null, isThinkChunk = false, loadingMessage = null}) => {
|
|
223
|
+
// console.log("--- updateMessageItem", chunk, status)
|
|
213
224
|
setNowAIContentList((prevState) => {
|
|
214
225
|
const latestMessageItem = prevState[prevState.length - 1]
|
|
226
|
+
const content = {}
|
|
227
|
+
if(isThinkChunk){
|
|
228
|
+
content["thinkMessage"] = latestMessageItem.thinkMessage + chunk
|
|
229
|
+
content["message"] = latestMessageItem.message
|
|
230
|
+
}else {
|
|
231
|
+
content["thinkMessage"] = latestMessageItem.thinkMessage
|
|
232
|
+
content["message"] = latestMessageItem.message + chunk
|
|
233
|
+
}
|
|
215
234
|
|
|
216
235
|
const newMessageItem: MessageItem = {
|
|
217
236
|
id: latestMessageItem.id === 'WAIT' ? content_id : latestMessageItem.id,
|
|
218
237
|
name: latestMessageItem.name,
|
|
219
|
-
message: latestMessageItem.message + chunk,
|
|
220
238
|
type: latestMessageItem.type,
|
|
221
239
|
toolCallInfo: status,
|
|
240
|
+
loadingMessage,
|
|
241
|
+
...content
|
|
222
242
|
}
|
|
223
243
|
|
|
224
244
|
const newState = [...prevState]
|
|
@@ -228,17 +248,16 @@ export default function ChatWindow({
|
|
|
228
248
|
return newState
|
|
229
249
|
})
|
|
230
250
|
}
|
|
231
|
-
|
|
232
251
|
|
|
233
252
|
// 流式输出消息,实时显示(token为流式输出内容,end为结束输出,整体输出一次)
|
|
234
253
|
const handleMessageContent = (event, data) => {
|
|
235
|
-
|
|
254
|
+
console.log("--- event, data",event, data)
|
|
236
255
|
|
|
237
256
|
if (event == 'add_message' && data['sender'] == 'Machine') {
|
|
238
257
|
getHistoryList();
|
|
239
258
|
}
|
|
240
|
-
else if (event == 'token') {
|
|
241
|
-
let { chunk, id, r_id, ns, name } = data
|
|
259
|
+
else if (event == 'token' || event == 't_full_token') {
|
|
260
|
+
let { chunk, id, r_id, ns, name, loading_message } = data
|
|
242
261
|
if (chunk.includes('```') && !chunk.startsWith('\n')) {
|
|
243
262
|
// 确保 ``` 前有换行
|
|
244
263
|
chunk = '\n' + chunk;
|
|
@@ -250,14 +269,16 @@ export default function ChatWindow({
|
|
|
250
269
|
// 如果刚才在调用工具,则追加信息
|
|
251
270
|
if (content_id === 'WAIT'){
|
|
252
271
|
content_id = id
|
|
253
|
-
updateMessageItem(chunk)
|
|
272
|
+
updateMessageItem({chunk, loadingMessage: loading_message})
|
|
254
273
|
}
|
|
255
274
|
// 新建信息
|
|
256
275
|
else{
|
|
257
276
|
content_id = id
|
|
258
|
-
addMessageItem(chunk, id, name)
|
|
277
|
+
addMessageItem({chunk, id, name, loadingMessage:loading_message})
|
|
259
278
|
}
|
|
260
|
-
}
|
|
279
|
+
}
|
|
280
|
+
// 输出主体没有变化
|
|
281
|
+
else {
|
|
261
282
|
// 如果 content_id === WAIT 表示 token 发出来之前 agent在调用工具
|
|
262
283
|
// 但是调用工具的时候已经创建了信息,所以这里更新一下 content_id。这样就可以追加信息
|
|
263
284
|
if(content_id === 'WAIT'){
|
|
@@ -267,19 +288,40 @@ export default function ChatWindow({
|
|
|
267
288
|
// id切换表示一句话说完了, 新增信息
|
|
268
289
|
if (content_id !== id){
|
|
269
290
|
content_id = id
|
|
270
|
-
|
|
291
|
+
|
|
292
|
+
// 如果 event_latest 是 token,此刻变成了 t_full_token,表示之前都在输出占位符,所以更新信息
|
|
293
|
+
if (event_latest === 'token' && event === 't_full_token'){
|
|
294
|
+
updateMessageItem({chunk, loadingMessage: loading_message})
|
|
295
|
+
} else {
|
|
296
|
+
addMessageItem({chunk, id, name, loadingMessage:loading_message})
|
|
297
|
+
}
|
|
271
298
|
}else{
|
|
272
|
-
updateMessageItem(chunk)
|
|
299
|
+
updateMessageItem({chunk, loadingMessage: loading_message})
|
|
273
300
|
}
|
|
274
301
|
}
|
|
275
302
|
|
|
276
303
|
if (lastMessage.current) {
|
|
277
304
|
lastMessage.current.scrollIntoView({ behavior: 'smooth' });
|
|
278
305
|
}
|
|
306
|
+
|
|
307
|
+
|
|
279
308
|
}
|
|
280
|
-
else if (event == '
|
|
281
|
-
|
|
282
|
-
|
|
309
|
+
else if (event == 't_token') {
|
|
310
|
+
let { chunk, id, r_id, ns, name, loading_message } = data
|
|
311
|
+
|
|
312
|
+
// ns切换表示切换了智能体
|
|
313
|
+
if (content_ns !== ns){
|
|
314
|
+
content_ns = ns
|
|
315
|
+
// 新建信息
|
|
316
|
+
content_id = id
|
|
317
|
+
addMessageItem({chunk, id, name, status:null, isThinkChunk:true, loadingMessage: loading_message})
|
|
318
|
+
}else {
|
|
319
|
+
updateMessageItem({chunk, status:null, isThinkChunk:true, loadingMessage: loading_message})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (lastMessage.current) {
|
|
323
|
+
lastMessage.current.scrollIntoView({ behavior: 'smooth' });
|
|
324
|
+
}
|
|
283
325
|
}
|
|
284
326
|
else if (event == 'status') {
|
|
285
327
|
// 更新状态
|
|
@@ -288,17 +330,26 @@ export default function ChatWindow({
|
|
|
288
330
|
// ns变化表示切换了智能体,表示智能体一上来就调用工具
|
|
289
331
|
if (content_ns !== ns){
|
|
290
332
|
content_ns = ns
|
|
291
|
-
//
|
|
292
|
-
content_id
|
|
293
|
-
|
|
333
|
+
// 表示智能体连续调用工具
|
|
334
|
+
if (content_id === 'WAIT') {
|
|
335
|
+
updateMessageItem({chunk:"", status, loadingMessage: loading_message})
|
|
336
|
+
}else{
|
|
337
|
+
// 这个时候还没有信息的id,所以 content_id 给特殊值
|
|
338
|
+
content_id = "WAIT"
|
|
339
|
+
addMessageItem({chunk:"", id: "WAIT", name, status})
|
|
340
|
+
}
|
|
294
341
|
}
|
|
295
342
|
// 当前智能体在调用工具
|
|
296
343
|
else{
|
|
297
|
-
updateMessageItem("", status)
|
|
344
|
+
updateMessageItem({chunk:"", status, loadingMessage: loading_message})
|
|
298
345
|
}
|
|
299
346
|
|
|
300
347
|
setAiStatus(data.status)
|
|
301
348
|
}
|
|
349
|
+
else if (event == 'form') {
|
|
350
|
+
// 这里添加到延时队列,直接处理可能会把form渲染到token上面
|
|
351
|
+
delayMessageList.push(data)
|
|
352
|
+
}
|
|
302
353
|
else if (event == 'end') {
|
|
303
354
|
// const res = {
|
|
304
355
|
// data: data['result'],
|
|
@@ -372,6 +423,8 @@ export default function ChatWindow({
|
|
|
372
423
|
content_ns = null
|
|
373
424
|
content_id = null
|
|
374
425
|
}
|
|
426
|
+
|
|
427
|
+
event_latest = event
|
|
375
428
|
};
|
|
376
429
|
|
|
377
430
|
const sendMessageNoStream = (res) => {
|
|
@@ -999,7 +1052,7 @@ export default function ChatWindow({
|
|
|
999
1052
|
if(messages.length > 0){
|
|
1000
1053
|
const { messageItemList } = messages[messages.length - 1]
|
|
1001
1054
|
const latestMessageItem = messageItemList[messageItemList.length - 1]
|
|
1002
|
-
isRender = !latestMessageItem.message.includes("```form")
|
|
1055
|
+
isRender = !(latestMessageItem.message || "").includes("```form")
|
|
1003
1056
|
}
|
|
1004
1057
|
|
|
1005
1058
|
if(!isRender){
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
export enum MessageType {
|
|
2
2
|
text,
|
|
3
|
-
form,
|
|
3
|
+
form, // 目前只用于渲染loading
|
|
4
4
|
echart // 目前只用于渲染loading
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export type MessageItem = {
|
|
8
8
|
id:string,
|
|
9
9
|
name: string;
|
|
10
|
+
thinkMessage: string;
|
|
10
11
|
message: string;
|
|
12
|
+
loadingMessage?: string;
|
|
11
13
|
rawInfo?: any;
|
|
12
14
|
type?: MessageType,
|
|
13
15
|
toolCallInfo?: string
|