@ray-js/t-agent-plugin-aistream 0.2.0-beta-8 → 0.2.0-beta-10

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.
@@ -445,6 +445,9 @@ export class AIStreamEvent {
445
445
  });
446
446
  }
447
447
  async abort(options) {
448
+ if (this.closed) {
449
+ return;
450
+ }
448
451
  if (this.sessionId) {
449
452
  await sendEventChatBreak({
450
453
  eventId: this.eventId,
@@ -112,35 +112,7 @@ mock.hooks.hook('createSession', context => {
112
112
  sessionId: generateId(),
113
113
  sendDataChannels: ['audio', 'video', 'text', 'image'],
114
114
  revDataChannels: ['text', 'audio'],
115
- currentEvent: null,
116
- replyText: function (streamFlag) {
117
- let data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
118
- if (session.closed) {
119
- return;
120
- }
121
- if (!session.currentEvent) {
122
- throw new Error('replyText event not exists');
123
- }
124
- dispatch('onTextReceived', {
125
- dataChannel: 'text',
126
- sessionIdList: [session.sessionId],
127
- streamFlag,
128
- text: data ? JSON.stringify(data) : ''
129
- });
130
- },
131
- replyEvent: eventType => {
132
- if (session.closed) {
133
- return;
134
- }
135
- if (!session.currentEvent) {
136
- throw new Error('replyEvent event not exists');
137
- }
138
- dispatch('onEventReceived', {
139
- eventId: session.currentEvent.eventId,
140
- sessionId: session.sessionId,
141
- eventType
142
- });
143
- }
115
+ currentEvent: null
144
116
  };
145
117
  map.set(session.sessionId, session);
146
118
  context.result = {
@@ -193,17 +165,47 @@ mock.hooks.hook('sendEventStart', context => {
193
165
  throw new Error('sendEventStart already in event');
194
166
  }
195
167
  const eventId = generateId();
196
- session.currentEvent = {
197
- asrStatus: {
198
- controller: new AbortController(),
168
+ const event = {
169
+ replyText: function (streamFlag) {
170
+ let data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
171
+ if (session.closed || event.controller.signal.aborted) {
172
+ return;
173
+ }
174
+ if (!session.currentEvent || session.currentEvent.eventId !== eventId) {
175
+ throw new Error('replyEvent event not exists or not in current event');
176
+ }
177
+ dispatch('onTextReceived', {
178
+ dataChannel: 'text',
179
+ sessionIdList: [session.sessionId],
180
+ streamFlag,
181
+ text: data ? JSON.stringify(data) : ''
182
+ });
183
+ },
184
+ replyEvent: eventType => {
185
+ if (session.closed || event.controller.signal.aborted) {
186
+ return;
187
+ }
188
+ if (!session.currentEvent || session.currentEvent.eventId !== eventId) {
189
+ throw new Error('replyEvent event not exists or not in current event');
190
+ }
191
+ dispatch('onEventReceived', {
192
+ eventId,
193
+ sessionId: session.sessionId,
194
+ eventType
195
+ });
196
+ },
197
+ asr: {
198
+ finishController: new AbortController(),
199
199
  promise: Promise.resolve()
200
200
  },
201
201
  eventId,
202
202
  data: [],
203
203
  controller: new AbortController()
204
204
  };
205
+ session.currentEvent = event;
205
206
  context.result = {
206
- eventId
207
+ eventId,
208
+ sessionId
207
209
  };
208
210
  });
209
211
  mock.hooks.hook('registerRecordAmplitudes', context => {
@@ -218,9 +220,12 @@ mock.hooks.hook('sendEventEnd', context => {
218
220
  const session = getSession(context.options.sessionId, context.options.eventId);
219
221
  context.result = true;
220
222
  const event = session.currentEvent;
223
+ if (event.eventId !== context.options.eventId) {
224
+ throw new Error('sendEventEnd eventId mismatch');
225
+ }
221
226
  (async () => {
222
227
  var _ctx$responseSkills;
223
- await event.asrStatus.promise;
228
+ await event.asr.promise;
224
229
  const ctx = {
225
230
  data: event.data,
226
231
  responseText: ''
@@ -238,9 +243,13 @@ mock.hooks.hook('sendEventEnd', context => {
238
243
  return;
239
244
  }
240
245
  if (ctx.data.length === 0) {
241
- session.replyEvent(EventType.EVENT_START);
246
+ event.replyEvent(EventType.EVENT_START);
242
247
  await mock.sleep(100);
243
- session.replyEvent(EventType.EVENT_END);
248
+ if (event.controller.signal.aborted) {
249
+ return;
250
+ }
251
+ event.replyEvent(EventType.EVENT_END);
252
+ console.log('No data to send, ending event, session.currentEvent = null');
244
253
  session.currentEvent = null;
245
254
  return;
246
255
  }
@@ -249,19 +258,19 @@ mock.hooks.hook('sendEventEnd', context => {
249
258
  if (event.controller.signal.aborted) {
250
259
  return;
251
260
  }
252
- session.replyEvent(EventType.EVENT_START);
261
+ event.replyEvent(EventType.EVENT_START);
253
262
  await mock.sleep(500);
254
263
  if (event.controller.signal.aborted) {
255
264
  return;
256
265
  }
257
266
  const bizId = generateId();
258
- session.replyText(StreamFlag.START);
267
+ event.replyText(StreamFlag.START);
259
268
  for (const word of words) {
260
269
  await mock.sleep(100);
261
270
  if (event.controller.signal.aborted) {
262
271
  return;
263
272
  }
264
- session.replyText(StreamFlag.IN_PROGRESS, {
273
+ event.replyText(StreamFlag.IN_PROGRESS, {
265
274
  bizType: ReceivedTextPacketType.NLG,
266
275
  eof: ReceivedTextPacketEof.CONTINUE,
267
276
  bizId,
@@ -276,7 +285,7 @@ mock.hooks.hook('sendEventEnd', context => {
276
285
  if (event.controller.signal.aborted) {
277
286
  return;
278
287
  }
279
- session.replyText(StreamFlag.IN_PROGRESS, {
288
+ event.replyText(StreamFlag.IN_PROGRESS, {
280
289
  bizType: ReceivedTextPacketType.NLG,
281
290
  eof: ReceivedTextPacketEof.END,
282
291
  bizId,
@@ -296,7 +305,7 @@ mock.hooks.hook('sendEventEnd', context => {
296
305
  if (event.controller.signal.aborted) {
297
306
  return;
298
307
  }
299
- session.replyText(StreamFlag.IN_PROGRESS, {
308
+ event.replyText(StreamFlag.IN_PROGRESS, {
300
309
  bizType: ReceivedTextPacketType.SKILL,
301
310
  eof: ReceivedTextPacketEof.END,
302
311
  bizId,
@@ -304,12 +313,13 @@ mock.hooks.hook('sendEventEnd', context => {
304
313
  });
305
314
  }
306
315
  }
307
- session.replyText(StreamFlag.END);
316
+ event.replyText(StreamFlag.END);
308
317
  await mock.sleep(500);
309
318
  if (event.controller.signal.aborted) {
310
319
  return;
311
320
  }
312
- session.replyEvent(EventType.EVENT_END);
321
+ event.replyEvent(EventType.EVENT_END);
322
+ console.log('finish sendEventEnd session.currentEvent = null');
313
323
  session.currentEvent = null;
314
324
  })();
315
325
  });
@@ -320,6 +330,7 @@ mock.hooks.hook('sendEventPayloadEnd', context => {
320
330
  mock.hooks.hook('sendEventChatBreak', context => {
321
331
  const session = getSession(context.options.sessionId, context.options.eventId);
322
332
  session.currentEvent.controller.abort(new Error('sendEventChatBreak'));
333
+ console.log('sendEventChatBreak currentEvent = null');
323
334
  session.currentEvent = null;
324
335
  context.result = true;
325
336
  });
@@ -328,11 +339,12 @@ mock.hooks.hook('startRecordAndSendAudioData', context => {
328
339
  if (!session.currentEvent) {
329
340
  throw new Error('startRecordAndSendAudioData event not exists');
330
341
  }
342
+ const event = session.currentEvent;
331
343
  context.result = true;
332
344
  (async () => {
333
345
  const {
334
- controller
335
- } = session.currentEvent.asrStatus;
346
+ finishController
347
+ } = event.asr;
336
348
  const count = mock.data.get('recordAmplitudesCount');
337
349
  if (count) {
338
350
  const id = setInterval(() => {
@@ -340,9 +352,11 @@ mock.hooks.hook('startRecordAndSendAudioData', context => {
340
352
  amplitudes: new Array(count).fill(0).map(() => Math.random())
341
353
  });
342
354
  }, 100);
343
- controller.signal.addEventListener('abort', () => {
355
+ finishController.signal.addEventListener('abort', () => {
356
+ clearInterval(id);
357
+ });
358
+ event.controller.signal.addEventListener('abort', () => {
344
359
  clearInterval(id);
345
- mock.data.delete('recordAmplitudesCountId');
346
360
  });
347
361
  }
348
362
  const result = {
@@ -352,23 +366,23 @@ mock.hooks.hook('startRecordAndSendAudioData', context => {
352
366
  const responseText = result.responseText || '';
353
367
  const bizId = generateId();
354
368
  let finishResolve;
355
- session.currentEvent.asrStatus.promise = new Promise(resolve => {
369
+ event.asr.promise = new Promise(resolve => {
356
370
  finishResolve = resolve;
357
371
  });
358
372
  const parts = splitString(responseText);
359
373
  let text = '';
360
- controller.signal.addEventListener('abort', async () => {
374
+ finishController.signal.addEventListener('abort', async () => {
361
375
  var _finishResolve;
362
- if (!session.currentEvent) {
376
+ if (event.controller.signal.aborted) {
363
377
  return;
364
378
  }
365
379
 
366
380
  // 终止识别到出完整结果的延迟
367
381
  await mock.sleep(5000);
368
- if (!session.currentEvent) {
382
+ if (event.controller.signal.aborted) {
369
383
  return;
370
384
  }
371
- session.replyText(StreamFlag.IN_PROGRESS, {
385
+ event.replyText(StreamFlag.IN_PROGRESS, {
372
386
  bizType: ReceivedTextPacketType.ASR,
373
387
  eof: ReceivedTextPacketEof.END,
374
388
  bizId,
@@ -377,10 +391,10 @@ mock.hooks.hook('startRecordAndSendAudioData', context => {
377
391
  }
378
392
  });
379
393
  await mock.sleep(100);
380
- if (!session.currentEvent) {
394
+ if (event.controller.signal.aborted) {
381
395
  return;
382
396
  }
383
- session.replyText(StreamFlag.END);
397
+ event.replyText(StreamFlag.END);
384
398
  if (session.currentEvent && text) {
385
399
  session.currentEvent.data.push({
386
400
  type: 'text',
@@ -392,10 +406,10 @@ mock.hooks.hook('startRecordAndSendAudioData', context => {
392
406
 
393
407
  // 识别 ASR 的延迟
394
408
  await mock.sleep(100);
395
- if (controller.signal.aborted) {
409
+ if (finishController.signal.aborted) {
396
410
  return;
397
411
  }
398
- session.replyText(StreamFlag.START, {
412
+ event.replyText(StreamFlag.START, {
399
413
  bizType: ReceivedTextPacketType.ASR,
400
414
  eof: ReceivedTextPacketEof.CONTINUE,
401
415
  bizId,
@@ -404,15 +418,15 @@ mock.hooks.hook('startRecordAndSendAudioData', context => {
404
418
  }
405
419
  });
406
420
  for (const part of parts) {
407
- if (controller.signal.aborted) {
421
+ if (finishController.signal.aborted) {
408
422
  break;
409
423
  }
410
424
  await mock.sleep(300);
411
- if (controller.signal.aborted) {
425
+ if (finishController.signal.aborted) {
412
426
  break;
413
427
  }
414
428
  text += part;
415
- session.replyText(StreamFlag.IN_PROGRESS, {
429
+ event.replyText(StreamFlag.IN_PROGRESS, {
416
430
  bizType: ReceivedTextPacketType.ASR,
417
431
  eof: ReceivedTextPacketEof.CONTINUE,
418
432
  bizId,
@@ -428,7 +442,7 @@ mock.hooks.hook('stopRecordAndSendAudioData', context => {
428
442
  if (!session.currentEvent) {
429
443
  throw new Error('stopRecordAndSendAudioData event not exists');
430
444
  }
431
- session.currentEvent.asrStatus.controller.abort(new Error('stopRecordAndSendAudioData'));
445
+ session.currentEvent.asr.finishController.abort(new Error('stopRecordAndSendAudioData'));
432
446
  context.result = true;
433
447
  });
434
448
  mock.hooks.hook('startRecordAndSendVideoData', context => {
@@ -6,6 +6,7 @@ export interface SendBlocksToAIStreamParams {
6
6
  blocks: InputBlock[];
7
7
  session: AIStreamSession;
8
8
  attribute?: AIStreamChatAttribute;
9
+ signal?: AbortSignal;
9
10
  }
10
11
  export declare class AIStreamSessionError extends Error {
11
12
  readonly code: number;
@@ -23,7 +23,8 @@ export class AIStreamSessionError extends Error {
23
23
  export function sendBlocksToAIStream(params) {
24
24
  const {
25
25
  session,
26
- blocks
26
+ blocks,
27
+ signal
27
28
  } = params;
28
29
  let audioEmitter = null;
29
30
  let amplitudeCount = 0;
@@ -64,6 +65,11 @@ export function sendBlocksToAIStream(params) {
64
65
  value: JSON.stringify(attribute)
65
66
  }]
66
67
  }));
68
+ if (signal !== null && signal !== void 0 && signal.aborted) {
69
+ canceled = true;
70
+ controller.close();
71
+ return;
72
+ }
67
73
  if (error) {
68
74
  controller.error(error);
69
75
  closed = true;
@@ -237,7 +243,10 @@ export function sendBlocksToAIStream(params) {
237
243
  });
238
244
  audioEmitter.addEventListener('cancel', async () => {
239
245
  await s.stop();
240
- await event.abort();
246
+ if (!canceled) {
247
+ canceled = true;
248
+ await event.abort();
249
+ }
241
250
  resolve();
242
251
  });
243
252
  s.start();
@@ -246,9 +255,11 @@ export function sendBlocksToAIStream(params) {
246
255
  await event.end();
247
256
  },
248
257
  async cancel() {
249
- var _event;
250
- canceled = true;
251
- await ((_event = event) === null || _event === void 0 ? void 0 : _event.abort());
258
+ if (!canceled) {
259
+ var _event;
260
+ canceled = true;
261
+ await ((_event = event) === null || _event === void 0 ? void 0 : _event.abort());
262
+ }
252
263
  }
253
264
  });
254
265
  return {
@@ -16,8 +16,6 @@ export interface AIStreamOptions {
16
16
  };
17
17
  /** 历史消息数量,默认1000,为0的话,则已分页的方式取全部数据 */
18
18
  historySize?: number;
19
- /** 是否支持多模态 */
20
- multiModal?: boolean;
21
19
  /** 是否将输入块传递给智能体,默认为 true,设置为 false 时,需要你自己编写 onInputBlocksPush Hook 来处理输入块 */
22
20
  wireInput?: boolean;
23
21
  /** 索引ID,如果传了 createChatHistoryStore,则会覆盖 */
@@ -30,7 +30,6 @@ export function withAIStream() {
30
30
  onAgentDispose,
31
31
  onTileEvent
32
32
  } = agent;
33
- agent.session.initValue('multiModal', options.multiModal);
34
33
  const ui = agent.plugins.ui;
35
34
  onAgentStart(async () => {
36
35
  /**
@@ -197,6 +196,7 @@ export function withAIStream() {
197
196
  const result = sendBlocksToAIStream({
198
197
  blocks,
199
198
  session: streamSession,
199
+ signal,
200
200
  attribute: _objectSpread({}, extraOptions)
201
201
  });
202
202
  signal === null || signal === void 0 || signal.addEventListener('abort', event => {
@@ -253,7 +253,9 @@ export function withAIStream() {
253
253
  return;
254
254
  }
255
255
  if (!event.detail.text) {
256
- await userMsg.remove();
256
+ if (userMsg.isShow) {
257
+ await userMsg.remove();
258
+ }
257
259
  reject(new Error('No text found in audio event'));
258
260
  return;
259
261
  }
@@ -272,7 +274,9 @@ export function withAIStream() {
272
274
  const onCancel = async () => {
273
275
  end = true;
274
276
  if (!response.started) {
275
- await userMsg.remove();
277
+ if (userMsg.isShow) {
278
+ await userMsg.remove();
279
+ }
276
280
  }
277
281
  audioEmitter.removeEventListener('cancel', onCancel);
278
282
  reject(new Error('User cancel'));
@@ -283,7 +287,9 @@ export function withAIStream() {
283
287
  });
284
288
  const onError = async () => {
285
289
  end = true;
286
- await userMsg.remove();
290
+ if (userMsg.isShow) {
291
+ await userMsg.remove();
292
+ }
287
293
  audioEmitter.removeEventListener('error', onError);
288
294
  reject(new Error('Audio emitter error'));
289
295
  };
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-8",
3
+ "version": "0.2.0-beta-10",
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": "586a5010616634911334e31f9c1e1993231dfb95"
38
+ "gitHead": "a17ba630a9ec71afe6a0fc46c427f664d46acf82"
39
39
  }