@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.
@@ -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
  */
@@ -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
- removeAll(): Promise<void>;
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
  }>;
@@ -150,8 +150,20 @@ export class ChatHistoryLocalStore {
150
150
  id: [id]
151
151
  });
152
152
  }
153
- async removeAll() {
154
- await deleteRecordList(this.getQueryCondition());
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();
@@ -51,6 +51,7 @@ export declare class AIStreamSession {
51
51
  sessionId: string | null;
52
52
  sendDataChannels: string[];
53
53
  revDataChannels: string[];
54
+ disposed: boolean;
54
55
  private activeEvent?;
55
56
  private activeObserver?;
56
57
  private promise;
@@ -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, isConnectSync, queryAgentToken, registerRecordAmplitudes, sendEventChatBreak, sendEventEnd, sendEventPayloadEnd, sendEventStart, sendImageData, sendTextData, startRecordAndSendAudioData, stopRecordAndSendAudioData, unregisterVoiceAmplitudes } from './ttt';
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 result = isConnectSync(this.options);
65
-
66
- // 监听断开事件,重置 state & 清理
67
- if (!this.observer) {
68
- this.observer = new AIStreamObserver(this.onStateChanged, this.pool);
69
- this.observer.observe({
70
- connectionState: true,
71
- sessionState: true
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
- // 调用 SDK connect
81
- const res = await connect(this.options);
82
- this.connectionId = res.connectionId;
83
- this.state = ConnectState.CONNECTED;
84
- this.promise = null;
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
- this.activeSessions.clear();
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 Error('Cannot start a new event while another is active');
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
- if (this.sessionId) {
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 Error("No available data code for type: ".concat(type));
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 Error('Cannot write to a closed event');
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 Error('Cannot stream to a closed event');
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 Error("".concat(dataChannel, " stream already exists"));
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 Error('Cannot stream to a closed event');
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
- const {
40
- options
41
- } = context;
42
- const key = "connection-".concat(options.clientType, "-").concat(options.deviceId || '');
43
- if (!mock.data.has(key)) {
44
- const connectionId = generateId();
45
- mock.data.set(key, {
46
- connectionId
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 = mock.data.get(key);
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 key = mock.data.get("connection-".concat(connectionId));
59
- mock.data.delete(key);
60
- mock.data.delete("connection-".concat(connectionId));
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]>;
@@ -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
- export class TTTError extends Error {
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
  }
@@ -1,4 +1,4 @@
1
- import { ApiRequestByAtopParams, ApiRequestByHighwayParams, AudioBody, AuthorizeParams, AuthorizePolicyStatusParams, CanIUseRouterParams, CheckConnectParams, CheckConnectResult, 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 } from '../AIStreamTypes';
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 isConnectSync: (params: CheckConnectParams) => CheckConnectResult;
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, true);
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 isConnectSync = params => {
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);
@@ -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
- } = (message === null || message === void 0 ? void 0 : message.meta) || {};
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).forEach(message => {
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",
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": "03c4af7fe4cfe27069db83d99a61042c8053399e"
38
+ "gitHead": "a0beda17c8e1c3ef81c718300c02a23d88a3ee4a"
39
39
  }