@agentscope-ai/chat 1.1.69 → 1.1.71-beta.1781610744021
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/components/AgentScopeRuntimeWebUI/core/Chat/Input/index.tsx +38 -5
- package/components/AgentScopeRuntimeWebUI/core/Chat/InputQueue/Panel.tsx +82 -0
- package/components/AgentScopeRuntimeWebUI/core/Chat/InputQueue/__tests__/inputQueue.test.ts +112 -0
- package/components/AgentScopeRuntimeWebUI/core/Chat/InputQueue/index.ts +122 -0
- package/components/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.tsx +111 -4
- package/components/AgentScopeRuntimeWebUI/core/Chat/index.tsx +21 -3
- package/components/AgentScopeRuntimeWebUI/core/Chat/styles.tsx +68 -1
- package/components/AgentScopeRuntimeWebUI/core/ChatAnywhere/index.tsx +1 -1
- package/components/AgentScopeRuntimeWebUI/core/Context/ChatAnywhereI18nContext.tsx +14 -0
- package/components/AgentScopeRuntimeWebUI/starter/index.tsx +100 -14
- package/components/AgentScopeRuntimeWebUI/starterForMe/index.tsx +31 -0
- package/components/Attachments/index.tsx +30 -2
- package/components/ChatAnywhere/Input/index.tsx +5 -0
- package/components/ChatAnywhere/hooks/ChatAnywhereProvider.tsx +1 -0
- package/components/ChatAnywhere/hooks/types.ts +5 -0
- package/lib/AgentScopeRuntimeWebUI/core/Chat/Input/index.d.ts +8 -0
- package/lib/AgentScopeRuntimeWebUI/core/Chat/Input/index.js +36 -8
- package/lib/AgentScopeRuntimeWebUI/core/Chat/InputQueue/Panel.d.ts +9 -0
- package/lib/AgentScopeRuntimeWebUI/core/Chat/InputQueue/Panel.js +78 -0
- package/lib/AgentScopeRuntimeWebUI/core/Chat/InputQueue/index.d.ts +37 -0
- package/lib/AgentScopeRuntimeWebUI/core/Chat/InputQueue/index.js +74 -0
- package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.d.ts +7 -0
- package/lib/AgentScopeRuntimeWebUI/core/Chat/hooks/useChatController.js +204 -63
- package/lib/AgentScopeRuntimeWebUI/core/Chat/index.js +14 -2
- package/lib/AgentScopeRuntimeWebUI/core/Chat/styles.js +31 -1
- package/lib/AgentScopeRuntimeWebUI/core/Context/ChatAnywhereI18nContext.d.ts +11 -1
- package/lib/AgentScopeRuntimeWebUI/core/Context/ChatAnywhereI18nContext.js +12 -0
- package/lib/AgentScopeRuntimeWebUI/starter/index.js +144 -20
- package/lib/AgentScopeRuntimeWebUI/starterForMe/index.d.ts +1 -0
- package/lib/AgentScopeRuntimeWebUI/starterForMe/index.js +34 -0
- package/lib/Attachments/index.js +40 -2
- package/lib/ChatAnywhere/Input/index.js +8 -0
- package/lib/ChatAnywhere/hooks/ChatAnywhereProvider.d.ts +1 -0
- package/lib/ChatAnywhere/hooks/types.d.ts +5 -0
- package/package.json +2 -1
|
@@ -35,6 +35,13 @@ const messages = {
|
|
|
35
35
|
'common.saveSuccess': '保存成功',
|
|
36
36
|
'common.saveFailed': '保存失败',
|
|
37
37
|
|
|
38
|
+
// Queue 相关
|
|
39
|
+
'queue.title': '待发送队列',
|
|
40
|
+
'queue.clear': '清空队列',
|
|
41
|
+
'queue.retry': '重试发送',
|
|
42
|
+
'queue.failed': '发送失败',
|
|
43
|
+
'queue.attachmentOnly': '附件消息',
|
|
44
|
+
|
|
38
45
|
// Actions 相关
|
|
39
46
|
'actions.regenerate': '重新生成',
|
|
40
47
|
|
|
@@ -73,6 +80,13 @@ const messages = {
|
|
|
73
80
|
'common.saveSuccess': 'Saved successfully',
|
|
74
81
|
'common.saveFailed': 'Failed to save',
|
|
75
82
|
|
|
83
|
+
// Queue related
|
|
84
|
+
'queue.title': 'Queued inputs',
|
|
85
|
+
'queue.clear': 'Clear queue',
|
|
86
|
+
'queue.retry': 'Retry',
|
|
87
|
+
'queue.failed': 'Failed to send',
|
|
88
|
+
'queue.attachmentOnly': 'Attachment message',
|
|
89
|
+
|
|
76
90
|
// Actions related
|
|
77
91
|
'actions.regenerate': 'Regenerate',
|
|
78
92
|
|
|
@@ -1,11 +1,97 @@
|
|
|
1
|
-
import { AgentScopeRuntimeWebUI, IAgentScopeRuntimeWebUIRef
|
|
1
|
+
import { AgentScopeRuntimeWebUI, IAgentScopeRuntimeWebUIRef } from '@agentscope-ai/chat';
|
|
2
2
|
import OptionsPanel from './OptionsPanel';
|
|
3
3
|
import { useMemo, useRef } from 'react';
|
|
4
4
|
import defaultConfig from './OptionsPanel/defaultConfig';
|
|
5
5
|
import { useLocalStorageState } from 'ahooks';
|
|
6
|
-
import { Flex } from 'antd';
|
|
6
|
+
import { Button, Flex } from 'antd';
|
|
7
7
|
import MessageImport from './MessageImport';
|
|
8
8
|
|
|
9
|
+
const encoder = new TextEncoder();
|
|
10
|
+
|
|
11
|
+
const sleep = (ms: number) => new Promise(resolve => {
|
|
12
|
+
setTimeout(resolve, ms);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function getLastUserText(input: any[] = []) {
|
|
16
|
+
const lastMessage = input[input.length - 1];
|
|
17
|
+
const content = lastMessage?.content || [];
|
|
18
|
+
return content
|
|
19
|
+
.filter((item: any) => item?.type === 'text')
|
|
20
|
+
.map((item: any) => item.text)
|
|
21
|
+
.join('\n');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function createMockQueueResponse(data: {
|
|
25
|
+
input?: any[];
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
}) {
|
|
28
|
+
const userText = getLastUserText(data.input);
|
|
29
|
+
const messageId = `queue-demo-${Date.now()}`;
|
|
30
|
+
const chunks = [
|
|
31
|
+
{
|
|
32
|
+
object: 'message',
|
|
33
|
+
id: messageId,
|
|
34
|
+
role: 'assistant',
|
|
35
|
+
type: 'message',
|
|
36
|
+
status: 'in_progress',
|
|
37
|
+
content: [],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
object: 'content',
|
|
41
|
+
msg_id: messageId,
|
|
42
|
+
type: 'text',
|
|
43
|
+
status: 'in_progress',
|
|
44
|
+
delta: true,
|
|
45
|
+
text: `Received: ${userText || 'attachment message'}\n\n`,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
object: 'content',
|
|
49
|
+
msg_id: messageId,
|
|
50
|
+
type: 'text',
|
|
51
|
+
status: 'in_progress',
|
|
52
|
+
delta: true,
|
|
53
|
+
text: 'This mock response is intentionally slow, ',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
object: 'content',
|
|
57
|
+
msg_id: messageId,
|
|
58
|
+
type: 'text',
|
|
59
|
+
status: 'in_progress',
|
|
60
|
+
delta: true,
|
|
61
|
+
text: 'so later inputs can enter the queue.',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
object: 'response',
|
|
65
|
+
id: messageId,
|
|
66
|
+
status: 'completed',
|
|
67
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
68
|
+
output: [],
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const stream = new ReadableStream({
|
|
73
|
+
async start(controller) {
|
|
74
|
+
for (const chunk of chunks) {
|
|
75
|
+
if (data.signal?.aborted) {
|
|
76
|
+
controller.close();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await sleep(700);
|
|
80
|
+
controller.enqueue(
|
|
81
|
+
encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
controller.close();
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return new Response(stream, {
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'text/event-stream',
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
9
95
|
|
|
10
96
|
export default function () {
|
|
11
97
|
|
|
@@ -20,6 +106,16 @@ export default function () {
|
|
|
20
106
|
|
|
21
107
|
const options = useMemo(() => {
|
|
22
108
|
const rightHeader = <Flex gap={16}>
|
|
109
|
+
<Button size="small" onClick={() => {
|
|
110
|
+
chatRef.current?.input.submit({ query: 'Queue demo 1: slow response' });
|
|
111
|
+
window.setTimeout(() => {
|
|
112
|
+
chatRef.current?.input.submit({ query: 'Queue demo 2: queued while busy' });
|
|
113
|
+
}, 400);
|
|
114
|
+
window.setTimeout(() => {
|
|
115
|
+
chatRef.current?.input.submit({ query: 'Queue demo 3: sent after demo 2' });
|
|
116
|
+
}, 800);
|
|
117
|
+
}}>Queue demo</Button>
|
|
118
|
+
|
|
23
119
|
<OptionsPanel value={optionsConfig} onChange={v => {
|
|
24
120
|
setOptionsConfig(prev => ({
|
|
25
121
|
...prev,
|
|
@@ -40,17 +136,6 @@ export default function () {
|
|
|
40
136
|
},
|
|
41
137
|
sender: {
|
|
42
138
|
...optionsConfig.sender,
|
|
43
|
-
beforeUI: <ChatInput.BeforeUIContainer>
|
|
44
|
-
<Flex gap={6}>
|
|
45
|
-
{
|
|
46
|
-
optionsConfig.welcome.prompts.map(prompt => (
|
|
47
|
-
<a key={prompt.value} onClick={() => {
|
|
48
|
-
chatRef.current?.input.submit({ query: prompt.value });
|
|
49
|
-
}}>{prompt.value}</a>
|
|
50
|
-
))
|
|
51
|
-
}
|
|
52
|
-
</Flex>
|
|
53
|
-
</ChatInput.BeforeUIContainer>,
|
|
54
139
|
attachments: optionsConfig.sender.attachments ? {
|
|
55
140
|
customRequest(options) {
|
|
56
141
|
// 模拟上传进度
|
|
@@ -71,6 +156,7 @@ export default function () {
|
|
|
71
156
|
},
|
|
72
157
|
api: {
|
|
73
158
|
...optionsConfig.api,
|
|
159
|
+
...(!optionsConfig.api.baseURL ? { fetch: createMockQueueResponse } : {}),
|
|
74
160
|
cancel: (data) => {
|
|
75
161
|
console.log('cancel', data);
|
|
76
162
|
},
|
|
@@ -87,4 +173,4 @@ export default function () {
|
|
|
87
173
|
options={options}
|
|
88
174
|
/>
|
|
89
175
|
</div>;
|
|
90
|
-
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AgentScopeRuntimeWebUI } from '@agentscope-ai/chat';
|
|
2
|
+
|
|
3
|
+
export default function Page() {
|
|
4
|
+
return (
|
|
5
|
+
<div style={{ height: '100dvh' }}>
|
|
6
|
+
<AgentScopeRuntimeWebUI
|
|
7
|
+
options={{
|
|
8
|
+
api: {
|
|
9
|
+
baseURL: '/api/runtime/chat',
|
|
10
|
+
token: 'your-token',
|
|
11
|
+
},
|
|
12
|
+
session: {
|
|
13
|
+
multiple: true,
|
|
14
|
+
},
|
|
15
|
+
theme: {
|
|
16
|
+
locale: 'en',
|
|
17
|
+
colorPrimary: '#615CED',
|
|
18
|
+
},
|
|
19
|
+
sender: {
|
|
20
|
+
placeholder: 'Ask something',
|
|
21
|
+
maxLength: 10000,
|
|
22
|
+
},
|
|
23
|
+
welcome: {
|
|
24
|
+
greeting: 'Hello, how can I help you today?',
|
|
25
|
+
prompts: [{ value: 'What can you do?' }],
|
|
26
|
+
},
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -178,14 +178,15 @@ function Attachments(props: AttachmentsProps, ref: React.Ref<AttachmentsRef>) {
|
|
|
178
178
|
};
|
|
179
179
|
|
|
180
180
|
const onItemReplace = useEvent((oldItem: Attachment, file: File) => {
|
|
181
|
+
const { customRequest } = uploadProps;
|
|
181
182
|
const newAttachment: Attachment = {
|
|
182
183
|
uid: oldItem.uid,
|
|
183
184
|
name: file.name,
|
|
184
185
|
size: file.size,
|
|
185
186
|
type: file.type,
|
|
186
187
|
originFileObj: file as any,
|
|
187
|
-
status: 'done',
|
|
188
|
-
percent: 100,
|
|
188
|
+
status: customRequest ? 'uploading' : 'done',
|
|
189
|
+
percent: customRequest ? 0 : 100,
|
|
189
190
|
};
|
|
190
191
|
const newFileList = fileList.map((fileItem) =>
|
|
191
192
|
fileItem.uid === oldItem.uid ? newAttachment : fileItem,
|
|
@@ -194,6 +195,33 @@ function Attachments(props: AttachmentsProps, ref: React.Ref<AttachmentsRef>) {
|
|
|
194
195
|
file: newAttachment,
|
|
195
196
|
fileList: newFileList,
|
|
196
197
|
});
|
|
198
|
+
|
|
199
|
+
if (customRequest) {
|
|
200
|
+
const updateFile = (updates: Partial<Attachment>) => {
|
|
201
|
+
setFileList((prev) => {
|
|
202
|
+
const updated = prev.map((f) =>
|
|
203
|
+
f.uid === oldItem.uid ? { ...f, ...updates } : f,
|
|
204
|
+
);
|
|
205
|
+
onChange?.({ file: { ...newAttachment, ...updates }, fileList: updated });
|
|
206
|
+
return updated;
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
customRequest({
|
|
211
|
+
file: file as any,
|
|
212
|
+
onSuccess: (response: any) => {
|
|
213
|
+
updateFile({ status: 'done', percent: 100, response });
|
|
214
|
+
},
|
|
215
|
+
onError: (error: any) => {
|
|
216
|
+
updateFile({ status: 'error', error });
|
|
217
|
+
},
|
|
218
|
+
onProgress: (event: any) => {
|
|
219
|
+
updateFile({ percent: event?.percent });
|
|
220
|
+
},
|
|
221
|
+
} as any, {
|
|
222
|
+
defaultRequest: () => { }
|
|
223
|
+
});
|
|
224
|
+
}
|
|
197
225
|
});
|
|
198
226
|
|
|
199
227
|
let renderChildren: React.ReactElement;
|
|
@@ -69,6 +69,10 @@ export default forwardRef(function (_, ref) {
|
|
|
69
69
|
setContent(content);
|
|
70
70
|
setAttachedFiles(fileList || [[]]);
|
|
71
71
|
},
|
|
72
|
+
clearInput: () => {
|
|
73
|
+
setContent('');
|
|
74
|
+
setAttachedFiles(attachedFilesRef.current.map(() => []));
|
|
75
|
+
},
|
|
72
76
|
getAttachedFiles: () => attachedFilesRef.current,
|
|
73
77
|
|
|
74
78
|
};
|
|
@@ -166,6 +170,7 @@ export default forwardRef(function (_, ref) {
|
|
|
166
170
|
key={index}
|
|
167
171
|
items={files}
|
|
168
172
|
replaceable={true}
|
|
173
|
+
customRequest={onUpload[index]?.customRequest}
|
|
169
174
|
onChange={(info) => handleFileChange(index, info.fileList)}
|
|
170
175
|
/>
|
|
171
176
|
})
|
|
@@ -220,6 +220,7 @@ export type ChatAnywhereRef =
|
|
|
220
220
|
ReturnType<typeof useSessionList> &
|
|
221
221
|
{
|
|
222
222
|
setInputContent: (content: string, fileList?: UploadFile[][]) => void;
|
|
223
|
+
clearInput: () => void;
|
|
223
224
|
scrollToBottom: (options?: ScrollToBottomOptions) => void;
|
|
224
225
|
reload: () => void;
|
|
225
226
|
};
|
|
@@ -318,6 +318,11 @@ export interface IChatAnywhereRef extends IChatAnywhereContext {
|
|
|
318
318
|
* @descriptionEn Method to set input field content
|
|
319
319
|
*/
|
|
320
320
|
setInputContent: (content: string, fileList?: UploadFile[][]) => void;
|
|
321
|
+
/**
|
|
322
|
+
* @description 清空输入框内容和附件
|
|
323
|
+
* @descriptionEn Clear input content and attached files
|
|
324
|
+
*/
|
|
325
|
+
clearInput: () => void;
|
|
321
326
|
/**
|
|
322
327
|
* @description 滚动到底部的方法
|
|
323
328
|
* @descriptionEn Method to scroll to bottom
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { IAgentScopeRuntimeWebUIInputData } from "../../../..";
|
|
2
|
+
import type { QueuedInputItem } from "../InputQueue";
|
|
2
3
|
export interface InputProps {
|
|
3
4
|
onCancel: () => void;
|
|
4
5
|
onSubmit: (data: IAgentScopeRuntimeWebUIInputData) => void;
|
|
6
|
+
queue?: {
|
|
7
|
+
items: QueuedInputItem[];
|
|
8
|
+
onEnqueue: (data: IAgentScopeRuntimeWebUIInputData) => void;
|
|
9
|
+
onRemove: (id: string) => void;
|
|
10
|
+
onClear: () => void;
|
|
11
|
+
onRetry: (id: string) => void;
|
|
12
|
+
};
|
|
5
13
|
}
|
|
6
14
|
export default function Input(props: InputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -14,10 +14,12 @@ import { useChatAnywhereOptions } from "../../Context/ChatAnywhereOptionsContext
|
|
|
14
14
|
import { useGetState } from 'ahooks';
|
|
15
15
|
import { useChatAnywhereInput } from "../../Context/ChatAnywhereInputContext";
|
|
16
16
|
import useAttachments from "./useAttachments";
|
|
17
|
+
import InputQueuePanel from "../InputQueue/Panel";
|
|
18
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
17
19
|
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
20
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
20
21
|
export default function Input(props) {
|
|
22
|
+
var _props$queue4, _props$queue5;
|
|
21
23
|
var _useGetState = useGetState(''),
|
|
22
24
|
_useGetState2 = _slicedToArray(_useGetState, 3),
|
|
23
25
|
content = _useGetState2[0],
|
|
@@ -55,7 +57,8 @@ export default function Input(props) {
|
|
|
55
57
|
uploadIconButton = _useAttachments.uploadIconButton,
|
|
56
58
|
uploadFileListHeader = _useAttachments.uploadFileListHeader;
|
|
57
59
|
var handleSubmit = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
|
|
58
|
-
var
|
|
60
|
+
var _props$queue;
|
|
61
|
+
var next, fileList, data, _props$queue2;
|
|
59
62
|
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
60
63
|
while (1) switch (_context.prev = _context.next) {
|
|
61
64
|
case 0:
|
|
@@ -73,26 +76,51 @@ export default function Input(props) {
|
|
|
73
76
|
var _i$response;
|
|
74
77
|
return (_i$response = i.response) === null || _i$response === void 0 ? void 0 : _i$response.url;
|
|
75
78
|
});
|
|
76
|
-
|
|
79
|
+
data = {
|
|
77
80
|
query: getContent(),
|
|
78
81
|
fileList: fileList
|
|
79
|
-
}
|
|
82
|
+
};
|
|
83
|
+
if (inputContext.loading || (_props$queue = props.queue) !== null && _props$queue !== void 0 && _props$queue.items.length) {
|
|
84
|
+
(_props$queue2 = props.queue) === null || _props$queue2 === void 0 || _props$queue2.onEnqueue(data);
|
|
85
|
+
} else {
|
|
86
|
+
props.onSubmit(data);
|
|
87
|
+
}
|
|
80
88
|
setContent('');
|
|
81
|
-
setFileList
|
|
82
|
-
case
|
|
89
|
+
setFileList === null || setFileList === void 0 || setFileList([]);
|
|
90
|
+
case 10:
|
|
83
91
|
case "end":
|
|
84
92
|
return _context.stop();
|
|
85
93
|
}
|
|
86
94
|
}, _callee);
|
|
87
|
-
})), []);
|
|
95
|
+
})), [beforeSubmit, getContent, getFileList, inputContext.loading, props.onSubmit, props.queue, setContent, setFileList]);
|
|
96
|
+
var handleKeyDownCapture = useCallback(function (event) {
|
|
97
|
+
var _event$nativeEvent, _props$queue3;
|
|
98
|
+
if (event.key !== 'Enter' || event.shiftKey) return;
|
|
99
|
+
if ((_event$nativeEvent = event.nativeEvent) !== null && _event$nativeEvent !== void 0 && _event$nativeEvent.isComposing) return;
|
|
100
|
+
if (!inputContext.loading && !((_props$queue3 = props.queue) !== null && _props$queue3 !== void 0 && _props$queue3.items.length)) return;
|
|
101
|
+
var fileList = ((getFileList === null || getFileList === void 0 ? void 0 : getFileList()) || []).filter(function (i) {
|
|
102
|
+
var _i$response2;
|
|
103
|
+
return (_i$response2 = i.response) === null || _i$response2 === void 0 ? void 0 : _i$response2.url;
|
|
104
|
+
});
|
|
105
|
+
if (!getContent().trim() && fileList.length === 0) return;
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
event.stopPropagation();
|
|
108
|
+
void handleSubmit();
|
|
109
|
+
}, [getContent, getFileList, handleSubmit, inputContext.loading, (_props$queue4 = props.queue) === null || _props$queue4 === void 0 ? void 0 : _props$queue4.items.length]);
|
|
88
110
|
var handleCancel = useCallback(function () {
|
|
89
111
|
props.onCancel();
|
|
90
112
|
}, []);
|
|
91
113
|
return /*#__PURE__*/_jsxs("div", {
|
|
92
114
|
className: prefixCls,
|
|
115
|
+
onKeyDownCapture: handleKeyDownCapture,
|
|
93
116
|
children: [/*#__PURE__*/_jsxs("div", {
|
|
94
117
|
className: "".concat(prefixCls, "-wrapper"),
|
|
95
|
-
children: [beforeUI, /*#__PURE__*/_jsx(
|
|
118
|
+
children: [beforeUI, (_props$queue5 = props.queue) !== null && _props$queue5 !== void 0 && _props$queue5.items.length ? /*#__PURE__*/_jsx(InputQueuePanel, {
|
|
119
|
+
items: props.queue.items,
|
|
120
|
+
onRemove: props.queue.onRemove,
|
|
121
|
+
onClear: props.queue.onClear,
|
|
122
|
+
onRetry: props.queue.onRetry
|
|
123
|
+
}) : null, /*#__PURE__*/_jsx(ChatInput, {
|
|
96
124
|
loading: inputContext.loading,
|
|
97
125
|
disabled: inputContext.disabled,
|
|
98
126
|
placeholder: placeholder,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { QueuedInputItem } from './index';
|
|
2
|
+
interface InputQueuePanelProps {
|
|
3
|
+
items: QueuedInputItem[];
|
|
4
|
+
onRemove: (id: string) => void;
|
|
5
|
+
onClear: () => void;
|
|
6
|
+
onRetry: (id: string) => void;
|
|
7
|
+
}
|
|
8
|
+
export default function InputQueuePanel(props: InputQueuePanelProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { IconButton } from '@agentscope-ai/design';
|
|
2
|
+
import { SparkClearLine, SparkDeleteLine, SparkRefreshLine } from '@agentscope-ai/icons';
|
|
3
|
+
import { Tooltip } from 'antd';
|
|
4
|
+
import { useProviderContext } from "../../../..";
|
|
5
|
+
import { useTranslation } from "../../Context/ChatAnywhereI18nContext";
|
|
6
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
export default function InputQueuePanel(props) {
|
|
9
|
+
var items = props.items,
|
|
10
|
+
onRemove = props.onRemove,
|
|
11
|
+
onClear = props.onClear,
|
|
12
|
+
onRetry = props.onRetry;
|
|
13
|
+
var prefixCls = useProviderContext().getPrefixCls('chat-anywhere-input-queue');
|
|
14
|
+
var _useTranslation = useTranslation(),
|
|
15
|
+
t = _useTranslation.t;
|
|
16
|
+
var tr = function tr(key) {
|
|
17
|
+
return (t === null || t === void 0 ? void 0 : t(key)) || key;
|
|
18
|
+
};
|
|
19
|
+
if (!items.length) return null;
|
|
20
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
21
|
+
className: prefixCls,
|
|
22
|
+
children: [/*#__PURE__*/_jsxs("div", {
|
|
23
|
+
className: "".concat(prefixCls, "-header"),
|
|
24
|
+
children: [/*#__PURE__*/_jsxs("span", {
|
|
25
|
+
children: [tr('queue.title'), " (", items.length, ")"]
|
|
26
|
+
}), /*#__PURE__*/_jsx(Tooltip, {
|
|
27
|
+
title: tr('queue.clear'),
|
|
28
|
+
children: /*#__PURE__*/_jsx(IconButton, {
|
|
29
|
+
size: "small",
|
|
30
|
+
bordered: false,
|
|
31
|
+
icon: /*#__PURE__*/_jsx(SparkClearLine, {}),
|
|
32
|
+
onClick: onClear
|
|
33
|
+
})
|
|
34
|
+
})]
|
|
35
|
+
}), /*#__PURE__*/_jsx("div", {
|
|
36
|
+
className: "".concat(prefixCls, "-list"),
|
|
37
|
+
children: items.map(function (item, index) {
|
|
38
|
+
var failed = item.status === 'failed';
|
|
39
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
40
|
+
className: "".concat(prefixCls, "-item"),
|
|
41
|
+
children: [/*#__PURE__*/_jsx("span", {
|
|
42
|
+
className: "".concat(prefixCls, "-index"),
|
|
43
|
+
children: index + 1
|
|
44
|
+
}), /*#__PURE__*/_jsxs("div", {
|
|
45
|
+
className: "".concat(prefixCls, "-content"),
|
|
46
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
47
|
+
className: "".concat(prefixCls, "-text"),
|
|
48
|
+
children: item.data.query || tr('queue.attachmentOnly')
|
|
49
|
+
}), failed ? /*#__PURE__*/_jsx("div", {
|
|
50
|
+
className: "".concat(prefixCls, "-error"),
|
|
51
|
+
children: item.errorMessage || tr('queue.failed')
|
|
52
|
+
}) : null]
|
|
53
|
+
}), failed ? /*#__PURE__*/_jsx(Tooltip, {
|
|
54
|
+
title: tr('queue.retry'),
|
|
55
|
+
children: /*#__PURE__*/_jsx(IconButton, {
|
|
56
|
+
size: "small",
|
|
57
|
+
bordered: false,
|
|
58
|
+
icon: /*#__PURE__*/_jsx(SparkRefreshLine, {}),
|
|
59
|
+
onClick: function onClick() {
|
|
60
|
+
return onRetry(item.id);
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}) : null, /*#__PURE__*/_jsx(Tooltip, {
|
|
64
|
+
title: tr('common.delete'),
|
|
65
|
+
children: /*#__PURE__*/_jsx(IconButton, {
|
|
66
|
+
size: "small",
|
|
67
|
+
bordered: false,
|
|
68
|
+
icon: /*#__PURE__*/_jsx(SparkDeleteLine, {}),
|
|
69
|
+
onClick: function onClick() {
|
|
70
|
+
return onRemove(item.id);
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
})]
|
|
74
|
+
}, item.id);
|
|
75
|
+
})
|
|
76
|
+
})]
|
|
77
|
+
});
|
|
78
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { IAgentScopeRuntimeWebUIInputData } from '../../types';
|
|
2
|
+
export type QueuedInputStatus = 'pending' | 'failed';
|
|
3
|
+
export interface QueuedInputItem {
|
|
4
|
+
id: string;
|
|
5
|
+
data: IAgentScopeRuntimeWebUIInputData;
|
|
6
|
+
status: QueuedInputStatus;
|
|
7
|
+
retryCount: number;
|
|
8
|
+
errorMessage?: string;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
}
|
|
11
|
+
export interface EnqueueQueuedInputResult {
|
|
12
|
+
queue: QueuedInputItem[];
|
|
13
|
+
item?: QueuedInputItem;
|
|
14
|
+
reason?: 'full';
|
|
15
|
+
}
|
|
16
|
+
export declare const MAX_INPUT_QUEUE_SIZE = 50;
|
|
17
|
+
export declare function createQueuedInputItem(data: IAgentScopeRuntimeWebUIInputData, options?: {
|
|
18
|
+
id?: string;
|
|
19
|
+
now?: number;
|
|
20
|
+
}): QueuedInputItem;
|
|
21
|
+
export declare function enqueueQueuedInput(queue: QueuedInputItem[], data: IAgentScopeRuntimeWebUIInputData, options?: {
|
|
22
|
+
maxSize?: number;
|
|
23
|
+
id?: string;
|
|
24
|
+
now?: number;
|
|
25
|
+
}): EnqueueQueuedInputResult;
|
|
26
|
+
export declare function dequeueNextQueuedInput(queue: QueuedInputItem[]): {
|
|
27
|
+
item?: QueuedInputItem;
|
|
28
|
+
queue: QueuedInputItem[];
|
|
29
|
+
};
|
|
30
|
+
export declare function removeQueuedInput(queue: QueuedInputItem[], id: string): QueuedInputItem[];
|
|
31
|
+
export declare function retryQueuedInput(queue: QueuedInputItem[], id: string): QueuedInputItem[];
|
|
32
|
+
export declare function restoreFailedQueuedInput(queue: QueuedInputItem[], item: QueuedInputItem, error?: unknown): QueuedInputItem[];
|
|
33
|
+
export declare function canSubmitDirectly(options: {
|
|
34
|
+
loading: boolean | string;
|
|
35
|
+
queueLength: number;
|
|
36
|
+
draining: boolean;
|
|
37
|
+
}): boolean;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
6
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
7
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
8
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
9
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
10
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
11
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
12
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
13
|
+
export var MAX_INPUT_QUEUE_SIZE = 50;
|
|
14
|
+
var queueId = 0;
|
|
15
|
+
export function createQueuedInputItem(data, options) {
|
|
16
|
+
var _options$now;
|
|
17
|
+
return {
|
|
18
|
+
id: (options === null || options === void 0 ? void 0 : options.id) || "input-queue-".concat(Date.now().toString(36), "-").concat((++queueId).toString(36)),
|
|
19
|
+
data: data,
|
|
20
|
+
status: 'pending',
|
|
21
|
+
retryCount: 0,
|
|
22
|
+
createdAt: (_options$now = options === null || options === void 0 ? void 0 : options.now) !== null && _options$now !== void 0 ? _options$now : Date.now()
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function enqueueQueuedInput(queue, data, options) {
|
|
26
|
+
var _options$maxSize;
|
|
27
|
+
var maxSize = (_options$maxSize = options === null || options === void 0 ? void 0 : options.maxSize) !== null && _options$maxSize !== void 0 ? _options$maxSize : MAX_INPUT_QUEUE_SIZE;
|
|
28
|
+
if (queue.length >= maxSize) {
|
|
29
|
+
return {
|
|
30
|
+
queue: queue,
|
|
31
|
+
reason: 'full'
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
var item = createQueuedInputItem(data, options);
|
|
35
|
+
return {
|
|
36
|
+
queue: [].concat(_toConsumableArray(queue), [item]),
|
|
37
|
+
item: item
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function dequeueNextQueuedInput(queue) {
|
|
41
|
+
var next = queue[0];
|
|
42
|
+
if (!next || next.status !== 'pending') {
|
|
43
|
+
return {
|
|
44
|
+
queue: queue
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
item: next,
|
|
49
|
+
queue: queue.slice(1)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function removeQueuedInput(queue, id) {
|
|
53
|
+
return queue.filter(function (item) {
|
|
54
|
+
return item.id !== id;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export function retryQueuedInput(queue, id) {
|
|
58
|
+
return queue.map(function (item) {
|
|
59
|
+
return item.id === id ? _objectSpread(_objectSpread({}, item), {}, {
|
|
60
|
+
status: 'pending',
|
|
61
|
+
errorMessage: undefined
|
|
62
|
+
}) : item;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export function restoreFailedQueuedInput(queue, item, error) {
|
|
66
|
+
return [_objectSpread(_objectSpread({}, item), {}, {
|
|
67
|
+
status: 'failed',
|
|
68
|
+
retryCount: item.retryCount + 1,
|
|
69
|
+
errorMessage: error instanceof Error ? error.message : String(error || '')
|
|
70
|
+
})].concat(_toConsumableArray(queue));
|
|
71
|
+
}
|
|
72
|
+
export function canSubmitDirectly(options) {
|
|
73
|
+
return !options.loading && options.queueLength === 0 && !options.draining;
|
|
74
|
+
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { InputProps } from "../Input";
|
|
2
|
+
import { type QueuedInputItem } from "../InputQueue";
|
|
1
3
|
/**
|
|
2
4
|
* Chat controller hook — coordinates all chat-related operations.
|
|
3
5
|
*/
|
|
4
6
|
export default function useChatController(): {
|
|
5
7
|
handleSubmit: (data: import("../../../..").IAgentScopeRuntimeWebUIInputData) => void;
|
|
6
8
|
handleCancel: () => void;
|
|
9
|
+
inputQueue: QueuedInputItem[];
|
|
10
|
+
enqueueQueuedInput: (data: Parameters<InputProps['onSubmit']>[0]) => void;
|
|
11
|
+
removeQueuedInput: (id: string) => void;
|
|
12
|
+
clearQueuedInputs: () => void;
|
|
13
|
+
retryQueuedInput: (id: string) => void;
|
|
7
14
|
};
|