@ray-js/t-agent-ui-ray 0.2.0-beta-11 → 0.2.0-beta-12
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/dist/MessageInput/MessageInputAIStream/AsrInput.d.ts +1 -1
- package/dist/MessageInput/MessageInputAIStream/AsrInput.js +13 -7
- package/dist/MessageInput/MessageInputAIStream/index.js +239 -146
- package/dist/MessageInput/index.less +7 -0
- package/dist/MessageList/index.js +1 -2
- package/dist/i18n/strings.d.ts +2 -0
- package/dist/i18n/strings.js +2 -0
- package/dist/tiles/ImageTile/index.js +0 -1
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ const AsrInput = props => {
|
|
|
12
12
|
const t = useTranslate();
|
|
13
13
|
const [active, setActive] = useState(false);
|
|
14
14
|
const [cancel, setCancel] = useState(false);
|
|
15
|
-
const onVoiceTouchEnd = event => {
|
|
15
|
+
const onVoiceTouchEnd = async event => {
|
|
16
16
|
const touchY = event.changedTouches[0].pageY;
|
|
17
17
|
setCancel(false);
|
|
18
18
|
setActive(false);
|
|
@@ -30,11 +30,13 @@ const AsrInput = props => {
|
|
|
30
30
|
startAt: 0,
|
|
31
31
|
y: 0
|
|
32
32
|
};
|
|
33
|
-
props.onCancel();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
const result = await props.onCancel();
|
|
34
|
+
if (result) {
|
|
35
|
+
ty.showToast({
|
|
36
|
+
icon: 'none',
|
|
37
|
+
title: t('t-agent.input.asr.error.too-short')
|
|
38
|
+
});
|
|
39
|
+
}
|
|
38
40
|
return;
|
|
39
41
|
}
|
|
40
42
|
props.onConfirm();
|
|
@@ -50,10 +52,14 @@ const AsrInput = props => {
|
|
|
50
52
|
}, /*#__PURE__*/React.createElement(View, {
|
|
51
53
|
className: cx('t-agent-message-input-ptt', {
|
|
52
54
|
't-agent-message-input-ptt-active': active,
|
|
53
|
-
't-agent-message-input-ptt-cancel': cancel
|
|
55
|
+
't-agent-message-input-ptt-cancel': cancel,
|
|
56
|
+
't-agent-message-input-ptt-disabled': props.disabled
|
|
54
57
|
}),
|
|
55
58
|
onTouchStart: async event => {
|
|
56
59
|
var _event$touches$;
|
|
60
|
+
if (props.disabled) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
57
63
|
touchRef.current = {
|
|
58
64
|
startAt: Date.now(),
|
|
59
65
|
y: (_event$touches$ = event.touches[0]) === null || _event$touches$ === void 0 ? void 0 : _event$touches$.pageY
|
|
@@ -5,40 +5,111 @@ import "core-js/modules/esnext.iterator.map.js";
|
|
|
5
5
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
6
6
|
import '../index.less';
|
|
7
7
|
import { Button, View } from '@ray-js/components';
|
|
8
|
-
import React, { useRef, useState } from 'react';
|
|
8
|
+
import React, { useRef, useState, useMemo } from 'react';
|
|
9
9
|
import { Image, Input, ScrollView } from '@ray-js/ray';
|
|
10
10
|
import { AbortController, authorize } from '@ray-js/t-agent-plugin-aistream';
|
|
11
|
-
import { Emitter, EmitterEvent } from '@ray-js/t-agent';
|
|
11
|
+
import { Emitter, EmitterEvent, isAbortError } from '@ray-js/t-agent';
|
|
12
12
|
import cx from 'clsx';
|
|
13
13
|
import imageSvg from '../icons/image.svg';
|
|
14
14
|
import videoSvg from '../icons/video.svg';
|
|
15
15
|
import cameraSvg from '../icons/camera.svg';
|
|
16
16
|
import { useAttachmentInput, useChatAgent, useEmitEvent, useIsUnmounted, useOnEvent, useTranslate } from '../../hooks';
|
|
17
17
|
import AsrInput from './AsrInput';
|
|
18
|
-
import
|
|
18
|
+
import logger from '../../logger';
|
|
19
19
|
const AMPLITUDE_COUNT = 60;
|
|
20
|
+
var InputState = /*#__PURE__*/function (InputState) {
|
|
21
|
+
InputState["PENDING"] = "pending";
|
|
22
|
+
InputState["RECORDING"] = "recording";
|
|
23
|
+
InputState["ASR"] = "asr";
|
|
24
|
+
InputState["RESPONDING"] = "responding";
|
|
25
|
+
InputState["ABORTING"] = "aborting";
|
|
26
|
+
return InputState;
|
|
27
|
+
}(InputState || {});
|
|
28
|
+
var InputAction = /*#__PURE__*/function (InputAction) {
|
|
29
|
+
InputAction["SEND"] = "send";
|
|
30
|
+
InputAction["RECORD"] = "record";
|
|
31
|
+
InputAction["RECORD_TIMEOUT"] = "record_timeout";
|
|
32
|
+
InputAction["RECORD_CANCEL"] = "record_cancel";
|
|
33
|
+
InputAction["RECORD_CONFIRM"] = "record_confirm";
|
|
34
|
+
InputAction["ASR_END"] = "asr_end";
|
|
35
|
+
InputAction["ASR_ERROR"] = "asr_error";
|
|
36
|
+
InputAction["ABORT"] = "abort";
|
|
37
|
+
InputAction["TEXT_END"] = "text_end";
|
|
38
|
+
InputAction["ABORT_DONE"] = "abort_done";
|
|
39
|
+
InputAction["NETWORK_CHANGE"] = "network_change";
|
|
40
|
+
return InputAction;
|
|
41
|
+
}(InputAction || {});
|
|
42
|
+
const transitions = {
|
|
43
|
+
[InputState.PENDING]: {
|
|
44
|
+
[InputAction.SEND]: InputState.RESPONDING,
|
|
45
|
+
[InputAction.RECORD]: InputState.RECORDING,
|
|
46
|
+
[InputAction.NETWORK_CHANGE]: InputState.PENDING,
|
|
47
|
+
// 这个变更是为了兼容 pushInputBlocks 结束的场景,没有实际意义
|
|
48
|
+
[InputAction.TEXT_END]: InputState.PENDING
|
|
49
|
+
},
|
|
50
|
+
[InputState.RECORDING]: {
|
|
51
|
+
[InputAction.RECORD_TIMEOUT]: InputState.ASR,
|
|
52
|
+
[InputAction.NETWORK_CHANGE]: InputState.PENDING,
|
|
53
|
+
[InputAction.RECORD_CANCEL]: InputState.PENDING,
|
|
54
|
+
[InputAction.RECORD_CONFIRM]: InputState.ASR,
|
|
55
|
+
[InputAction.ABORT]: InputState.ABORTING,
|
|
56
|
+
[InputAction.ASR_ERROR]: InputState.PENDING
|
|
57
|
+
},
|
|
58
|
+
[InputState.ASR]: {
|
|
59
|
+
[InputAction.ASR_END]: InputState.RESPONDING,
|
|
60
|
+
[InputAction.ASR_ERROR]: InputState.PENDING,
|
|
61
|
+
[InputAction.ABORT]: InputState.ABORTING,
|
|
62
|
+
[InputAction.NETWORK_CHANGE]: InputState.PENDING
|
|
63
|
+
},
|
|
64
|
+
[InputState.RESPONDING]: {
|
|
65
|
+
[InputAction.ABORT]: InputState.ABORTING,
|
|
66
|
+
[InputAction.TEXT_END]: InputState.PENDING,
|
|
67
|
+
[InputAction.NETWORK_CHANGE]: InputState.PENDING
|
|
68
|
+
},
|
|
69
|
+
[InputState.ABORTING]: {
|
|
70
|
+
[InputAction.ABORT_DONE]: InputState.PENDING,
|
|
71
|
+
[InputAction.NETWORK_CHANGE]: InputState.PENDING
|
|
72
|
+
}
|
|
73
|
+
};
|
|
20
74
|
export default function MessageInputAIStream(props) {
|
|
21
75
|
const [moreOpen, setMoreOpen] = useState(false);
|
|
22
76
|
const t = useTranslate();
|
|
23
77
|
const [text, setText] = useState('');
|
|
24
|
-
const [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
confirm: null,
|
|
28
|
-
cancel: null
|
|
78
|
+
const [state, setState] = useState({
|
|
79
|
+
current: InputState.PENDING,
|
|
80
|
+
payload: {}
|
|
29
81
|
});
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
82
|
+
const dispatchRef = useRef(null);
|
|
83
|
+
dispatchRef.current = (event, payload) => {
|
|
84
|
+
var _transitions$state$cu;
|
|
85
|
+
const next = (_transitions$state$cu = transitions[state.current]) === null || _transitions$state$cu === void 0 ? void 0 : _transitions$state$cu[event];
|
|
86
|
+
if (next) {
|
|
87
|
+
logger.debug('MessageInputAIStream', "".concat(state.current, " ==").concat(event, "==> ").concat(next));
|
|
88
|
+
setState({
|
|
89
|
+
current: next,
|
|
90
|
+
payload
|
|
91
|
+
});
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
logger.error('MessageInputAIStream', "".concat(state.current, " ==").concat(event, "==> X not allowed"));
|
|
95
|
+
return false;
|
|
35
96
|
};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
97
|
+
const dispatch = (event, payload) => dispatchRef.current(event, payload);
|
|
98
|
+
const attachmentOptions = useMemo(() => {
|
|
99
|
+
const options = {
|
|
100
|
+
image: true,
|
|
101
|
+
video: true,
|
|
102
|
+
imageCount: 1,
|
|
103
|
+
videoCount: 1
|
|
104
|
+
};
|
|
105
|
+
if (props.attachment === false) {
|
|
106
|
+
options.image = false;
|
|
107
|
+
options.video = false;
|
|
108
|
+
} else if (typeof props.attachment === 'object') {
|
|
109
|
+
Object.assign(options, props.attachment);
|
|
110
|
+
}
|
|
111
|
+
return options;
|
|
112
|
+
}, [props.attachment]);
|
|
42
113
|
const attachmentInput = useAttachmentInput({
|
|
43
114
|
local: true
|
|
44
115
|
});
|
|
@@ -48,8 +119,6 @@ export default function MessageInputAIStream(props) {
|
|
|
48
119
|
setUploaded,
|
|
49
120
|
upload
|
|
50
121
|
} = attachmentInput;
|
|
51
|
-
const abortRef = useRef(null);
|
|
52
|
-
const [responding, setResponding] = useState(false);
|
|
53
122
|
const [mode, setMode] = useState('text');
|
|
54
123
|
const agent = useChatAgent();
|
|
55
124
|
const emitEvent = useEmitEvent();
|
|
@@ -58,8 +127,8 @@ export default function MessageInputAIStream(props) {
|
|
|
58
127
|
let {
|
|
59
128
|
online
|
|
60
129
|
} = _ref;
|
|
61
|
-
if (!online
|
|
62
|
-
|
|
130
|
+
if (!online) {
|
|
131
|
+
dispatch(InputAction.NETWORK_CHANGE, {});
|
|
63
132
|
}
|
|
64
133
|
});
|
|
65
134
|
const hasMore = attachmentOptions.image || attachmentOptions.video;
|
|
@@ -68,32 +137,31 @@ export default function MessageInputAIStream(props) {
|
|
|
68
137
|
if (!(inputBlocks !== null && inputBlocks !== void 0 && inputBlocks.length)) {
|
|
69
138
|
return;
|
|
70
139
|
}
|
|
71
|
-
setUploaded([]);
|
|
72
|
-
setText('');
|
|
73
|
-
setResponding(true);
|
|
74
140
|
const controller = new AbortController();
|
|
75
141
|
let abortResolve;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
promise: new Promise(resolve => {
|
|
79
|
-
abortResolve = resolve;
|
|
80
|
-
})
|
|
81
|
-
};
|
|
82
|
-
controller.signal.addEventListener('abort', () => {
|
|
83
|
-
var _abortRef$current;
|
|
84
|
-
if (((_abortRef$current = abortRef.current) === null || _abortRef$current === void 0 ? void 0 : _abortRef$current.controller) === controller) {
|
|
85
|
-
abortRef.current = null;
|
|
86
|
-
setResponding(false);
|
|
87
|
-
}
|
|
142
|
+
const abortPromise = new Promise(resolve => {
|
|
143
|
+
abortResolve = resolve;
|
|
88
144
|
});
|
|
145
|
+
if (!dispatch(InputAction.SEND, {
|
|
146
|
+
abort: async () => {
|
|
147
|
+
if (dispatch(InputAction.ABORT, {})) {
|
|
148
|
+
controller.abort('User abort');
|
|
149
|
+
await abortPromise;
|
|
150
|
+
dispatch(InputAction.ABORT_DONE, {});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
setUploaded([]);
|
|
157
|
+
setMoreOpen(false);
|
|
158
|
+
setText('');
|
|
89
159
|
try {
|
|
90
160
|
await agent.pushInputBlocks(inputBlocks, controller.signal);
|
|
91
|
-
if (controller.signal.aborted) {
|
|
92
|
-
abortResolve();
|
|
93
|
-
}
|
|
94
161
|
} finally {
|
|
95
|
-
|
|
96
|
-
|
|
162
|
+
abortResolve();
|
|
163
|
+
if (!isUnmounted() && !controller.signal.aborted) {
|
|
164
|
+
dispatch(InputAction.TEXT_END, {});
|
|
97
165
|
}
|
|
98
166
|
}
|
|
99
167
|
};
|
|
@@ -101,43 +169,16 @@ export default function MessageInputAIStream(props) {
|
|
|
101
169
|
let {
|
|
102
170
|
blocks
|
|
103
171
|
} = _ref2;
|
|
104
|
-
if (uploading
|
|
172
|
+
if (uploading) {
|
|
105
173
|
return;
|
|
106
174
|
}
|
|
107
|
-
|
|
108
|
-
setMoreOpen(false);
|
|
109
|
-
setUploaded([]);
|
|
110
|
-
setResponding(true);
|
|
111
|
-
const controller = new AbortController();
|
|
112
|
-
let abortResolve;
|
|
113
|
-
abortRef.current = {
|
|
114
|
-
controller,
|
|
115
|
-
promise: new Promise(resolve => {
|
|
116
|
-
abortResolve = resolve;
|
|
117
|
-
})
|
|
118
|
-
};
|
|
119
|
-
controller.signal.addEventListener('abort', () => {
|
|
120
|
-
if (abortRef.current.controller === controller) {
|
|
121
|
-
abortRef.current = null;
|
|
122
|
-
}
|
|
123
|
-
setResponding(false);
|
|
124
|
-
});
|
|
125
|
-
try {
|
|
126
|
-
await agent.pushInputBlocks(blocks, controller.signal);
|
|
127
|
-
if (controller.signal.aborted) {
|
|
128
|
-
abortResolve();
|
|
129
|
-
}
|
|
130
|
-
} finally {
|
|
131
|
-
if (!isUnmounted()) {
|
|
132
|
-
setResponding(false);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
175
|
+
send(blocks);
|
|
135
176
|
});
|
|
136
177
|
useOnEvent('setInputBlocks', async _ref3 => {
|
|
137
178
|
let {
|
|
138
179
|
blocks
|
|
139
180
|
} = _ref3;
|
|
140
|
-
if (uploading ||
|
|
181
|
+
if (uploading || state.current !== InputState.PENDING) {
|
|
141
182
|
return;
|
|
142
183
|
}
|
|
143
184
|
if (mode !== 'text') {
|
|
@@ -163,9 +204,10 @@ export default function MessageInputAIStream(props) {
|
|
|
163
204
|
});
|
|
164
205
|
}
|
|
165
206
|
};
|
|
166
|
-
useSleep();
|
|
167
207
|
let container;
|
|
168
|
-
const canSend = text.trim().length &&
|
|
208
|
+
const canSend = text.trim().length && state.current === InputState.PENDING && !attachmentInput.uploading;
|
|
209
|
+
const canAbort = state.current === InputState.RESPONDING || state.current === InputState.ASR;
|
|
210
|
+
const recordingFlagRef = useRef(false);
|
|
169
211
|
if (mode === 'text') {
|
|
170
212
|
container = /*#__PURE__*/React.createElement(View, {
|
|
171
213
|
className: "t-agent-message-input-text-bar"
|
|
@@ -217,15 +259,14 @@ export default function MessageInputAIStream(props) {
|
|
|
217
259
|
className: cx('t-agent-message-input-button', {
|
|
218
260
|
't-agent-message-input-button-more': isMore,
|
|
219
261
|
't-agent-message-input-button-more-open': moreOpen && isMore,
|
|
220
|
-
't-agent-message-input-button-send': !isMore && !
|
|
221
|
-
't-agent-message-input-button-stop':
|
|
262
|
+
't-agent-message-input-button-send': !isMore && !canAbort,
|
|
263
|
+
't-agent-message-input-button-stop': canAbort
|
|
222
264
|
}),
|
|
223
265
|
"data-testid": "t-agent-message-input-button-main",
|
|
224
266
|
onClick: async () => {
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
267
|
+
if (canAbort) {
|
|
268
|
+
var _state$payload$abort, _state$payload;
|
|
269
|
+
await ((_state$payload$abort = (_state$payload = state.payload).abort) === null || _state$payload$abort === void 0 ? void 0 : _state$payload$abort.call(_state$payload));
|
|
229
270
|
} else if (isMore) {
|
|
230
271
|
openMoreClick();
|
|
231
272
|
} else if (canSend) {
|
|
@@ -238,92 +279,144 @@ export default function MessageInputAIStream(props) {
|
|
|
238
279
|
}));
|
|
239
280
|
} else {
|
|
240
281
|
container = /*#__PURE__*/React.createElement(AsrInput, {
|
|
241
|
-
responding:
|
|
242
|
-
disabled:
|
|
282
|
+
responding: canAbort,
|
|
283
|
+
disabled: state.current === InputState.ABORTING,
|
|
243
284
|
amplitudeCount: AMPLITUDE_COUNT,
|
|
244
|
-
recording:
|
|
285
|
+
recording: state.current === InputState.RECORDING,
|
|
245
286
|
onRecord: async () => {
|
|
287
|
+
logger.debug('MessageInputAIStream', 'AsrInput onRecord');
|
|
246
288
|
if (attachmentInput.uploading) {
|
|
247
289
|
return;
|
|
248
290
|
}
|
|
249
|
-
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
291
|
+
recordingFlagRef.current = true;
|
|
292
|
+
if (canAbort) {
|
|
293
|
+
logger.debug('MessageInputAIStream', 'AsrInput onRecord canAbort');
|
|
294
|
+
await state.payload.abort();
|
|
295
|
+
logger.debug('MessageInputAIStream', 'AsrInput onRecord canAbort done');
|
|
254
296
|
}
|
|
297
|
+
if (!recordingFlagRef.current) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
recordingFlagRef.current = false;
|
|
301
|
+
// 有可能在中断完成前就触发了录音,所以需要检查一下状态
|
|
302
|
+
// 中断完成后,再检查一次状态,如果取消了,那么不再继续录音
|
|
303
|
+
|
|
255
304
|
const emitter = new Emitter();
|
|
256
|
-
|
|
257
|
-
|
|
305
|
+
emitter.addEventListener('error', async error => {
|
|
306
|
+
logger.error('MessageInputAIStream', 'AsrInput error', error);
|
|
307
|
+
if (dispatch(InputAction.ASR_ERROR, {})) {
|
|
308
|
+
var _error$detail;
|
|
309
|
+
if (((_error$detail = error.detail) === null || _error$detail === void 0 ? void 0 : _error$detail.name) === 'AsrEmptyError') {
|
|
310
|
+
ty.showToast({
|
|
311
|
+
icon: 'error',
|
|
312
|
+
title: t('t-agent.input.asr.error.empty')
|
|
313
|
+
});
|
|
314
|
+
} else {
|
|
315
|
+
ty.showToast({
|
|
316
|
+
icon: 'error',
|
|
317
|
+
title: t('t-agent.input.asr.error.unknown')
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
emitter.addEventListener('finish', () => {
|
|
323
|
+
dispatch(InputAction.ASR_END, {
|
|
324
|
+
abort
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
const controller = new AbortController();
|
|
328
|
+
let abortResolve;
|
|
329
|
+
const abortPromise = new Promise(resolve => {
|
|
330
|
+
abortResolve = resolve;
|
|
331
|
+
});
|
|
332
|
+
const abort = async () => {
|
|
333
|
+
if (dispatch(InputAction.ABORT, {})) {
|
|
334
|
+
controller.abort('User abort');
|
|
335
|
+
await abortPromise;
|
|
336
|
+
dispatch(InputAction.ABORT_DONE, {});
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
const recordTimeOutId = setTimeout(async () => {
|
|
340
|
+
if (dispatch(InputAction.RECORD_TIMEOUT, {
|
|
341
|
+
abort
|
|
342
|
+
})) {
|
|
343
|
+
clearTimeout(recordTimeOutId);
|
|
344
|
+
emitter.dispatchEvent(new EmitterEvent('confirm'));
|
|
258
345
|
ty.showToast({
|
|
259
346
|
icon: 'none',
|
|
260
347
|
title: t('t-agent.input.asr.error.timeout')
|
|
261
348
|
});
|
|
262
|
-
r.confirm();
|
|
263
349
|
}
|
|
264
350
|
}, props.maxAudioMs || 30000);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
});
|
|
278
|
-
},
|
|
279
|
-
cancel: () => {
|
|
280
|
-
r.recording = false;
|
|
281
|
-
clearTimeout(id);
|
|
282
|
-
emitter.dispatchEvent(new EmitterEvent('cancel'));
|
|
283
|
-
setRecord({
|
|
284
|
-
startAt: 0,
|
|
285
|
-
recording: false,
|
|
286
|
-
confirm: null,
|
|
287
|
-
cancel: null
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
setRecord(r);
|
|
292
|
-
emitter.addEventListener('error', () => {
|
|
293
|
-
ty.showToast({
|
|
294
|
-
icon: 'error',
|
|
295
|
-
title: t('t-agent.input.asr.error.empty')
|
|
296
|
-
});
|
|
297
|
-
setRecord({
|
|
298
|
-
startAt: 0,
|
|
299
|
-
recording: false,
|
|
300
|
-
confirm: null,
|
|
301
|
-
cancel: null
|
|
302
|
-
});
|
|
303
|
-
});
|
|
304
|
-
await send([...attachmentInput.blocks, {
|
|
351
|
+
if (!dispatch(InputAction.RECORD, {
|
|
352
|
+
emitter,
|
|
353
|
+
recordTimeOutId,
|
|
354
|
+
recordStartAt: Date.now(),
|
|
355
|
+
abort
|
|
356
|
+
})) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
setUploaded([]);
|
|
360
|
+
setMoreOpen(false);
|
|
361
|
+
setText('');
|
|
362
|
+
const inputBlocks = [...attachmentInput.blocks, {
|
|
305
363
|
type: 'audio',
|
|
306
364
|
audio_emitter: emitter,
|
|
307
365
|
amplitude_count: AMPLITUDE_COUNT
|
|
308
|
-
}]
|
|
366
|
+
}];
|
|
367
|
+
try {
|
|
368
|
+
logger.debug('MessageInputAIStream', 'pushInputBlocks start');
|
|
369
|
+
await agent.pushInputBlocks(inputBlocks, controller.signal);
|
|
370
|
+
logger.debug('MessageInputAIStream', 'pushInputBlocks end');
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (!isAbortError(error)) {
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
} finally {
|
|
376
|
+
abortResolve();
|
|
377
|
+
if (!isUnmounted() && !controller.signal.aborted) {
|
|
378
|
+
dispatch(InputAction.TEXT_END, {});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
309
381
|
},
|
|
310
|
-
onConfirm: () => {
|
|
311
|
-
|
|
312
|
-
|
|
382
|
+
onConfirm: async () => {
|
|
383
|
+
logger.debug('MessageInputAIStream', 'AsrInput onConfirm');
|
|
384
|
+
recordingFlagRef.current = false;
|
|
385
|
+
if (state.current === InputState.ABORTING) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const payload = state.payload;
|
|
389
|
+
if (dispatch(InputAction.RECORD_CONFIRM, {
|
|
390
|
+
abort: payload.abort
|
|
391
|
+
})) {
|
|
392
|
+
clearTimeout(payload.recordTimeOutId);
|
|
393
|
+
payload.emitter.dispatchEvent(new EmitterEvent('confirm'));
|
|
394
|
+
}
|
|
313
395
|
},
|
|
314
|
-
onCancel: () => {
|
|
315
|
-
|
|
316
|
-
|
|
396
|
+
onCancel: async () => {
|
|
397
|
+
logger.debug('MessageInputAIStream', 'AsrInput onCancel');
|
|
398
|
+
recordingFlagRef.current = false;
|
|
399
|
+
if (state.current === InputState.ABORTING) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
const payload = state.payload;
|
|
403
|
+
if (dispatch(InputAction.RECORD_CANCEL, {})) {
|
|
404
|
+
clearTimeout(payload.recordTimeOutId);
|
|
405
|
+
logger.debug('MessageInputAIStream', 'AsrInput onCancel send cancel');
|
|
406
|
+
payload.emitter.dispatchEvent(new EmitterEvent('cancel'));
|
|
407
|
+
await payload.abort();
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
return false;
|
|
317
411
|
},
|
|
318
412
|
onBack: () => {
|
|
319
413
|
setText('');
|
|
320
414
|
setMode('text');
|
|
321
415
|
setMoreOpen(false);
|
|
322
416
|
},
|
|
323
|
-
onAbort: () => {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
417
|
+
onAbort: async () => {
|
|
418
|
+
var _state$payload$abort2, _state$payload2;
|
|
419
|
+
await ((_state$payload$abort2 = (_state$payload2 = state.payload).abort) === null || _state$payload$abort2 === void 0 ? void 0 : _state$payload$abort2.call(_state$payload2));
|
|
327
420
|
}
|
|
328
421
|
});
|
|
329
422
|
}
|
|
@@ -379,7 +472,7 @@ export default function MessageInputAIStream(props) {
|
|
|
379
472
|
className: "t-agent-message-input-panel ".concat(moreOpen ? '' : 't-agent-message-input-panel-close')
|
|
380
473
|
}, /*#__PURE__*/React.createElement(View, {
|
|
381
474
|
className: "t-agent-message-input-panel-content"
|
|
382
|
-
}, attachmentOptions.image && /*#__PURE__*/React.createElement(Button, {
|
|
475
|
+
}, (attachmentOptions === null || attachmentOptions === void 0 ? void 0 : attachmentOptions.image) && /*#__PURE__*/React.createElement(Button, {
|
|
383
476
|
className: "t-agent-message-input-panel-button",
|
|
384
477
|
onClick: async () => {
|
|
385
478
|
try {
|
|
@@ -402,7 +495,7 @@ export default function MessageInputAIStream(props) {
|
|
|
402
495
|
className: "t-agent-message-input-panel-button-icon"
|
|
403
496
|
}, /*#__PURE__*/React.createElement(Image, {
|
|
404
497
|
src: imageSvg
|
|
405
|
-
}))), attachmentOptions.image && /*#__PURE__*/React.createElement(Button, {
|
|
498
|
+
}))), (attachmentOptions === null || attachmentOptions === void 0 ? void 0 : attachmentOptions.image) && /*#__PURE__*/React.createElement(Button, {
|
|
406
499
|
className: "t-agent-message-input-panel-button",
|
|
407
500
|
onClick: async () => {
|
|
408
501
|
try {
|
|
@@ -425,7 +518,7 @@ export default function MessageInputAIStream(props) {
|
|
|
425
518
|
className: "t-agent-message-input-panel-button-icon"
|
|
426
519
|
}, /*#__PURE__*/React.createElement(Image, {
|
|
427
520
|
src: cameraSvg
|
|
428
|
-
}))), attachmentOptions.video && /*#__PURE__*/React.createElement(Button, {
|
|
521
|
+
}))), (attachmentOptions === null || attachmentOptions === void 0 ? void 0 : attachmentOptions.video) && /*#__PURE__*/React.createElement(Button, {
|
|
429
522
|
className: "t-agent-message-input-panel-button",
|
|
430
523
|
onClick: async () => {
|
|
431
524
|
try {
|
|
@@ -8,7 +8,6 @@ import { View } from '@ray-js/components';
|
|
|
8
8
|
import React, { useMemo, useRef, useState } from 'react';
|
|
9
9
|
import { ScrollView } from '@ray-js/ray';
|
|
10
10
|
import { generateId } from '@ray-js/t-agent';
|
|
11
|
-
import { isVersionMatch } from '@ray-js/t-agent-plugin-aistream';
|
|
12
11
|
import MessageRender from '../MessageRender';
|
|
13
12
|
import { useAgentMessage, useAgentSessionValue, useOnEvent } from '../hooks';
|
|
14
13
|
import { useDebouncedFn } from '../hooks/useDebouncedFn';
|
|
@@ -40,7 +39,7 @@ export default function MessageList(props) {
|
|
|
40
39
|
} = systemInfo;
|
|
41
40
|
return {
|
|
42
41
|
// 在 2.27.2 版本之前,pageScrollTo 在某些场景不生效
|
|
43
|
-
scroll:
|
|
42
|
+
scroll: false
|
|
44
43
|
};
|
|
45
44
|
}, []);
|
|
46
45
|
const scroll = useDebouncedFn(animation => {
|
package/dist/i18n/strings.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ declare const _default: {
|
|
|
22
22
|
't-agent.input.asr.ptt': string;
|
|
23
23
|
't-agent.input.asr.error.too-short': string;
|
|
24
24
|
't-agent.input.asr.error.empty': string;
|
|
25
|
+
't-agent.input.asr.error.unknown': string;
|
|
25
26
|
't-agent.input.asr.error.timeout': string;
|
|
26
27
|
't-agent.message.feedback.success': string;
|
|
27
28
|
't-agent.message.bubble.aborted': string;
|
|
@@ -165,6 +166,7 @@ declare const _default: {
|
|
|
165
166
|
't-agent.input.asr.ptt': string;
|
|
166
167
|
't-agent.input.asr.error.too-short': string;
|
|
167
168
|
't-agent.input.asr.error.empty': string;
|
|
169
|
+
't-agent.input.asr.error.unknown': string;
|
|
168
170
|
't-agent.input.asr.error.timeout': string;
|
|
169
171
|
't-agent.message.feedback.success': string;
|
|
170
172
|
't-agent.message.bubble.aborted': string;
|
package/dist/i18n/strings.js
CHANGED
|
@@ -22,6 +22,7 @@ export default {
|
|
|
22
22
|
't-agent.input.asr.ptt': '按住说话',
|
|
23
23
|
't-agent.input.asr.error.too-short': '说话时间太短',
|
|
24
24
|
't-agent.input.asr.error.empty': '未能从语音中识别到文字',
|
|
25
|
+
't-agent.input.asr.error.unknown': '语音识别失败',
|
|
25
26
|
't-agent.input.asr.error.timeout': '语音识别已达时长限制,将直接发送',
|
|
26
27
|
't-agent.message.feedback.success': '反馈成功',
|
|
27
28
|
't-agent.message.bubble.aborted': '用户中断',
|
|
@@ -165,6 +166,7 @@ export default {
|
|
|
165
166
|
't-agent.input.asr.ptt': 'Press & hold to talk',
|
|
166
167
|
't-agent.input.asr.error.too-short': 'Speaking time too short',
|
|
167
168
|
't-agent.input.asr.error.empty': 'No text recognized from voice',
|
|
169
|
+
't-agent.input.asr.error.unknown': 'Text recognized failed',
|
|
168
170
|
't-agent.input.asr.error.timeout': 'Voice recognition has reached time limit, sending directly',
|
|
169
171
|
't-agent.message.feedback.success': 'Feedback Successful',
|
|
170
172
|
't-agent.message.bubble.aborted': 'User Aborted',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ray-js/t-agent-ui-ray",
|
|
3
|
-
"version": "0.2.0-beta-
|
|
3
|
+
"version": "0.2.0-beta-12",
|
|
4
4
|
"author": "Tuya.inc",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"@types/echarts": "^4.9.22",
|
|
41
41
|
"@types/markdown-it": "^14.1.1"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "a4e65a8f099f9ffa6e215ae005589c63f40587fe"
|
|
44
44
|
}
|