@fe-free/ai 6.0.12 → 6.0.14
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/CHANGELOG.md +18 -0
- package/package.json +4 -4
- package/src/ai.stories.tsx +59 -39
- package/src/index.ts +5 -4
- package/src/messages/messages.tsx +25 -13
- package/src/store/index.ts +113 -108
- package/src/store/types.ts +12 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @fe-free/ai
|
|
2
2
|
|
|
3
|
+
## 6.0.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ai
|
|
8
|
+
- @fe-free/core@6.0.14
|
|
9
|
+
- @fe-free/icons@6.0.14
|
|
10
|
+
- @fe-free/tool@6.0.14
|
|
11
|
+
|
|
12
|
+
## 6.0.13
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- @fe-free/core@6.0.13
|
|
18
|
+
- @fe-free/icons@6.0.13
|
|
19
|
+
- @fe-free/tool@6.0.13
|
|
20
|
+
|
|
3
21
|
## 6.0.12
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/ai",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.14",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"lodash-es": "^4.17.21",
|
|
18
18
|
"uuid": "^13.0.0",
|
|
19
19
|
"zustand": "^4.5.7",
|
|
20
|
-
"@fe-free/core": "6.0.
|
|
20
|
+
"@fe-free/core": "6.0.14"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"antd": "^6.2.1",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"i18next-icu": "^2.4.1",
|
|
28
28
|
"react": "^19.2.0",
|
|
29
29
|
"react-i18next": "^16.4.0",
|
|
30
|
-
"@fe-free/icons": "6.0.
|
|
31
|
-
"@fe-free/tool": "6.0.
|
|
30
|
+
"@fe-free/icons": "6.0.14",
|
|
31
|
+
"@fe-free/tool": "6.0.14"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"test": "echo \"Error: no test specified\" && exit 1",
|
package/src/ai.stories.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Message, MSenderProps } from '@fe-free/ai';
|
|
2
2
|
import {
|
|
3
3
|
Chat,
|
|
4
4
|
createChatStore,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
EnumMessageStatus,
|
|
6
|
+
EnumMessageType,
|
|
7
7
|
generateUUID,
|
|
8
8
|
getRecordAudioOfPCM,
|
|
9
9
|
Markdown,
|
|
@@ -15,18 +15,16 @@ import { sleep } from '@fe-free/tool';
|
|
|
15
15
|
import type { Meta } from '@storybook/react-vite';
|
|
16
16
|
import { App, Button, Divider } from 'antd';
|
|
17
17
|
import { set } from 'lodash-es';
|
|
18
|
-
import { useCallback, useEffect, useMemo } from 'react';
|
|
18
|
+
import { useCallback, useEffect, useEffectEvent, useMemo } from 'react';
|
|
19
19
|
|
|
20
20
|
const meta: Meta = {
|
|
21
21
|
title: '@fe-free/ai/Example',
|
|
22
22
|
tags: ['autodocs'],
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
25
|
+
async function fakeFetchStream(value: UserData, { onUpdate }) {
|
|
26
|
+
console.log('fakeFetchStream', value);
|
|
28
27
|
|
|
29
|
-
async function fakeFetchStream(v: MSenderProps['value'], { onUpdate }) {
|
|
30
28
|
// 这里模拟 fetchStream
|
|
31
29
|
await sleep(1000);
|
|
32
30
|
|
|
@@ -42,11 +40,30 @@ async function fakeFetchStream(v: MSenderProps['value'], { onUpdate }) {
|
|
|
42
40
|
}, 300);
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
/**
|
|
44
|
+
|
|
45
|
+
如何接入(具体见下面的代码)
|
|
46
|
+
- store.ts
|
|
47
|
+
- types.ts 定义类型
|
|
48
|
+
- 使用 createChatStore 创建 store
|
|
49
|
+
- chat.ts
|
|
50
|
+
- 接入 Chat 组件
|
|
51
|
+
- 接入 Messages 组件,实现 renderMessageOfXXX
|
|
52
|
+
- 接入 Sender or MSender 组件
|
|
53
|
+
- 数据相关从 useChatStore 中获取
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
type UserData = NonNullable<MSenderProps['value']>;
|
|
57
|
+
interface AIData {
|
|
58
|
+
text?: string;
|
|
59
|
+
}
|
|
60
|
+
interface SystemData {
|
|
61
|
+
type?: string;
|
|
62
|
+
}
|
|
49
63
|
|
|
64
|
+
const { useChatStore, useChatStoreComputed } = createChatStore<UserData, AIData, SystemData>();
|
|
65
|
+
|
|
66
|
+
function Component() {
|
|
50
67
|
const senderValue = useChatStore((state) => state.senderValue);
|
|
51
68
|
const setSenderValue = useChatStore((state) => state.setSenderValue);
|
|
52
69
|
const messages = useChatStore((state) => state.messages);
|
|
@@ -54,51 +71,54 @@ function Component() {
|
|
|
54
71
|
const addMessage = useChatStore((state) => state.addMessage);
|
|
55
72
|
const updateMessage = useChatStore((state) => state.updateMessage);
|
|
56
73
|
const { chatStatus } = useChatStoreComputed();
|
|
74
|
+
const loading =
|
|
75
|
+
chatStatus === EnumMessageStatus.PENDING || chatStatus === EnumMessageStatus.STREAMING;
|
|
57
76
|
|
|
58
|
-
const { message } = App.useApp();
|
|
77
|
+
const { message: appMessage } = App.useApp();
|
|
59
78
|
|
|
60
|
-
|
|
61
|
-
useEffect(() => {
|
|
79
|
+
const handleLoaded = useEffectEvent(() => {
|
|
62
80
|
const cacheMessages = localStorage.getItem('chatMessages');
|
|
63
81
|
if (cacheMessages) {
|
|
64
82
|
setMessages(JSON.parse(cacheMessages));
|
|
65
83
|
}
|
|
66
|
-
}
|
|
84
|
+
});
|
|
67
85
|
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
// init from cache
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
handleLoaded();
|
|
89
|
+
}, []);
|
|
70
90
|
|
|
71
91
|
const handleSubmit = useCallback(
|
|
72
92
|
(v) => {
|
|
73
93
|
console.log('onSubmit', v);
|
|
74
94
|
|
|
75
|
-
const
|
|
95
|
+
const chatMessage: Message<UserData, AIData, SystemData> = {
|
|
76
96
|
uuid: generateUUID(),
|
|
77
|
-
status:
|
|
97
|
+
status: EnumMessageStatus.PENDING,
|
|
78
98
|
user: {
|
|
79
99
|
...v,
|
|
80
100
|
},
|
|
81
101
|
};
|
|
82
102
|
|
|
83
|
-
addMessage(
|
|
103
|
+
addMessage(chatMessage);
|
|
84
104
|
|
|
85
105
|
// fake
|
|
86
|
-
fakeFetchStream(v, {
|
|
106
|
+
void fakeFetchStream(v, {
|
|
87
107
|
onUpdate: ({ event, data }) => {
|
|
88
108
|
if (event === 'message') {
|
|
89
|
-
|
|
109
|
+
chatMessage.status = EnumMessageStatus.STREAMING;
|
|
90
110
|
|
|
91
|
-
const preText =
|
|
92
|
-
set(
|
|
111
|
+
const preText = chatMessage.ai?.data?.text || '';
|
|
112
|
+
set(chatMessage, 'ai.data.text', preText + data);
|
|
93
113
|
|
|
94
114
|
// 假设有 session_id
|
|
95
|
-
set(
|
|
115
|
+
set(chatMessage, 'ai.session_id', '123');
|
|
96
116
|
|
|
97
|
-
updateMessage(
|
|
117
|
+
updateMessage(chatMessage);
|
|
98
118
|
}
|
|
99
119
|
if (event === 'done') {
|
|
100
|
-
|
|
101
|
-
updateMessage(
|
|
120
|
+
chatMessage.status = EnumMessageStatus.DONE;
|
|
121
|
+
updateMessage(chatMessage);
|
|
102
122
|
}
|
|
103
123
|
},
|
|
104
124
|
});
|
|
@@ -117,7 +137,7 @@ function Component() {
|
|
|
117
137
|
onClick={() => {
|
|
118
138
|
addMessage({
|
|
119
139
|
uuid: generateUUID(),
|
|
120
|
-
type:
|
|
140
|
+
type: EnumMessageType.SYSTEM,
|
|
121
141
|
system: {
|
|
122
142
|
data: {
|
|
123
143
|
type: 'new_session',
|
|
@@ -156,7 +176,7 @@ function Component() {
|
|
|
156
176
|
console.log('onAudio', data);
|
|
157
177
|
},
|
|
158
178
|
onError: (err) => {
|
|
159
|
-
|
|
179
|
+
appMessage.error(err.message);
|
|
160
180
|
},
|
|
161
181
|
});
|
|
162
182
|
} catch (err) {
|
|
@@ -175,29 +195,29 @@ function Component() {
|
|
|
175
195
|
>
|
|
176
196
|
<Messages
|
|
177
197
|
messages={messages}
|
|
178
|
-
renderMessageOfSystem={({ message }) => {
|
|
179
|
-
if (
|
|
198
|
+
renderMessageOfSystem={({ message: msg }) => {
|
|
199
|
+
if (msg.system?.data?.type === 'new_session') {
|
|
180
200
|
return <Divider>让我们聊点新内容吧</Divider>;
|
|
181
201
|
}
|
|
182
202
|
|
|
183
203
|
return null;
|
|
184
204
|
}}
|
|
185
|
-
renderMessageOfUser={({ message }) => {
|
|
205
|
+
renderMessageOfUser={({ message: msg }) => {
|
|
186
206
|
return (
|
|
187
207
|
<div className="p-2">
|
|
188
|
-
<div className="rounded-xl
|
|
208
|
+
<div className="bg-primary rounded-xl p-2 text-white">{msg.user?.text}</div>
|
|
189
209
|
</div>
|
|
190
210
|
);
|
|
191
211
|
}}
|
|
192
|
-
renderMessageOfAI={({ message }) => {
|
|
212
|
+
renderMessageOfAI={({ message: msg }) => {
|
|
193
213
|
return (
|
|
194
214
|
<div className="max-w-full p-2">
|
|
195
215
|
<div>
|
|
196
|
-
status: {
|
|
216
|
+
status: {msg.status} session_id: {msg.ai?.session_id}
|
|
197
217
|
</div>
|
|
198
|
-
<Markdown content={
|
|
218
|
+
<Markdown content={msg.ai?.data?.text || ''} />
|
|
199
219
|
<div className="flex gap-2">
|
|
200
|
-
<MessageActions.Copy value={
|
|
220
|
+
<MessageActions.Copy value={msg.ai?.data?.text || ''} />
|
|
201
221
|
<MessageActions.Like
|
|
202
222
|
onClick={async () => {
|
|
203
223
|
// some thing
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-ignore
|
|
1
2
|
import './style.scss';
|
|
2
3
|
|
|
3
4
|
export { CustomMarkdown, Markdown } from '@fe-free/core';
|
|
@@ -8,13 +9,13 @@ export { generateUUID } from './helper';
|
|
|
8
9
|
export { completeHtml, completeJson } from './helper/complete';
|
|
9
10
|
export { MSender } from './m_sender';
|
|
10
11
|
export type { MSenderProps, MSenderRef } from './m_sender';
|
|
11
|
-
export { MessageActions, MessageThink, MessageThinkOfDeepSeek
|
|
12
|
-
export type {
|
|
12
|
+
export { MessageActions, Messages, MessageThink, MessageThinkOfDeepSeek } from './messages';
|
|
13
|
+
export type { MessagesProps, MessageThinkProps } from './messages';
|
|
13
14
|
export { Sender } from './sender';
|
|
14
15
|
export type { SenderProps, SenderRef } from './sender';
|
|
15
16
|
export { createChatStore } from './store';
|
|
16
|
-
export {
|
|
17
|
-
export type {
|
|
17
|
+
export { EnumMessageStatus, EnumMessageType } from './store/types';
|
|
18
|
+
export type { Message, MessageOfAI } from './store/types';
|
|
18
19
|
export { fetchStream } from './stream';
|
|
19
20
|
export { Tip } from './tip';
|
|
20
21
|
export { getRecordAudioOfBlob, getRecordAudioOfPCM } from './voice';
|
|
@@ -3,27 +3,35 @@ import { ArrowDownOutlined } from '@fe-free/icons';
|
|
|
3
3
|
import { useMemoizedFn } from 'ahooks';
|
|
4
4
|
import { Button } from 'antd';
|
|
5
5
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
-
import { EnumChatMessageType, type ChatMessage } from '../store/types';
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
import { EnumMessageType, type Message } from '../store/types';
|
|
8
|
+
|
|
9
|
+
interface MessagesProps<UserData, AIData, SystemData> {
|
|
9
10
|
refList?: React.RefObject<HTMLDivElement | null>;
|
|
10
|
-
messages?:
|
|
11
|
+
messages?: Message<UserData, AIData, SystemData>[];
|
|
11
12
|
/** 含所有 */
|
|
12
|
-
renderMessage?: (props: { message:
|
|
13
|
+
renderMessage?: (props: { message: Message<UserData, AIData, SystemData> }) => React.ReactNode;
|
|
13
14
|
/** 系统消息 */
|
|
14
|
-
renderMessageOfSystem?: (props: {
|
|
15
|
+
renderMessageOfSystem?: (props: {
|
|
16
|
+
message: Message<UserData, AIData, SystemData>;
|
|
17
|
+
}) => React.ReactNode;
|
|
15
18
|
/** 用户消息 */
|
|
16
|
-
renderMessageOfUser?: (props: {
|
|
19
|
+
renderMessageOfUser?: (props: {
|
|
20
|
+
message: Message<UserData, AIData, SystemData>;
|
|
21
|
+
}) => React.ReactNode;
|
|
17
22
|
/** AI消息 */
|
|
18
|
-
renderMessageOfAI?: (props: {
|
|
23
|
+
renderMessageOfAI?: (props: {
|
|
24
|
+
message: Message<UserData, AIData, SystemData>;
|
|
25
|
+
}) => React.ReactNode;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
function useScrollToBottom({ ref }) {
|
|
22
29
|
const [showScrollBottom, setShowScrollBottom] = useState(false);
|
|
23
30
|
|
|
24
31
|
useEffect(() => {
|
|
32
|
+
const el = ref.current;
|
|
25
33
|
const handleScroll = () => {
|
|
26
|
-
if (!
|
|
34
|
+
if (!el) {
|
|
27
35
|
return;
|
|
28
36
|
}
|
|
29
37
|
|
|
@@ -42,14 +50,18 @@ function useScrollToBottom({ ref }) {
|
|
|
42
50
|
handleScroll();
|
|
43
51
|
|
|
44
52
|
return () => {
|
|
45
|
-
|
|
53
|
+
if (el) {
|
|
54
|
+
el.removeEventListener('scroll', handleScroll);
|
|
55
|
+
}
|
|
46
56
|
};
|
|
47
57
|
}, [ref]);
|
|
48
58
|
|
|
49
59
|
return showScrollBottom;
|
|
50
60
|
}
|
|
51
61
|
|
|
52
|
-
function Messages<UserData, AIData
|
|
62
|
+
function Messages<UserData, AIData, SystemData>(
|
|
63
|
+
props: MessagesProps<UserData, AIData, SystemData>,
|
|
64
|
+
) {
|
|
53
65
|
const {
|
|
54
66
|
refList,
|
|
55
67
|
messages,
|
|
@@ -129,15 +141,15 @@ function Messages<UserData, AIData>(props: MessagesProps<UserData, AIData>) {
|
|
|
129
141
|
renderMessage?.({ message })
|
|
130
142
|
) : (
|
|
131
143
|
<>
|
|
132
|
-
{message.type ===
|
|
144
|
+
{message.type === EnumMessageType.SYSTEM && message.system && (
|
|
133
145
|
<div className="flex justify-center">
|
|
134
146
|
{renderMessageOfSystem?.({ message })}
|
|
135
147
|
</div>
|
|
136
148
|
)}
|
|
137
|
-
{message.type !==
|
|
149
|
+
{message.type !== EnumMessageType.SYSTEM && message.user && (
|
|
138
150
|
<div className="flex justify-end">{renderMessageOfUser?.({ message })}</div>
|
|
139
151
|
)}
|
|
140
|
-
{message.type !==
|
|
152
|
+
{message.type !== EnumMessageType.SYSTEM && message.ai && (
|
|
141
153
|
<div className="flex justify-start">{renderMessageOfAI?.({ message })}</div>
|
|
142
154
|
)}
|
|
143
155
|
</>
|
package/src/store/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { create } from 'zustand';
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import type { Message } from './types';
|
|
4
5
|
|
|
5
6
|
interface BaseSenderValue {
|
|
6
7
|
text?: string;
|
|
@@ -9,17 +10,18 @@ interface BaseSenderValue {
|
|
|
9
10
|
interface ChatStore<
|
|
10
11
|
UserData extends BaseSenderValue,
|
|
11
12
|
AIData,
|
|
13
|
+
SystemData,
|
|
12
14
|
ContextData extends Record<string, any>,
|
|
13
15
|
> {
|
|
14
16
|
senderValue?: UserData;
|
|
15
17
|
setSenderValue: (senderValue?: UserData) => void;
|
|
16
18
|
|
|
17
|
-
messages:
|
|
18
|
-
setMessages: (messages:
|
|
19
|
-
addMessage: (message:
|
|
20
|
-
updateMessage: (message:
|
|
21
|
-
setMessagesBefore: (messages:
|
|
22
|
-
setMessagesAfter: (messages:
|
|
19
|
+
messages: Message<UserData, AIData, SystemData>[];
|
|
20
|
+
setMessages: (messages: Message<UserData, AIData, SystemData>[]) => void;
|
|
21
|
+
addMessage: (message: Message<UserData, AIData, SystemData>) => void;
|
|
22
|
+
updateMessage: (message: Message<UserData, AIData, SystemData>) => void;
|
|
23
|
+
setMessagesBefore: (messages: Message<UserData, AIData, SystemData>[]) => void;
|
|
24
|
+
setMessagesAfter: (messages: Message<UserData, AIData, SystemData>[]) => void;
|
|
23
25
|
|
|
24
26
|
/** 存放Chat的上下文数据 */
|
|
25
27
|
contextData?: ContextData;
|
|
@@ -36,114 +38,117 @@ interface ChatStore<
|
|
|
36
38
|
function createChatStore<
|
|
37
39
|
UserData extends BaseSenderValue,
|
|
38
40
|
AIData,
|
|
41
|
+
SystemData,
|
|
39
42
|
ContextData extends Record<string, any> = any,
|
|
40
43
|
>() {
|
|
41
|
-
const useChatStore = create<ChatStore<UserData, AIData, ContextData>>(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
updatedAt: Date.now(),
|
|
54
|
-
...message,
|
|
55
|
-
})),
|
|
56
|
-
}));
|
|
57
|
-
},
|
|
58
|
-
addMessage: (message) => {
|
|
59
|
-
set((state) => ({
|
|
60
|
-
messages: [
|
|
61
|
-
...state.messages,
|
|
62
|
-
{
|
|
63
|
-
...message,
|
|
64
|
-
// 覆盖
|
|
44
|
+
const useChatStore = create<ChatStore<UserData, AIData, SystemData, ContextData>>(
|
|
45
|
+
(set, get, store) => ({
|
|
46
|
+
senderValue: undefined,
|
|
47
|
+
setSenderValue: (senderValue) => {
|
|
48
|
+
set(() => ({ senderValue }));
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
messages: [],
|
|
52
|
+
setMessages: (messages) => {
|
|
53
|
+
set(() => ({
|
|
54
|
+
messages: messages.map((message) => ({
|
|
55
|
+
// 如果没有,则用当前的时间
|
|
65
56
|
createdAt: Date.now(),
|
|
66
57
|
updatedAt: Date.now(),
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
58
|
+
...message,
|
|
59
|
+
})),
|
|
60
|
+
}));
|
|
61
|
+
},
|
|
62
|
+
addMessage: (message) => {
|
|
63
|
+
set((state) => ({
|
|
64
|
+
messages: [
|
|
65
|
+
...state.messages,
|
|
66
|
+
{
|
|
76
67
|
...message,
|
|
77
68
|
// 覆盖
|
|
69
|
+
createdAt: Date.now(),
|
|
78
70
|
updatedAt: Date.now(),
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
}));
|
|
74
|
+
},
|
|
75
|
+
updateMessage: (message) => {
|
|
76
|
+
set((state) => ({
|
|
77
|
+
messages: state.messages.map((m) => {
|
|
78
|
+
if (m.uuid === message.uuid) {
|
|
79
|
+
return {
|
|
80
|
+
...message,
|
|
81
|
+
// 覆盖
|
|
82
|
+
updatedAt: Date.now(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return m;
|
|
86
|
+
}),
|
|
87
|
+
}));
|
|
88
|
+
},
|
|
89
|
+
setMessagesBefore: (messagesBefore) => {
|
|
90
|
+
if (messagesBefore.length === 0) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const messages = get().messages;
|
|
95
|
+
|
|
96
|
+
const lastMessageBefore = messagesBefore[messagesBefore.length - 1];
|
|
97
|
+
const index = messages.findIndex((message) => message.uuid === lastMessageBefore.uuid);
|
|
98
|
+
|
|
99
|
+
// 如果 index 非 -1,则合并
|
|
100
|
+
// 如果 index -1,-1 + 1 为 0,即全取 message,也适用
|
|
101
|
+
const newMessages = [...messagesBefore, ...messages.slice(index + 1)];
|
|
102
|
+
|
|
103
|
+
set({
|
|
104
|
+
messages: newMessages,
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
setMessagesAfter: (messagesAfter) => {
|
|
108
|
+
if (messagesAfter.length === 0) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const messages = get().messages;
|
|
113
|
+
|
|
114
|
+
const firstMessageAfter = messagesAfter[0];
|
|
115
|
+
const index = messages.findIndex((message) => message.uuid === firstMessageAfter.uuid);
|
|
116
|
+
|
|
117
|
+
// 如果 index 非 -1,则合并
|
|
118
|
+
// 如果 index -1,-1 + 1 为 0,即全取 message,也适用
|
|
119
|
+
const newMessages = [...messages.slice(0, index), ...messagesAfter];
|
|
120
|
+
|
|
121
|
+
set({
|
|
122
|
+
messages: newMessages,
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
contextData: undefined,
|
|
126
|
+
setContextData: (contextData) => {
|
|
127
|
+
set({ contextData });
|
|
128
|
+
},
|
|
129
|
+
setContextDataWithField: (field, contextDataValue) => {
|
|
130
|
+
const preContextData = get().contextData;
|
|
131
|
+
set({
|
|
132
|
+
contextData: {
|
|
133
|
+
...preContextData,
|
|
134
|
+
[field]: contextDataValue,
|
|
135
|
+
} as ContextData,
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
setContextDataPartial: (contextDataPartial) => {
|
|
139
|
+
const preContextData = get().contextData;
|
|
140
|
+
set({
|
|
141
|
+
contextData: {
|
|
142
|
+
...preContextData,
|
|
143
|
+
...contextDataPartial,
|
|
144
|
+
} as ContextData,
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
reset: () => {
|
|
148
|
+
set(store.getInitialState());
|
|
149
|
+
},
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
147
152
|
|
|
148
153
|
const useChatStoreComputed = () => {
|
|
149
154
|
const messages = useChatStore((state) => state.messages);
|
package/src/store/types.ts
CHANGED
|
@@ -1,39 +1,34 @@
|
|
|
1
|
-
enum
|
|
1
|
+
enum EnumMessageType {
|
|
2
2
|
SYSTEM = 'system',
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
enum
|
|
5
|
+
enum EnumMessageStatus {
|
|
6
6
|
PENDING = 'pending',
|
|
7
7
|
STREAMING = 'streaming',
|
|
8
8
|
DONE = 'done',
|
|
9
9
|
ERROR = 'error',
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
interface
|
|
13
|
-
data?:
|
|
12
|
+
interface MessageOfSystem<SystemData> {
|
|
13
|
+
data?: SystemData;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
interface
|
|
17
|
-
text?: string;
|
|
18
|
-
files?: string[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ChatMessageOfAI<AIData> {
|
|
16
|
+
interface MessageOfAI<AIData> {
|
|
22
17
|
data?: AIData;
|
|
23
18
|
error?: any;
|
|
24
19
|
// 其他字段,根据业务需要使用
|
|
25
20
|
session_id?: string;
|
|
26
21
|
}
|
|
27
22
|
|
|
28
|
-
interface
|
|
23
|
+
interface Message<UserData, AIData, SystemData> {
|
|
29
24
|
uuid: string;
|
|
30
25
|
|
|
31
|
-
status?:
|
|
26
|
+
status?: EnumMessageStatus;
|
|
32
27
|
|
|
33
|
-
type?:
|
|
34
|
-
system?:
|
|
28
|
+
type?: EnumMessageType;
|
|
29
|
+
system?: MessageOfSystem<SystemData>;
|
|
35
30
|
user?: UserData;
|
|
36
|
-
ai?:
|
|
31
|
+
ai?: MessageOfAI<AIData>;
|
|
37
32
|
|
|
38
33
|
/** 自动生成 */
|
|
39
34
|
createdAt?: number;
|
|
@@ -41,6 +36,6 @@ interface ChatMessage<UserData, AIData> {
|
|
|
41
36
|
updatedAt?: number;
|
|
42
37
|
}
|
|
43
38
|
|
|
44
|
-
export {
|
|
39
|
+
export { EnumMessageStatus, EnumMessageType };
|
|
45
40
|
|
|
46
|
-
export type {
|
|
41
|
+
export type { Message, MessageOfAI, MessageOfSystem };
|