@ray-js/t-agent-plugin-aistream 0.2.7-beta.12 → 0.2.7-beta.13
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/utils/sendMessage.js +46 -13
- package/dist/withAIStream.js +55 -12
- package/package.json +2 -2
|
@@ -42,6 +42,7 @@ export function sendBlocksToAIStream(params) {
|
|
|
42
42
|
});
|
|
43
43
|
const stream = new ReadableStream({
|
|
44
44
|
async start(controller) {
|
|
45
|
+
var _cleanupPreEventListe2;
|
|
45
46
|
const enqueue = part => {
|
|
46
47
|
if (signal !== null && signal !== void 0 && signal.aborted || canceled || closed) {
|
|
47
48
|
return;
|
|
@@ -84,22 +85,28 @@ export function sendBlocksToAIStream(params) {
|
|
|
84
85
|
});
|
|
85
86
|
};
|
|
86
87
|
let pendingCancel = false;
|
|
88
|
+
// phase-1: 事件创建前监听 confirm/cancel,用于捕获"边界竞态"时的取消意图
|
|
89
|
+
// 使用具名函数以便在 startEvent 后统一解绑,避免监听器残留在 audioEmitter 上
|
|
90
|
+
let cleanupPreEventListeners;
|
|
87
91
|
if (audioEmitter) {
|
|
88
|
-
|
|
89
|
-
// 在确认发送时,如果事件还没创建,则取消发送
|
|
92
|
+
const onPreEventConfirm = () => {
|
|
90
93
|
if (!event) {
|
|
91
94
|
logger.debug('sendBlocksToAIStream audioEmitter confirm before event start');
|
|
92
|
-
// event 留到后面再关
|
|
93
95
|
pendingCancel = true;
|
|
94
96
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
+
};
|
|
98
|
+
const onPreEventCancel = () => {
|
|
97
99
|
if (!event) {
|
|
98
100
|
logger.debug('sendBlocksToAIStream audioEmitter cancel before event start');
|
|
99
|
-
// event 留到后面再关
|
|
100
101
|
pendingCancel = true;
|
|
101
102
|
}
|
|
102
|
-
}
|
|
103
|
+
};
|
|
104
|
+
audioEmitter.addEventListener('confirm', onPreEventConfirm);
|
|
105
|
+
audioEmitter.addEventListener('cancel', onPreEventCancel);
|
|
106
|
+
cleanupPreEventListeners = () => {
|
|
107
|
+
audioEmitter.removeEventListener('confirm', onPreEventConfirm);
|
|
108
|
+
audioEmitter.removeEventListener('cancel', onPreEventCancel);
|
|
109
|
+
};
|
|
103
110
|
}
|
|
104
111
|
const chatAttributes = {
|
|
105
112
|
'processing.interrupt': 'false',
|
|
@@ -124,7 +131,10 @@ export function sendBlocksToAIStream(params) {
|
|
|
124
131
|
}]
|
|
125
132
|
}));
|
|
126
133
|
if (error) {
|
|
134
|
+
var _cleanupPreEventListe;
|
|
127
135
|
emitError(error);
|
|
136
|
+
// startEvent 失败,phase-1 监听已无用,立即解绑
|
|
137
|
+
(_cleanupPreEventListe = cleanupPreEventListeners) === null || _cleanupPreEventListe === void 0 || _cleanupPreEventListe();
|
|
128
138
|
if (audioEmitter) {
|
|
129
139
|
audioEmitter.dispatchEvent(new EmitterEvent('error', {
|
|
130
140
|
detail: error
|
|
@@ -133,6 +143,9 @@ export function sendBlocksToAIStream(params) {
|
|
|
133
143
|
close();
|
|
134
144
|
return;
|
|
135
145
|
}
|
|
146
|
+
|
|
147
|
+
// startEvent 已成功(event 已创建),phase-1 监听完成使命,解绑
|
|
148
|
+
(_cleanupPreEventListe2 = cleanupPreEventListeners) === null || _cleanupPreEventListe2 === void 0 || _cleanupPreEventListe2();
|
|
136
149
|
if (signal !== null && signal !== void 0 && signal.aborted || pendingCancel) {
|
|
137
150
|
logger.debug('sendBlocksToAIStream pendingCancel aborted');
|
|
138
151
|
abort();
|
|
@@ -306,20 +319,40 @@ export function sendBlocksToAIStream(params) {
|
|
|
306
319
|
const s = event.stream({
|
|
307
320
|
type: 'audio'
|
|
308
321
|
});
|
|
309
|
-
|
|
322
|
+
let audioDone = false;
|
|
323
|
+
|
|
324
|
+
// cleanupPostEventListeners 先初始化为 noop,让两个 handler 可在闭包里安全引用
|
|
325
|
+
// 后面赋值为真正的清理函数
|
|
326
|
+
let cleanupPostEventListeners = () => {};
|
|
327
|
+
const onPostEventConfirm = async () => {
|
|
328
|
+
// 任一监听触发后,立即同时解绑两者,防止另一个因偶发二次触发产生副作用
|
|
329
|
+
cleanupPostEventListeners();
|
|
310
330
|
if (!canceled) {
|
|
311
331
|
await s.stop();
|
|
312
332
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
333
|
+
if (!audioDone) {
|
|
334
|
+
audioDone = true;
|
|
335
|
+
resolve();
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
const onPostEventCancel = async () => {
|
|
339
|
+
cleanupPostEventListeners();
|
|
316
340
|
if (!canceled) {
|
|
317
341
|
await s.stop();
|
|
318
342
|
}
|
|
319
343
|
logger.debug('sendBlocksToAIStream audio cancel aborted');
|
|
320
344
|
abort();
|
|
321
|
-
|
|
322
|
-
|
|
345
|
+
if (!audioDone) {
|
|
346
|
+
audioDone = true;
|
|
347
|
+
resolve();
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
cleanupPostEventListeners = () => {
|
|
351
|
+
audioEmitter.removeEventListener('confirm', onPostEventConfirm);
|
|
352
|
+
audioEmitter.removeEventListener('cancel', onPostEventCancel);
|
|
353
|
+
};
|
|
354
|
+
audioEmitter.addEventListener('confirm', onPostEventConfirm);
|
|
355
|
+
audioEmitter.addEventListener('cancel', onPostEventCancel);
|
|
323
356
|
s.start();
|
|
324
357
|
});
|
|
325
358
|
if (signal !== null && signal !== void 0 && signal.aborted) {
|
package/dist/withAIStream.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
|
2
2
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
3
|
+
import _wrapAsyncGenerator from "@babel/runtime/helpers/esm/wrapAsyncGenerator";
|
|
4
|
+
import _awaitAsyncGenerator from "@babel/runtime/helpers/esm/awaitAsyncGenerator";
|
|
5
|
+
import _asyncGeneratorDelegate from "@babel/runtime/helpers/esm/asyncGeneratorDelegate";
|
|
3
6
|
const _excluded = ["message"];
|
|
7
|
+
import "core-js/modules/es.symbol.description.js";
|
|
8
|
+
import "core-js/modules/es.symbol.async-iterator.js";
|
|
4
9
|
import "core-js/modules/es.array.flat.js";
|
|
5
10
|
import "core-js/modules/es.array.reverse.js";
|
|
6
11
|
import "core-js/modules/es.array.unscopables.flat.js";
|
|
7
12
|
import "core-js/modules/esnext.iterator.constructor.js";
|
|
8
13
|
import "core-js/modules/esnext.iterator.map.js";
|
|
9
14
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
15
|
+
function _asyncIterator(r) { var n, t, o, e = 2; for ("undefined" != typeof Symbol && (t = Symbol.asyncIterator, o = Symbol.iterator); e--;) { if (t && null != (n = r[t])) return n.call(r); if (o && null != (n = r[o])) return new AsyncFromSyncIterator(n.call(r)); t = "@@asyncIterator", o = "@@iterator"; } throw new TypeError("Object is not async iterable"); }
|
|
16
|
+
function AsyncFromSyncIterator(r) { function AsyncFromSyncIteratorContinuation(r) { if (Object(r) !== r) return Promise.reject(new TypeError(r + " is not an object.")); var n = r.done; return Promise.resolve(r.value).then(function (r) { return { value: r, done: n }; }); } return AsyncFromSyncIterator = function (r) { this.s = r, this.n = r.next; }, AsyncFromSyncIterator.prototype = { s: null, n: null, next: function () { return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments)); }, return: function (r) { var n = this.s.return; return void 0 === n ? Promise.resolve({ value: r, done: !0 }) : AsyncFromSyncIteratorContinuation(n.apply(this.s, arguments)); }, throw: function (r) { var n = this.s.return; return void 0 === n ? Promise.reject(r) : AsyncFromSyncIteratorContinuation(n.apply(this.s, arguments)); } }, new AsyncFromSyncIterator(r); }
|
|
10
17
|
import { BubbleTileStatus, ChatMessageStatus, createHooks, EmitterEvent } from '@ray-js/t-agent';
|
|
11
18
|
import { messageAppraise } from './utils/apis';
|
|
12
19
|
import { getAccountInfo, getCurrentHomeInfo, runTTTAction, sendBlocksToAIStream } from './utils';
|
|
@@ -254,13 +261,37 @@ export function withAIStream() {
|
|
|
254
261
|
return userData;
|
|
255
262
|
}
|
|
256
263
|
});
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
result.response.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
if (signal) {
|
|
265
|
+
const onAbort = event => {
|
|
266
|
+
logger.debug('withAIStream signal aborted, response.started:', result.response.started);
|
|
267
|
+
if (result.response.started) {
|
|
268
|
+
result.response.cancel(event.reason);
|
|
269
|
+
}
|
|
270
|
+
agent.hooks.callHook('onUserAbort', event.reason);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// once:true 保证 abort 触发后自动解绑,避免监听器残留
|
|
274
|
+
signal.addEventListener('abort', onAbort, {
|
|
275
|
+
once: true
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// 正常消费流时(未触发 abort)在 parts() 的 finally 里解绑,防止 signal 长期持有闭包
|
|
279
|
+
const _parts = result.response.parts.bind(result.response);
|
|
280
|
+
result.response.parts = () => {
|
|
281
|
+
const iterable = _parts();
|
|
282
|
+
return {
|
|
283
|
+
[Symbol.asyncIterator]() {
|
|
284
|
+
return _wrapAsyncGenerator(function* () {
|
|
285
|
+
try {
|
|
286
|
+
yield* _asyncGeneratorDelegate(_asyncIterator(iterable), _awaitAsyncGenerator);
|
|
287
|
+
} finally {
|
|
288
|
+
signal.removeEventListener('abort', onAbort);
|
|
289
|
+
}
|
|
290
|
+
})();
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
};
|
|
294
|
+
}
|
|
264
295
|
signal === null || signal === void 0 || signal.throwIfAborted();
|
|
265
296
|
return result;
|
|
266
297
|
};
|
|
@@ -291,8 +322,18 @@ export function withAIStream() {
|
|
|
291
322
|
if (audioEmitter) {
|
|
292
323
|
let end = false;
|
|
293
324
|
audioPromise = new Promise((resolve, reject) => {
|
|
294
|
-
// 当确认发送时,展示 loading
|
|
295
325
|
let userMsgShow = false;
|
|
326
|
+
|
|
327
|
+
// onAbort 先定义,让后续终态处理器可引用它来解绑
|
|
328
|
+
const onAbort = () => {
|
|
329
|
+
logger.debug('withAIStream chat agent.chat audioEmitter onAbort');
|
|
330
|
+
audioEmitter.dispatchEvent(new EmitterEvent('cancel'));
|
|
331
|
+
};
|
|
332
|
+
// signal 可选:仅在传入时注册
|
|
333
|
+
signal === null || signal === void 0 || signal.addEventListener('abort', onAbort);
|
|
334
|
+
|
|
335
|
+
// 当确认发送时,展示 loading
|
|
336
|
+
|
|
296
337
|
audioEmitter.addEventListener('confirm', async () => {
|
|
297
338
|
logger.debug('withAIStream chat agent.chat audioEmitter onConfirm');
|
|
298
339
|
if (end) {
|
|
@@ -316,6 +357,8 @@ export function withAIStream() {
|
|
|
316
357
|
return;
|
|
317
358
|
}
|
|
318
359
|
end = true;
|
|
360
|
+
// 终态:解绑 signal 上的 abort 监听,避免 signal 长期持有本轮闭包
|
|
361
|
+
signal === null || signal === void 0 || signal.removeEventListener('abort', onAbort);
|
|
319
362
|
if (!event.detail.text && userMsgShow) {
|
|
320
363
|
await userMsg.remove();
|
|
321
364
|
reject(new Error('No text found in audio event'));
|
|
@@ -335,6 +378,8 @@ export function withAIStream() {
|
|
|
335
378
|
audioEmitter.addEventListener('cancel', async () => {
|
|
336
379
|
logger.debug('withAIStream chat agent.chat audioEmitter onCancel');
|
|
337
380
|
end = true;
|
|
381
|
+
// 终态:解绑 signal 上的 abort 监听
|
|
382
|
+
signal === null || signal === void 0 || signal.removeEventListener('abort', onAbort);
|
|
338
383
|
// 取消时,有可能 userMsg.persist 还在执行,所以这里不 await,先 reject
|
|
339
384
|
reject(new Error('User cancel'));
|
|
340
385
|
if (!response.started && userMsgShow) {
|
|
@@ -343,13 +388,11 @@ export function withAIStream() {
|
|
|
343
388
|
}, {
|
|
344
389
|
once: true
|
|
345
390
|
});
|
|
346
|
-
signal.addEventListener('abort', () => {
|
|
347
|
-
logger.debug('withAIStream chat agent.chat audioEmitter onAbort');
|
|
348
|
-
audioEmitter.dispatchEvent(new EmitterEvent('cancel'));
|
|
349
|
-
});
|
|
350
391
|
audioEmitter.addEventListener('error', async () => {
|
|
351
392
|
logger.debug('withAIStream chat agent.chat audioEmitter onError');
|
|
352
393
|
end = true;
|
|
394
|
+
// 终态:解绑 signal 上的 abort 监听
|
|
395
|
+
signal === null || signal === void 0 || signal.removeEventListener('abort', onAbort);
|
|
353
396
|
reject(new Error('Audio emitter error'));
|
|
354
397
|
if (userMsgShow && userMsg.status !== ChatMessageStatus.FINISH) {
|
|
355
398
|
await userMsg.remove();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ray-js/t-agent-plugin-aistream",
|
|
3
|
-
"version": "0.2.7-beta.
|
|
3
|
+
"version": "0.2.7-beta.13",
|
|
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": "8355bb1b4ce4849a1628cd59005b16d2b85fff62"
|
|
39
39
|
}
|