@ray-js/t-agent-plugin-aistream 0.2.0-beta-3 → 0.2.0-beta-5
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/AIStreamTypes.d.ts +24 -0
- package/dist/AIStreamTypes.js +1 -0
- package/dist/ChatHistoryStore.d.ts +5 -2
- package/dist/ChatHistoryStore.js +14 -2
- package/dist/utils/AIStream.d.ts +1 -0
- package/dist/utils/AIStream.js +55 -36
- package/dist/utils/defaultMock.js +84 -15
- package/dist/utils/errors.d.ts +26 -0
- package/dist/utils/errors.js +42 -0
- package/dist/utils/promisify.d.ts +0 -6
- package/dist/utils/promisify.js +2 -11
- package/dist/utils/sendMessage.d.ts +0 -4
- package/dist/utils/sendMessage.js +13 -10
- package/dist/utils/ttt.d.ts +6 -2
- package/dist/utils/ttt.js +2 -14
- package/dist/withAIStream.js +11 -8
- package/package.json +2 -2
package/dist/AIStreamTypes.d.ts
CHANGED
|
@@ -651,6 +651,7 @@ export declare enum FileFormat {
|
|
|
651
651
|
SWEEPER_MAP = 6
|
|
652
652
|
}
|
|
653
653
|
export declare enum AIStreamErrorCode {
|
|
654
|
+
AIStreamError = -1,
|
|
654
655
|
OK = 200,
|
|
655
656
|
BadRequest = 400,
|
|
656
657
|
Unauthenticated = 401,
|
|
@@ -828,6 +829,29 @@ export type CheckConnectResult = {
|
|
|
828
829
|
/** 连接的唯一标识 */
|
|
829
830
|
connectionId?: string;
|
|
830
831
|
};
|
|
832
|
+
export type IsConnectedParams = {
|
|
833
|
+
/** client 类型: 1-作为设备代理, 2-作为 App */
|
|
834
|
+
clientType: number;
|
|
835
|
+
/** 代理的设备ID, clientType == 1 时必传 */
|
|
836
|
+
deviceId?: string;
|
|
837
|
+
success?: (params: {
|
|
838
|
+
/** 是否已连接 */
|
|
839
|
+
connected: boolean;
|
|
840
|
+
/** 通道连接状态: 0-初始化,1-连接中,2-鉴权中,3-已连接,4-被云端断开,5-主动关闭 */
|
|
841
|
+
state: number;
|
|
842
|
+
/** 连接的唯一标识 */
|
|
843
|
+
connectionId?: string;
|
|
844
|
+
}) => void;
|
|
845
|
+
fail?: (params: {
|
|
846
|
+
errorMsg: string;
|
|
847
|
+
errorCode: string | number;
|
|
848
|
+
innerError: {
|
|
849
|
+
errorCode: string | number;
|
|
850
|
+
errorMsg: string;
|
|
851
|
+
};
|
|
852
|
+
}) => void;
|
|
853
|
+
complete?: () => void;
|
|
854
|
+
};
|
|
831
855
|
/**
|
|
832
856
|
*@description 发起通道连接,若此前已连接会直接回调成功
|
|
833
857
|
*/
|
package/dist/AIStreamTypes.js
CHANGED
|
@@ -118,6 +118,7 @@ export let FileFormat = /*#__PURE__*/function (FileFormat) {
|
|
|
118
118
|
return FileFormat;
|
|
119
119
|
}({});
|
|
120
120
|
export let AIStreamErrorCode = /*#__PURE__*/function (AIStreamErrorCode) {
|
|
121
|
+
AIStreamErrorCode[AIStreamErrorCode["AIStreamError"] = -1] = "AIStreamError";
|
|
121
122
|
AIStreamErrorCode[AIStreamErrorCode["OK"] = 200] = "OK";
|
|
122
123
|
AIStreamErrorCode[AIStreamErrorCode["BadRequest"] = 400] = "BadRequest";
|
|
123
124
|
AIStreamErrorCode[AIStreamErrorCode["Unauthenticated"] = 401] = "Unauthenticated";
|
|
@@ -36,8 +36,11 @@ export interface ChatHistoryStore<Key = number> {
|
|
|
36
36
|
}>;
|
|
37
37
|
/** 更新消息 */
|
|
38
38
|
update(id: Key, body: ChatMessageObject): Promise<void>;
|
|
39
|
+
/** 删除消息 */
|
|
39
40
|
remove(id: Key): Promise<void>;
|
|
40
|
-
|
|
41
|
+
/** 批量删除消息 */
|
|
42
|
+
removeAll(ids?: Key[]): Promise<void>;
|
|
43
|
+
/** 插入消息 */
|
|
41
44
|
insert(body: ChatMessageObject): Promise<{
|
|
42
45
|
id: Key;
|
|
43
46
|
}>;
|
|
@@ -57,7 +60,7 @@ export declare class ChatHistoryLocalStore implements ChatHistoryStore {
|
|
|
57
60
|
}>;
|
|
58
61
|
update(id: number, body: ChatMessageObject): Promise<void>;
|
|
59
62
|
remove(id: number): Promise<void>;
|
|
60
|
-
removeAll(): Promise<void>;
|
|
63
|
+
removeAll(ids?: number[]): Promise<void>;
|
|
61
64
|
insert(message: ChatMessageObject): Promise<{
|
|
62
65
|
id: number;
|
|
63
66
|
}>;
|
package/dist/ChatHistoryStore.js
CHANGED
|
@@ -150,8 +150,20 @@ export class ChatHistoryLocalStore {
|
|
|
150
150
|
id: [id]
|
|
151
151
|
});
|
|
152
152
|
}
|
|
153
|
-
async removeAll() {
|
|
154
|
-
|
|
153
|
+
async removeAll(ids) {
|
|
154
|
+
if (ids) {
|
|
155
|
+
if (!Array.isArray(ids)) {
|
|
156
|
+
throw new Error('ids is not array');
|
|
157
|
+
}
|
|
158
|
+
if (ids.length === 0) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
await deleteRecordList(_objectSpread({
|
|
162
|
+
id: ids
|
|
163
|
+
}, this.getQueryCondition()));
|
|
164
|
+
} else {
|
|
165
|
+
await deleteRecordList(this.getQueryCondition());
|
|
166
|
+
}
|
|
155
167
|
}
|
|
156
168
|
async insert(message) {
|
|
157
169
|
const nowTime = Date.now();
|
package/dist/utils/AIStream.d.ts
CHANGED
package/dist/utils/AIStream.js
CHANGED
|
@@ -6,9 +6,12 @@ import "core-js/modules/esnext.iterator.for-each.js";
|
|
|
6
6
|
import "core-js/modules/esnext.iterator.map.js";
|
|
7
7
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
8
8
|
import { AIStreamErrorCode, BizTag, ConnectClientType, ConnectState, EventType, SessionState } from '../AIStreamTypes';
|
|
9
|
-
import { closeSession, connect, createSession, disconnect, getCurrentHomeInfo,
|
|
9
|
+
import { closeSession, connect, createSession, disconnect, getCurrentHomeInfo, isConnected, queryAgentToken, registerRecordAmplitudes, sendEventChatBreak, sendEventEnd, sendEventPayloadEnd, sendEventStart, sendImageData, sendTextData, startRecordAndSendAudioData, stopRecordAndSendAudioData, unregisterVoiceAmplitudes } from './ttt';
|
|
10
10
|
import { AIStreamObserver, AIStreamObserverPool } from './observer';
|
|
11
11
|
import { isAbortError } from '@ray-js/t-agent';
|
|
12
|
+
import logger from './logger';
|
|
13
|
+
import { AIStreamConnectionError, AIStreamEventError } from './errors';
|
|
14
|
+
import { tryCatch } from './misc';
|
|
12
15
|
export class AIStreamClient {
|
|
13
16
|
constructor() {
|
|
14
17
|
_defineProperty(this, "pool", new AIStreamObserverPool());
|
|
@@ -38,8 +41,8 @@ export class AIStreamConnection {
|
|
|
38
41
|
session._onStateChanged(entry);
|
|
39
42
|
}
|
|
40
43
|
});
|
|
41
|
-
if (entry.body.connectState === ConnectState.DISCONNECTED || entry.body.connectState === ConnectState.CLOSED) {
|
|
42
|
-
//
|
|
44
|
+
if (entry.body.connectState === ConnectState.DISCONNECTED || entry.body.connectState === ConnectState.CLOSED || entry.body.connectState === ConnectState.CONNECTING || entry.body.connectState === ConnectState.AUTHORIZING) {
|
|
45
|
+
// 断开事件触发时只做清理,因为连接已经关掉了
|
|
43
46
|
this.cleanup();
|
|
44
47
|
}
|
|
45
48
|
}
|
|
@@ -47,9 +50,6 @@ export class AIStreamConnection {
|
|
|
47
50
|
this.activeSessions.forEach(session => {
|
|
48
51
|
if (session.sessionId && session.sessionId === entry.body.sessionId) {
|
|
49
52
|
session._onStateChanged(entry);
|
|
50
|
-
if (entry.body.sessionState === SessionState.CLOSED || entry.body.sessionState === SessionState.CREATE_FAILED) {
|
|
51
|
-
this.activeSessions.delete(session);
|
|
52
|
-
}
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
}
|
|
@@ -61,39 +61,52 @@ export class AIStreamConnection {
|
|
|
61
61
|
if (this.promise) {
|
|
62
62
|
return this.promise;
|
|
63
63
|
}
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
if (result.connected) {
|
|
75
|
-
this.state = result.state;
|
|
76
|
-
this.connectionId = result.connectionId;
|
|
77
|
-
return Promise.resolve();
|
|
78
|
-
}
|
|
64
|
+
const observe = () => {
|
|
65
|
+
if (!this.observer) {
|
|
66
|
+
this.observer = new AIStreamObserver(this.onStateChanged, this.pool);
|
|
67
|
+
this.observer.observe({
|
|
68
|
+
connectionState: true,
|
|
69
|
+
sessionState: true
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
79
73
|
this.promise = (async () => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
{
|
|
75
|
+
const [error, result] = await tryCatch(() => isConnected(this.options));
|
|
76
|
+
if (error) {
|
|
77
|
+
throw new AIStreamConnectionError(error.message, error.code || AIStreamErrorCode.AIStreamError);
|
|
78
|
+
}
|
|
79
|
+
if (result.connected) {
|
|
80
|
+
this.state = result.state;
|
|
81
|
+
this.connectionId = result.connectionId;
|
|
82
|
+
observe();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
{
|
|
87
|
+
// 调用 SDK connect
|
|
88
|
+
const [error, result] = await tryCatch(() => connect(this.options));
|
|
89
|
+
if (error) {
|
|
90
|
+
throw new AIStreamConnectionError(error.message, error.code || AIStreamErrorCode.AIStreamError);
|
|
91
|
+
}
|
|
92
|
+
this.connectionId = result.connectionId;
|
|
93
|
+
this.state = ConnectState.CONNECTED;
|
|
94
|
+
this.promise = null;
|
|
95
|
+
observe();
|
|
96
|
+
}
|
|
85
97
|
})();
|
|
86
98
|
return this.promise;
|
|
87
99
|
}
|
|
88
100
|
cleanup() {
|
|
89
101
|
var _this$observer;
|
|
102
|
+
logger.debug('AIStreamConnection cleanup');
|
|
90
103
|
(_this$observer = this.observer) === null || _this$observer === void 0 || _this$observer.disconnect();
|
|
91
104
|
this.observer = null;
|
|
92
105
|
this.state = ConnectState.CLOSED;
|
|
93
106
|
this.connectionId = null;
|
|
94
107
|
this.promise = null;
|
|
95
108
|
this.activeSessions.forEach(s => s.cleanup());
|
|
96
|
-
|
|
109
|
+
// session 不清空,closeSession 被调用时才清空
|
|
97
110
|
}
|
|
98
111
|
createSession(options) {
|
|
99
112
|
const session = new AIStreamSession(this, this.pool, options);
|
|
@@ -129,6 +142,7 @@ export class AIStreamSession {
|
|
|
129
142
|
_defineProperty(this, "sessionId", null);
|
|
130
143
|
_defineProperty(this, "sendDataChannels", []);
|
|
131
144
|
_defineProperty(this, "revDataChannels", []);
|
|
145
|
+
_defineProperty(this, "disposed", false);
|
|
132
146
|
_defineProperty(this, "promise", null);
|
|
133
147
|
_defineProperty(this, "_onStateChanged", entry => {
|
|
134
148
|
var _this$activeEvent;
|
|
@@ -203,8 +217,11 @@ export class AIStreamSession {
|
|
|
203
217
|
}
|
|
204
218
|
async startEvent() {
|
|
205
219
|
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
220
|
+
if (this.disposed) {
|
|
221
|
+
throw new AIStreamEventError('Event has been disposed', AIStreamErrorCode.AIStreamError);
|
|
222
|
+
}
|
|
206
223
|
if (this.activeEvent) {
|
|
207
|
-
throw new
|
|
224
|
+
throw new AIStreamEventError('Cannot start a new event while another is active', AIStreamErrorCode.AIStreamError);
|
|
208
225
|
}
|
|
209
226
|
await this.ensureSession();
|
|
210
227
|
const {
|
|
@@ -251,12 +268,14 @@ export class AIStreamSession {
|
|
|
251
268
|
}
|
|
252
269
|
cleanupEvent() {
|
|
253
270
|
var _this$activeObserver, _this$activeEvent4;
|
|
271
|
+
logger.debug('AIStreamSession cleanupEvent');
|
|
254
272
|
(_this$activeObserver = this.activeObserver) === null || _this$activeObserver === void 0 || _this$activeObserver.disconnect();
|
|
255
273
|
this.activeObserver = null;
|
|
256
274
|
(_this$activeEvent4 = this.activeEvent) === null || _this$activeEvent4 === void 0 || _this$activeEvent4.emit('close');
|
|
257
275
|
this.activeEvent = null;
|
|
258
276
|
}
|
|
259
277
|
cleanup() {
|
|
278
|
+
logger.debug('AIStreamSession cleanup');
|
|
260
279
|
this.cleanupEvent();
|
|
261
280
|
this.sessionId = null;
|
|
262
281
|
this.sendDataChannels = [];
|
|
@@ -265,10 +284,10 @@ export class AIStreamSession {
|
|
|
265
284
|
|
|
266
285
|
// 会话关闭清理
|
|
267
286
|
async close() {
|
|
287
|
+
logger.debug('AIStreamSession close');
|
|
288
|
+
this.disposed = true;
|
|
268
289
|
await this.connection.closeSession(this);
|
|
269
|
-
|
|
270
|
-
this.cleanup();
|
|
271
|
-
}
|
|
290
|
+
this.cleanup();
|
|
272
291
|
}
|
|
273
292
|
}
|
|
274
293
|
export class AIStreamEvent {
|
|
@@ -289,13 +308,13 @@ export class AIStreamEvent {
|
|
|
289
308
|
findFirstCode(type) {
|
|
290
309
|
const code = this.sendDataChannels.find(code => code.startsWith(type));
|
|
291
310
|
if (!code) {
|
|
292
|
-
throw new
|
|
311
|
+
throw new AIStreamEventError("No available data code for type: ".concat(type), AIStreamErrorCode.AIStreamError);
|
|
293
312
|
}
|
|
294
313
|
return code;
|
|
295
314
|
}
|
|
296
315
|
write(chunk) {
|
|
297
316
|
if (this.closed) {
|
|
298
|
-
throw new
|
|
317
|
+
throw new AIStreamEventError('Cannot write to a closed event', AIStreamErrorCode.AIStreamError);
|
|
299
318
|
}
|
|
300
319
|
const dataChannel = chunk.dataChannel || this.findFirstCode(chunk.type);
|
|
301
320
|
let promise = this.chains[dataChannel];
|
|
@@ -341,11 +360,11 @@ export class AIStreamEvent {
|
|
|
341
360
|
}
|
|
342
361
|
stream(source) {
|
|
343
362
|
if (this.closed) {
|
|
344
|
-
throw new
|
|
363
|
+
throw new AIStreamEventError('Cannot stream to a closed event', AIStreamErrorCode.AIStreamError);
|
|
345
364
|
}
|
|
346
365
|
const dataChannel = source.dataChannel || this.findFirstCode(source.type);
|
|
347
366
|
if (this.streams[dataChannel]) {
|
|
348
|
-
throw new
|
|
367
|
+
throw new AIStreamEventError("".concat(dataChannel, " stream already exists"), AIStreamErrorCode.AIStreamError);
|
|
349
368
|
}
|
|
350
369
|
const stream = {
|
|
351
370
|
dataChannel,
|
|
@@ -353,7 +372,7 @@ export class AIStreamEvent {
|
|
|
353
372
|
started: false,
|
|
354
373
|
start: async () => {
|
|
355
374
|
if (this.closed) {
|
|
356
|
-
throw new
|
|
375
|
+
throw new AIStreamEventError('Cannot stream to a closed event', AIStreamErrorCode.AIStreamError);
|
|
357
376
|
}
|
|
358
377
|
if (stream.started) {
|
|
359
378
|
return;
|
|
@@ -8,13 +8,17 @@ import "core-js/modules/esnext.iterator.map.js";
|
|
|
8
8
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
9
9
|
import { mock } from './mock';
|
|
10
10
|
import { EmitterEvent, generateId } from '@ray-js/t-agent';
|
|
11
|
-
import { BizCode, EventType, ReceivedTextPacketEof, ReceivedTextPacketType, StreamFlag } from '../AIStreamTypes';
|
|
11
|
+
import { BizCode, ConnectState, EventType, ReceivedTextPacketEof, ReceivedTextPacketType, StreamFlag } from '../AIStreamTypes';
|
|
12
12
|
import AbortController from './abort';
|
|
13
13
|
function splitString(input) {
|
|
14
14
|
return input.match(/[a-zA-Z0-9]+\s*|[\u4e00-\u9fff]+\s*|[^\w\s\u4e00-\u9fff]+\s*|[\s]+/g) || [];
|
|
15
15
|
}
|
|
16
16
|
mock.data.set('sessionMap', new Map());
|
|
17
17
|
const getSession = (sessionId, eventId) => {
|
|
18
|
+
const connection = getCurrentConnection();
|
|
19
|
+
if (!connection) {
|
|
20
|
+
throw new Error('not connected');
|
|
21
|
+
}
|
|
18
22
|
const map = mock.data.get('sessionMap');
|
|
19
23
|
const session = map.get(sessionId);
|
|
20
24
|
if (!session) {
|
|
@@ -35,19 +39,35 @@ mock.hooks.hook('getMiniAppConfig', context => {
|
|
|
35
39
|
config: {}
|
|
36
40
|
};
|
|
37
41
|
});
|
|
42
|
+
const getCurrentConnection = () => {
|
|
43
|
+
return mock.data.get('currentConnection');
|
|
44
|
+
};
|
|
45
|
+
mock.hooks.hook('isConnected', context => {
|
|
46
|
+
const connection = getCurrentConnection();
|
|
47
|
+
if (connection) {
|
|
48
|
+
context.result = {
|
|
49
|
+
connected: true,
|
|
50
|
+
state: ConnectState.CONNECTED,
|
|
51
|
+
connectionId: connection.connectionId
|
|
52
|
+
};
|
|
53
|
+
} else {
|
|
54
|
+
context.result = {
|
|
55
|
+
connected: false,
|
|
56
|
+
state: ConnectState.DISCONNECTED
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
38
60
|
mock.hooks.hook('connect', context => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
mock.data.set("connection-".concat(connectionId), key);
|
|
61
|
+
let connection = getCurrentConnection();
|
|
62
|
+
if (connection) {
|
|
63
|
+
throw new Error('already connected');
|
|
64
|
+
} else {
|
|
65
|
+
connection = {
|
|
66
|
+
connectionId: generateId()
|
|
67
|
+
};
|
|
68
|
+
mock.data.set('currentConnection', connection);
|
|
49
69
|
}
|
|
50
|
-
context.result =
|
|
70
|
+
context.result = connection;
|
|
51
71
|
});
|
|
52
72
|
mock.hooks.hook('disconnect', context => {
|
|
53
73
|
const {
|
|
@@ -55,9 +75,15 @@ mock.hooks.hook('disconnect', context => {
|
|
|
55
75
|
connectionId
|
|
56
76
|
}
|
|
57
77
|
} = context;
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
const connection = getCurrentConnection();
|
|
79
|
+
if (!connection) {
|
|
80
|
+
throw new Error('not connected');
|
|
81
|
+
}
|
|
82
|
+
if (connection.connectionId !== connectionId) {
|
|
83
|
+
throw new Error('connectionId mismatch');
|
|
84
|
+
}
|
|
85
|
+
mock.data.set('currentConnection', null);
|
|
86
|
+
mock.data.set('sessionMap', new Map());
|
|
61
87
|
});
|
|
62
88
|
mock.hooks.hook('queryAgentToken', context => {
|
|
63
89
|
context.result = {
|
|
@@ -75,14 +101,25 @@ const dispatch = (type, detail) => {
|
|
|
75
101
|
}));
|
|
76
102
|
};
|
|
77
103
|
mock.hooks.hook('createSession', context => {
|
|
104
|
+
const connection = getCurrentConnection();
|
|
105
|
+
if (!connection) {
|
|
106
|
+
throw new Error('not connected');
|
|
107
|
+
}
|
|
78
108
|
const map = mock.data.get('sessionMap');
|
|
79
109
|
const session = {
|
|
110
|
+
closed: false,
|
|
80
111
|
sessionId: generateId(),
|
|
81
112
|
sendDataChannels: ['audio', 'video', 'text', 'image'],
|
|
82
113
|
revDataChannels: ['text', 'audio'],
|
|
83
114
|
currentEvent: null,
|
|
84
115
|
replyText: function (streamFlag) {
|
|
85
116
|
let data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
117
|
+
if (session.closed) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!session.currentEvent) {
|
|
121
|
+
throw new Error('replyText event not exists');
|
|
122
|
+
}
|
|
86
123
|
dispatch('onTextReceived', {
|
|
87
124
|
dataChannel: 'text',
|
|
88
125
|
sessionIdList: [session.sessionId],
|
|
@@ -91,6 +128,9 @@ mock.hooks.hook('createSession', context => {
|
|
|
91
128
|
});
|
|
92
129
|
},
|
|
93
130
|
replyEvent: eventType => {
|
|
131
|
+
if (session.closed) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
94
134
|
if (!session.currentEvent) {
|
|
95
135
|
throw new Error('replyEvent event not exists');
|
|
96
136
|
}
|
|
@@ -107,6 +147,32 @@ mock.hooks.hook('createSession', context => {
|
|
|
107
147
|
sendDataChannels: session.sendDataChannels,
|
|
108
148
|
revDataChannels: session.revDataChannels
|
|
109
149
|
};
|
|
150
|
+
|
|
151
|
+
// 用于测试断开连接;
|
|
152
|
+
// setTimeout(() => {
|
|
153
|
+
// dispatch('onConnectStateChanged', {
|
|
154
|
+
// connectionId: getCurrentConnection().connectionId,
|
|
155
|
+
// connectState: ConnectState.DISCONNECTED,
|
|
156
|
+
// code: 200,
|
|
157
|
+
// });
|
|
158
|
+
// mock.data.set('currentConnection', null);
|
|
159
|
+
// const map: Map<string, MockSession> = mock.data.get('sessionMap');
|
|
160
|
+
// map.forEach(s => {
|
|
161
|
+
// s.closed = true;
|
|
162
|
+
// });
|
|
163
|
+
// mock.data.set('sessionMap', new Map());
|
|
164
|
+
// }, 3000);
|
|
165
|
+
|
|
166
|
+
// 用于测试断开会话
|
|
167
|
+
// setTimeout(() => {
|
|
168
|
+
// dispatch('onSessionStateChanged', {
|
|
169
|
+
// sessionId: session.sessionId,
|
|
170
|
+
// sessionState: SessionState.CREATE_FAILED,
|
|
171
|
+
// code: 200,
|
|
172
|
+
// });
|
|
173
|
+
// session.closed = true;
|
|
174
|
+
// map.delete(session.sessionId);
|
|
175
|
+
// }, 3000);
|
|
110
176
|
});
|
|
111
177
|
mock.hooks.hook('closeSession', context => {
|
|
112
178
|
const map = mock.data.get('sessionMap');
|
|
@@ -259,6 +325,7 @@ mock.hooks.hook('startRecordAndSendAudioData', context => {
|
|
|
259
325
|
let text = '';
|
|
260
326
|
controller.signal.addEventListener('abort', async () => {
|
|
261
327
|
var _finishResolve;
|
|
328
|
+
// 终止识别到出完整结果的延迟
|
|
262
329
|
await mock.sleep(300);
|
|
263
330
|
session.replyText(StreamFlag.IN_PROGRESS, {
|
|
264
331
|
bizType: ReceivedTextPacketType.ASR,
|
|
@@ -278,6 +345,8 @@ mock.hooks.hook('startRecordAndSendAudioData', context => {
|
|
|
278
345
|
}
|
|
279
346
|
(_finishResolve = finishResolve) === null || _finishResolve === void 0 || _finishResolve();
|
|
280
347
|
});
|
|
348
|
+
|
|
349
|
+
// 识别 ASR 的延迟
|
|
281
350
|
await mock.sleep(100);
|
|
282
351
|
if (controller.signal.aborted) {
|
|
283
352
|
return;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class BaseError extends Error {
|
|
2
|
+
message: string;
|
|
3
|
+
code: string | number;
|
|
4
|
+
constructor(message: string, code: string | number);
|
|
5
|
+
toString(): string;
|
|
6
|
+
}
|
|
7
|
+
export declare class TTTError extends BaseError {
|
|
8
|
+
message: string;
|
|
9
|
+
code: string | number;
|
|
10
|
+
constructor(message: string, code: string | number);
|
|
11
|
+
}
|
|
12
|
+
export declare class AIStreamConnectionError extends BaseError {
|
|
13
|
+
message: string;
|
|
14
|
+
code: string | number;
|
|
15
|
+
constructor(message: string, code: string | number);
|
|
16
|
+
}
|
|
17
|
+
export declare class AIStreamSessionError extends BaseError {
|
|
18
|
+
message: string;
|
|
19
|
+
code: string | number;
|
|
20
|
+
constructor(message: string, code: string | number);
|
|
21
|
+
}
|
|
22
|
+
export declare class AIStreamEventError extends BaseError {
|
|
23
|
+
message: string;
|
|
24
|
+
code: string | number;
|
|
25
|
+
constructor(message: string, code: string | number);
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export class BaseError extends Error {
|
|
2
|
+
constructor(message, code) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.message = message;
|
|
5
|
+
this.code = code;
|
|
6
|
+
}
|
|
7
|
+
toString() {
|
|
8
|
+
return "".concat(this.name, "(").concat(this.code, "): ").concat(this.message);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class TTTError extends BaseError {
|
|
12
|
+
constructor(message, code) {
|
|
13
|
+
super(message, code);
|
|
14
|
+
this.message = message;
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.name = 'TTTError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class AIStreamConnectionError extends BaseError {
|
|
20
|
+
constructor(message, code) {
|
|
21
|
+
super(message, code);
|
|
22
|
+
this.message = message;
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.name = 'AIStreamConnectionError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class AIStreamSessionError extends BaseError {
|
|
28
|
+
constructor(message, code) {
|
|
29
|
+
super(message, code);
|
|
30
|
+
this.message = message;
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.name = 'AIStreamSessionError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class AIStreamEventError extends BaseError {
|
|
36
|
+
constructor(message, code) {
|
|
37
|
+
super(message, code);
|
|
38
|
+
this.message = message;
|
|
39
|
+
this.code = code;
|
|
40
|
+
this.name = 'AIStreamEventError';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -11,12 +11,6 @@ interface AsyncTTTFnParams<P> {
|
|
|
11
11
|
}) => void;
|
|
12
12
|
[key: string]: any;
|
|
13
13
|
}
|
|
14
|
-
export declare class TTTError extends Error {
|
|
15
|
-
message: string;
|
|
16
|
-
errorCode: string | number;
|
|
17
|
-
constructor(message: string, errorCode: string | number);
|
|
18
|
-
toString(): string;
|
|
19
|
-
}
|
|
20
14
|
export declare const getEnableMock: () => boolean;
|
|
21
15
|
export declare const setEnableMock: (enable: boolean) => boolean;
|
|
22
16
|
export declare function promisify<T extends AsyncTTTFnParams<any>>(fn: (options: any) => void, enableMock?: boolean): (options?: Omit<T, 'success' | 'fail'>) => Promise<Parameters<NonNullable<T["success"]>>[0]>;
|
package/dist/utils/promisify.js
CHANGED
|
@@ -2,16 +2,7 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
|
2
2
|
import { isDevTools } from './ttt';
|
|
3
3
|
import logger from './logger';
|
|
4
4
|
import { mock } from './mock';
|
|
5
|
-
|
|
6
|
-
constructor(message, errorCode) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.message = message;
|
|
9
|
-
this.errorCode = errorCode;
|
|
10
|
-
}
|
|
11
|
-
toString() {
|
|
12
|
-
return "TTTError: ".concat(this.message, ", errorCode: ").concat(this.errorCode);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
5
|
+
import { TTTError } from './errors';
|
|
15
6
|
let callId = 100000;
|
|
16
7
|
export const getEnableMock = () => {
|
|
17
8
|
try {
|
|
@@ -44,7 +35,7 @@ export function promisify(fn) {
|
|
|
44
35
|
return options => {
|
|
45
36
|
return new Promise((_resolve, _reject) => {
|
|
46
37
|
if (!fn) {
|
|
47
|
-
_reject(new Error('fn is not a function'));
|
|
38
|
+
_reject(new Error('promisify: fn is not a function'));
|
|
48
39
|
}
|
|
49
40
|
const id = callId++;
|
|
50
41
|
logger.debug("TTT call #".concat(id, " %c").concat(fn.name), 'background: blue; color: white', options);
|
|
@@ -11,10 +11,6 @@ export declare class AIStreamSessionError extends Error {
|
|
|
11
11
|
readonly code: number;
|
|
12
12
|
constructor(message: string, code: number);
|
|
13
13
|
}
|
|
14
|
-
export declare class AIStreamConnectionError extends Error {
|
|
15
|
-
readonly code: number;
|
|
16
|
-
constructor(message: string, code: number);
|
|
17
|
-
}
|
|
18
14
|
export declare function sendBlocksToAIStream(params: SendBlocksToAIStreamParams): {
|
|
19
15
|
response: StreamResponse;
|
|
20
16
|
metaPromise: Promise<Record<string, any>>;
|
|
@@ -6,6 +6,7 @@ import { ReadableStream } from 'web-streams-polyfill';
|
|
|
6
6
|
import { AIStreamAttributePayloadType, AIStreamAttributeType, AIStreamChatSysWorkflow, ConnectState, FileFormat, ReceivedTextPacketEof, ReceivedTextPacketType, SessionState, StreamFlag } from '../AIStreamTypes';
|
|
7
7
|
import { EmitterEvent, generateId, safeParseJSON, StreamResponse } from '@ray-js/t-agent';
|
|
8
8
|
import { tryCatch } from './misc';
|
|
9
|
+
import { AIStreamConnectionError } from './errors';
|
|
9
10
|
const mimeTypeToFormatMap = {
|
|
10
11
|
'video/mp4': FileFormat.MP4,
|
|
11
12
|
'text/json': FileFormat.JSON,
|
|
@@ -19,13 +20,6 @@ export class AIStreamSessionError extends Error {
|
|
|
19
20
|
this.code = code;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
export class AIStreamConnectionError extends Error {
|
|
23
|
-
constructor(message, code) {
|
|
24
|
-
super(message);
|
|
25
|
-
this.name = 'AIStreamConnectionError';
|
|
26
|
-
this.code = code;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
23
|
export function sendBlocksToAIStream(params) {
|
|
30
24
|
const {
|
|
31
25
|
session,
|
|
@@ -48,6 +42,7 @@ export function sendBlocksToAIStream(params) {
|
|
|
48
42
|
'sys.workflow': audioEmitter ? AIStreamChatSysWorkflow.ASR_LLM : AIStreamChatSysWorkflow.LLM
|
|
49
43
|
}, params.attribute);
|
|
50
44
|
let canceled = false;
|
|
45
|
+
let closed = false;
|
|
51
46
|
let event = null;
|
|
52
47
|
let metaResolve;
|
|
53
48
|
const metaPromise = new Promise(resolve => {
|
|
@@ -56,7 +51,7 @@ export function sendBlocksToAIStream(params) {
|
|
|
56
51
|
const stream = new ReadableStream({
|
|
57
52
|
async start(controller) {
|
|
58
53
|
const enqueue = part => {
|
|
59
|
-
if (canceled) {
|
|
54
|
+
if (canceled || closed) {
|
|
60
55
|
return;
|
|
61
56
|
}
|
|
62
57
|
controller.enqueue(part);
|
|
@@ -71,6 +66,7 @@ export function sendBlocksToAIStream(params) {
|
|
|
71
66
|
}));
|
|
72
67
|
if (error) {
|
|
73
68
|
controller.error(error);
|
|
69
|
+
closed = true;
|
|
74
70
|
controller.close();
|
|
75
71
|
return;
|
|
76
72
|
}
|
|
@@ -155,7 +151,7 @@ export function sendBlocksToAIStream(params) {
|
|
|
155
151
|
audioId = null;
|
|
156
152
|
}
|
|
157
153
|
} else if (data.type === 'sessionState') {
|
|
158
|
-
if (data.body.sessionState === SessionState.CLOSED) {
|
|
154
|
+
if (data.body.sessionState === SessionState.CLOSED || data.body.sessionState === SessionState.CREATE_FAILED) {
|
|
159
155
|
enqueue({
|
|
160
156
|
type: 'error',
|
|
161
157
|
error: new AIStreamSessionError('Session closed', data.body.code),
|
|
@@ -164,7 +160,7 @@ export function sendBlocksToAIStream(params) {
|
|
|
164
160
|
});
|
|
165
161
|
}
|
|
166
162
|
} else if (data.type === 'connectionState') {
|
|
167
|
-
if (data.body.connectState === ConnectState.DISCONNECTED) {
|
|
163
|
+
if (data.body.connectState === ConnectState.DISCONNECTED || data.body.connectState === ConnectState.CLOSED) {
|
|
168
164
|
enqueue({
|
|
169
165
|
type: 'error',
|
|
170
166
|
error: new AIStreamConnectionError('Connection disconnected', data.body.code),
|
|
@@ -174,8 +170,15 @@ export function sendBlocksToAIStream(params) {
|
|
|
174
170
|
}
|
|
175
171
|
}
|
|
176
172
|
});
|
|
173
|
+
event.on('close', () => {
|
|
174
|
+
if (!canceled && !closed) {
|
|
175
|
+
// 当取消后,不需要关闭控制器
|
|
176
|
+
controller.close();
|
|
177
|
+
}
|
|
178
|
+
});
|
|
177
179
|
event.on('finish', () => {
|
|
178
180
|
if (!canceled) {
|
|
181
|
+
closed = true;
|
|
179
182
|
// 当取消后,不需要关闭控制器
|
|
180
183
|
controller.close();
|
|
181
184
|
}
|
package/dist/utils/ttt.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApiRequestByAtopParams, ApiRequestByHighwayParams, AudioBody, AuthorizeParams, AuthorizePolicyStatusParams, CanIUseRouterParams,
|
|
1
|
+
import { ApiRequestByAtopParams, ApiRequestByHighwayParams, AudioBody, AuthorizeParams, AuthorizePolicyStatusParams, CanIUseRouterParams, CloseSessionParams, ConnectParams, ConnectStateBody, CreateSessionParams, DeleteRecordListParams, DisconnectParams, EventBody, EventChannelMessageParams, GetAppInfoParams, GetCurrentHomeInfoParams, GetMiniAppConfigParams, GetAccountInfoParams, ImageBody, InsertRecordParams, NavigateToMiniProgramParams, OpenInnerH5Params, OpenMiniWidgetParams, QueryAgentTokenParams, QueryRecordListParams, RecordAmplitudesBody, RegisterChannelParams, RouterParams, SendEventChatBreakParams, SendEventEndParams, SendEventPayloadEndParams, SendEventStartParams, SendImageDataParams, SendTextDataParams, SessionStateBody, StartRecordAndSendAudioDataParams, StopRecordAndSendAudioDataParams, TextBody, UpdateRecordParams, IsConnectedParams } from '../AIStreamTypes';
|
|
2
2
|
export declare const getMiniAppConfig: (options?: Omit<GetMiniAppConfigParams, "success" | "fail"> | undefined) => Promise<{
|
|
3
3
|
config: any;
|
|
4
4
|
}>;
|
|
@@ -52,7 +52,11 @@ export declare const authorizePolicyStatus: (options?: Omit<AuthorizePolicyStatu
|
|
|
52
52
|
export declare const registerChannel: (options?: Omit<RegisterChannelParams, "success" | "fail"> | undefined) => Promise<null>;
|
|
53
53
|
export declare const listenReceiveMessage: (listener: (params: EventChannelMessageParams) => void) => () => void;
|
|
54
54
|
export declare const openMiniWidget: (options?: Omit<OpenMiniWidgetParams, "success" | "fail"> | undefined) => Promise<null>;
|
|
55
|
-
export declare const
|
|
55
|
+
export declare const isConnected: (options?: Omit<IsConnectedParams, "success" | "fail"> | undefined) => Promise<{
|
|
56
|
+
connected: boolean;
|
|
57
|
+
state: number;
|
|
58
|
+
connectionId?: string | undefined;
|
|
59
|
+
}>;
|
|
56
60
|
export declare const connect: (options?: Omit<ConnectParams, "success" | "fail"> | undefined) => Promise<{
|
|
57
61
|
connectionId: string;
|
|
58
62
|
}>;
|
package/dist/utils/ttt.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { listening, promisify } from './promisify';
|
|
2
|
-
import { ConnectClientType, ConnectState } from '../AIStreamTypes';
|
|
3
|
-
import { generateId } from '@ray-js/t-agent';
|
|
4
2
|
export const getMiniAppConfig = promisify(ty.getMiniAppConfig, true);
|
|
5
|
-
export const getAccountInfo = promisify(ty.getAccountInfo
|
|
3
|
+
export const getAccountInfo = promisify(ty.getAccountInfo);
|
|
6
4
|
export const isDevTools = () => {
|
|
7
5
|
return ty.getSystemInfoSync().brand === 'devtools';
|
|
8
6
|
};
|
|
@@ -30,17 +28,7 @@ ty.authorizePolicyStatus);
|
|
|
30
28
|
export const registerChannel = promisify(ty.registerChannel);
|
|
31
29
|
export const listenReceiveMessage = listening(ty.onReceiveMessage, ty.offReceiveMessage);
|
|
32
30
|
export const openMiniWidget = promisify(ty.openMiniWidget);
|
|
33
|
-
export const
|
|
34
|
-
if (isDevTools()) {
|
|
35
|
-
return {
|
|
36
|
-
connected: true,
|
|
37
|
-
state: ConnectState.CONNECTED,
|
|
38
|
-
connectionId: generateId(),
|
|
39
|
-
clientType: ConnectClientType.APP
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
return ty.aistream.isConnectedSync(params);
|
|
43
|
-
};
|
|
31
|
+
export const isConnected = promisify(ty.aistream.isConnected, true);
|
|
44
32
|
export const connect = promisify(ty.aistream.connect, true);
|
|
45
33
|
export const disconnect = promisify(ty.aistream.disconnect, true);
|
|
46
34
|
export const queryAgentToken = promisify(ty.aistream.queryAgentToken, true);
|
package/dist/withAIStream.js
CHANGED
|
@@ -5,7 +5,6 @@ import "core-js/modules/es.array.flat.js";
|
|
|
5
5
|
import "core-js/modules/es.array.reverse.js";
|
|
6
6
|
import "core-js/modules/es.array.unscopables.flat.js";
|
|
7
7
|
import "core-js/modules/esnext.iterator.constructor.js";
|
|
8
|
-
import "core-js/modules/esnext.iterator.for-each.js";
|
|
9
8
|
import "core-js/modules/esnext.iterator.map.js";
|
|
10
9
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
11
10
|
import { BubbleTileStatus, ChatMessageStatus, createHooks } from '@ray-js/t-agent';
|
|
@@ -351,10 +350,19 @@ export function withAIStream() {
|
|
|
351
350
|
payload
|
|
352
351
|
} = context;
|
|
353
352
|
const message = session.messages.get(payload.messageId);
|
|
353
|
+
if (!message) {
|
|
354
|
+
throw new Error('message not found');
|
|
355
|
+
}
|
|
354
356
|
const {
|
|
355
357
|
eventId,
|
|
356
358
|
sessionId
|
|
357
|
-
} =
|
|
359
|
+
} = message.meta;
|
|
360
|
+
if (!eventId || !sessionId) {
|
|
361
|
+
context.result = {
|
|
362
|
+
success: true
|
|
363
|
+
};
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
358
366
|
await feedback({
|
|
359
367
|
requestId: [eventId, sessionId].join('#'),
|
|
360
368
|
type: payload.rate
|
|
@@ -440,12 +448,7 @@ export function withAIStream() {
|
|
|
440
448
|
clearAllMessages: async () => {
|
|
441
449
|
// 删除内存中的消息
|
|
442
450
|
const messages = agent.session.messages.values();
|
|
443
|
-
Array.from(messages).
|
|
444
|
-
message.remove();
|
|
445
|
-
});
|
|
446
|
-
if (options !== null && options !== void 0 && options.indexId) {
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
451
|
+
await Promise.all(Array.from(messages).map(message => message.remove()));
|
|
449
452
|
// 清空缓存的数据
|
|
450
453
|
const historyStore = getHistoryStore();
|
|
451
454
|
if (historyStore) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ray-js/t-agent-plugin-aistream",
|
|
3
|
-
"version": "0.2.0-beta-
|
|
3
|
+
"version": "0.2.0-beta-5",
|
|
4
4
|
"author": "Tuya.inc",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/url-parse": "^1.4.11"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "a0beda17c8e1c3ef81c718300c02a23d88a3ee4a"
|
|
39
39
|
}
|