@ray-js/t-agent-plugin-aistream 0.2.7 → 0.2.8-beta.2
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/README-zh_CN.md +286 -95
- package/dist/AIStreamTypes.d.ts +55 -18
- package/dist/AIStreamTypes.js +5 -0
- package/dist/asr/AsrAgent.d.ts +3 -3
- package/dist/asr/AsrAgent.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/mcp/McpServer.d.ts +11 -0
- package/dist/mcp/McpServer.js +162 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/types.d.ts +114 -0
- package/dist/mcp/types.js +1 -0
- package/dist/mcp/withMCP.d.ts +15 -0
- package/dist/mcp/withMCP.js +98 -0
- package/dist/polyfill.js +1 -0
- package/dist/utils/AIStream.d.ts +15 -2
- package/dist/utils/AIStream.js +36 -4
- package/dist/utils/defaultMock.js +285 -79
- package/dist/utils/errors.js +3 -1
- package/dist/utils/mock.d.ts +17 -3
- package/dist/utils/object.d.ts +0 -1
- package/dist/utils/object.js +1 -24
- package/dist/utils/observer.d.ts +6 -2
- package/dist/utils/observer.js +17 -6
- package/dist/utils/promisify.d.ts +1 -1
- package/dist/utils/sendMessage.js +9 -7
- package/dist/utils/track.js +2 -2
- package/dist/utils/ttt.d.ts +7 -6
- package/dist/utils/ttt.js +2 -1
- package/dist/withAIStream.d.ts +15 -12
- package/dist/withAIStream.js +12 -6
- package/package.json +5 -3
|
@@ -4,10 +4,11 @@ import "core-js/modules/es.json.stringify.js";
|
|
|
4
4
|
import "core-js/modules/es.regexp.exec.js";
|
|
5
5
|
import "core-js/modules/esnext.iterator.constructor.js";
|
|
6
6
|
import "core-js/modules/esnext.iterator.filter.js";
|
|
7
|
+
import "core-js/modules/esnext.iterator.find.js";
|
|
7
8
|
import "core-js/modules/esnext.iterator.map.js";
|
|
8
9
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
9
10
|
import { mock } from './mock';
|
|
10
|
-
import { EmitterEvent, generateId } from '@ray-js/t-agent';
|
|
11
|
+
import { EmitterEvent, generateId, safeParseJSON } from '@ray-js/t-agent';
|
|
11
12
|
import { AIStreamAppErrorCode, AIStreamServerErrorCode, BizCode, ConnectState, EventType, NetworkType, ReceivedTextPacketEof, ReceivedTextPacketType, SessionState, StreamFlag } from '../AIStreamTypes';
|
|
12
13
|
import AbortController from './abort';
|
|
13
14
|
import { tryCatch } from './misc';
|
|
@@ -17,6 +18,9 @@ function splitString(input) {
|
|
|
17
18
|
}
|
|
18
19
|
mock.data.set('sessionMap', new Map());
|
|
19
20
|
mock.data.set('tokenMap', new Map());
|
|
21
|
+
mock.data.set('mcpRequestId', 1);
|
|
22
|
+
mock.data.set('sentEvents', []);
|
|
23
|
+
mock.data.set('mcpAutoHandshake', true);
|
|
20
24
|
const getSession = (sessionId, eventId) => {
|
|
21
25
|
const connection = getCurrentConnection();
|
|
22
26
|
if (!connection) {
|
|
@@ -131,6 +135,46 @@ const dispatch = (type, detail) => {
|
|
|
131
135
|
detail
|
|
132
136
|
}));
|
|
133
137
|
};
|
|
138
|
+
const scheduleMcpHandshake = session => {
|
|
139
|
+
var _userData$sessionAttr;
|
|
140
|
+
if (!mock.data.get('mcpAutoHandshake')) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const userData = safeParseJSON(session.userDataJson || '');
|
|
144
|
+
if (!(userData !== null && userData !== void 0 && (_userData$sessionAttr = userData.sessionAttributes) !== null && _userData$sessionAttr !== void 0 && _userData$sessionAttr.deviceMcp)) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
if (session.closed) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
dispatch('onEventReceived', {
|
|
152
|
+
sessionId: session.sessionId,
|
|
153
|
+
eventId: generateId(),
|
|
154
|
+
eventType: EventType.MCP_CMD,
|
|
155
|
+
payload: JSON.stringify({
|
|
156
|
+
jsonrpc: '2.0',
|
|
157
|
+
id: 1,
|
|
158
|
+
method: 'initialize',
|
|
159
|
+
params: {}
|
|
160
|
+
})
|
|
161
|
+
});
|
|
162
|
+
}, 0);
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
if (session.closed) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
dispatch('onEventReceived', {
|
|
168
|
+
sessionId: session.sessionId,
|
|
169
|
+
eventId: generateId(),
|
|
170
|
+
eventType: EventType.MCP_CMD,
|
|
171
|
+
payload: JSON.stringify({
|
|
172
|
+
jsonrpc: '2.0',
|
|
173
|
+
method: 'notifications/initialized'
|
|
174
|
+
})
|
|
175
|
+
});
|
|
176
|
+
}, 10);
|
|
177
|
+
};
|
|
134
178
|
mock.hooks.hook('createSession', context => {
|
|
135
179
|
const connection = getCurrentConnection();
|
|
136
180
|
if (!connection) {
|
|
@@ -146,7 +190,8 @@ mock.hooks.hook('createSession', context => {
|
|
|
146
190
|
revDataChannels: ['text', 'audio'],
|
|
147
191
|
currentEvent: null,
|
|
148
192
|
tokenConfig,
|
|
149
|
-
chatId: tokenConfig.chatId
|
|
193
|
+
chatId: tokenConfig.chatId,
|
|
194
|
+
userDataJson: context.options.userDataJson
|
|
150
195
|
};
|
|
151
196
|
map.set(session.sessionId, session);
|
|
152
197
|
context.result = {
|
|
@@ -154,6 +199,7 @@ mock.hooks.hook('createSession', context => {
|
|
|
154
199
|
sendDataChannels: session.sendDataChannels,
|
|
155
200
|
revDataChannels: session.revDataChannels
|
|
156
201
|
};
|
|
202
|
+
scheduleMcpHandshake(session);
|
|
157
203
|
|
|
158
204
|
// 用于测试断开连接;
|
|
159
205
|
// setTimeout(() => {
|
|
@@ -185,6 +231,10 @@ mock.hooks.hook('closeSession', context => {
|
|
|
185
231
|
const map = mock.data.get('sessionMap');
|
|
186
232
|
const sessionId = context.options.sessionId;
|
|
187
233
|
const session = map.get(sessionId);
|
|
234
|
+
if (!session) {
|
|
235
|
+
context.result = true;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
188
238
|
session.closed = true;
|
|
189
239
|
map.delete(sessionId);
|
|
190
240
|
context.result = true;
|
|
@@ -221,7 +271,7 @@ mock.hooks.hook('sendEventStart', async context => {
|
|
|
221
271
|
text: data ? JSON.stringify(data) : ''
|
|
222
272
|
});
|
|
223
273
|
},
|
|
224
|
-
replyEvent: eventType => {
|
|
274
|
+
replyEvent: (eventType, payload) => {
|
|
225
275
|
if (session.closed || event.controller.signal.aborted) {
|
|
226
276
|
return;
|
|
227
277
|
}
|
|
@@ -231,9 +281,12 @@ mock.hooks.hook('sendEventStart', async context => {
|
|
|
231
281
|
dispatch('onEventReceived', {
|
|
232
282
|
eventId,
|
|
233
283
|
sessionId: session.sessionId,
|
|
234
|
-
eventType
|
|
284
|
+
eventType,
|
|
285
|
+
payload
|
|
235
286
|
});
|
|
236
287
|
},
|
|
288
|
+
resolveMcpResponse: () => undefined,
|
|
289
|
+
rejectMcpResponse: () => undefined,
|
|
237
290
|
asr: {
|
|
238
291
|
finishController: new AbortController(),
|
|
239
292
|
promise: Promise.resolve()
|
|
@@ -257,7 +310,12 @@ mock.hooks.hook('unregisterVoiceAmplitudes', context => {
|
|
|
257
310
|
context.result = true;
|
|
258
311
|
});
|
|
259
312
|
mock.hooks.hook('sendEventEnd', async context => {
|
|
260
|
-
const
|
|
313
|
+
const map = mock.data.get('sessionMap');
|
|
314
|
+
const session = map.get(context.options.sessionId);
|
|
315
|
+
if (!(session !== null && session !== void 0 && session.currentEvent) || session.currentEvent.eventId !== context.options.eventId) {
|
|
316
|
+
context.result = true;
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
261
319
|
context.result = true;
|
|
262
320
|
const event = session.currentEvent;
|
|
263
321
|
if (event.eventId !== context.options.eventId) {
|
|
@@ -265,18 +323,184 @@ mock.hooks.hook('sendEventEnd', async context => {
|
|
|
265
323
|
}
|
|
266
324
|
await mock.sleep(100);
|
|
267
325
|
(async () => {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
326
|
+
let streamEnded = false;
|
|
327
|
+
let hasStreamedResponse = false;
|
|
328
|
+
const isEventActive = () => {
|
|
329
|
+
return !streamEnded && !event.controller.signal.aborted && !session.closed && !!session.currentEvent && session.currentEvent.eventId === event.eventId;
|
|
330
|
+
};
|
|
331
|
+
const finishEvent = async function () {
|
|
332
|
+
let delayMs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
333
|
+
if (streamEnded) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
if (delayMs > 0) {
|
|
337
|
+
await mock.sleep(delayMs);
|
|
338
|
+
}
|
|
339
|
+
if (!isEventActive()) {
|
|
340
|
+
var _session$currentEvent;
|
|
341
|
+
streamEnded = true;
|
|
342
|
+
if (((_session$currentEvent = session.currentEvent) === null || _session$currentEvent === void 0 ? void 0 : _session$currentEvent.eventId) === event.eventId) {
|
|
343
|
+
session.currentEvent = null;
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
streamEnded = true;
|
|
271
348
|
event.replyEvent(EventType.EVENT_END);
|
|
272
349
|
session.currentEvent = null;
|
|
350
|
+
};
|
|
351
|
+
await event.asr.promise;
|
|
352
|
+
if (session.tokenConfig.onlyAsr) {
|
|
353
|
+
await finishEvent();
|
|
273
354
|
return;
|
|
274
355
|
}
|
|
356
|
+
event.replyEvent(EventType.EVENT_START);
|
|
275
357
|
const ctx = {
|
|
276
358
|
data: event.data,
|
|
277
359
|
responseText: '',
|
|
278
360
|
wordDelayMs: 100,
|
|
279
|
-
chatId: session.chatId
|
|
361
|
+
chatId: session.chatId,
|
|
362
|
+
writeText: async (text, options) => {
|
|
363
|
+
var _ref, _options$wordDelayMs;
|
|
364
|
+
if (!text) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (options !== null && options !== void 0 && options.delayMs) {
|
|
368
|
+
await mock.sleep(options.delayMs);
|
|
369
|
+
}
|
|
370
|
+
if (!isEventActive()) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const wordDelayMs = (_ref = (_options$wordDelayMs = options === null || options === void 0 ? void 0 : options.wordDelayMs) !== null && _options$wordDelayMs !== void 0 ? _options$wordDelayMs : ctx.wordDelayMs) !== null && _ref !== void 0 ? _ref : 100;
|
|
374
|
+
const words = splitString(text);
|
|
375
|
+
const bizId = generateId();
|
|
376
|
+
hasStreamedResponse = true;
|
|
377
|
+
event.replyText(StreamFlag.START);
|
|
378
|
+
for (let i = 0; i < words.length; i++) {
|
|
379
|
+
const word = words[i];
|
|
380
|
+
if (wordDelayMs > 0) {
|
|
381
|
+
await mock.sleep(wordDelayMs);
|
|
382
|
+
}
|
|
383
|
+
if (!isEventActive()) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
387
|
+
bizType: ReceivedTextPacketType.NLG,
|
|
388
|
+
eof: ReceivedTextPacketEof.CONTINUE,
|
|
389
|
+
bizId,
|
|
390
|
+
data: {
|
|
391
|
+
content: word,
|
|
392
|
+
appendMode: 'append',
|
|
393
|
+
finish: false
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
if (!isEventActive()) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
401
|
+
bizType: ReceivedTextPacketType.NLG,
|
|
402
|
+
eof: ReceivedTextPacketEof.END,
|
|
403
|
+
bizId,
|
|
404
|
+
data: {
|
|
405
|
+
content: '',
|
|
406
|
+
appendMode: 'append',
|
|
407
|
+
finish: true
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
if (!isEventActive()) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
event.replyText(StreamFlag.END);
|
|
414
|
+
},
|
|
415
|
+
writeSkill: async (skill, options) => {
|
|
416
|
+
if (!skill) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (options !== null && options !== void 0 && options.delayMs) {
|
|
420
|
+
await mock.sleep(options.delayMs);
|
|
421
|
+
}
|
|
422
|
+
if (!isEventActive()) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
hasStreamedResponse = true;
|
|
426
|
+
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
427
|
+
bizType: ReceivedTextPacketType.SKILL,
|
|
428
|
+
eof: ReceivedTextPacketEof.END,
|
|
429
|
+
bizId: generateId(),
|
|
430
|
+
data: skill
|
|
431
|
+
});
|
|
432
|
+
},
|
|
433
|
+
end: async options => {
|
|
434
|
+
await finishEvent((options === null || options === void 0 ? void 0 : options.delayMs) || 0);
|
|
435
|
+
},
|
|
436
|
+
callMCPTool: async (toolName, params, options) => {
|
|
437
|
+
if (!isEventActive()) {
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
const requestId = mock.data.get('mcpRequestId') || 1;
|
|
441
|
+
mock.data.set('mcpRequestId', requestId + 1);
|
|
442
|
+
const request = {
|
|
443
|
+
jsonrpc: '2.0',
|
|
444
|
+
id: requestId,
|
|
445
|
+
method: 'tools/call',
|
|
446
|
+
params: {
|
|
447
|
+
name: toolName,
|
|
448
|
+
arguments: params || {}
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
const shouldWaitResponse = typeof (options === null || options === void 0 ? void 0 : options.waitResponse) === 'boolean' ? options.waitResponse : !!(request !== null && request !== void 0 && request.id) && (request === null || request === void 0 ? void 0 : request.method) !== 'notifications/initialized';
|
|
452
|
+
await mock.sleep((options === null || options === void 0 ? void 0 : options.delayMs) || 0);
|
|
453
|
+
if (!isEventActive()) {
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
event.replyEvent(EventType.MCP_CMD, JSON.stringify(request));
|
|
457
|
+
if (!shouldWaitResponse) {
|
|
458
|
+
return undefined;
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
var _result$content2;
|
|
462
|
+
const responsePayload = await new Promise((resolve, reject) => {
|
|
463
|
+
event.resolveMcpResponse = resolve;
|
|
464
|
+
event.rejectMcpResponse = reject;
|
|
465
|
+
});
|
|
466
|
+
event.resolveMcpResponse = () => undefined;
|
|
467
|
+
event.rejectMcpResponse = () => undefined;
|
|
468
|
+
if (!responsePayload) {
|
|
469
|
+
return undefined;
|
|
470
|
+
}
|
|
471
|
+
const response = safeParseJSON(responsePayload);
|
|
472
|
+
if (!response || typeof response !== 'object') {
|
|
473
|
+
throw new Error('Invalid MCP response payload');
|
|
474
|
+
}
|
|
475
|
+
if (response.error) {
|
|
476
|
+
throw new Error(response.error.message || 'MCP tool call failed');
|
|
477
|
+
}
|
|
478
|
+
const result = response.result || {};
|
|
479
|
+
if (result.isError) {
|
|
480
|
+
var _result$content;
|
|
481
|
+
const text = (_result$content = result.content) === null || _result$content === void 0 || (_result$content = _result$content.find(item => (item === null || item === void 0 ? void 0 : item.type) === 'text')) === null || _result$content === void 0 ? void 0 : _result$content.text;
|
|
482
|
+
const parsed = typeof text === 'string' ? safeParseJSON(text) : undefined;
|
|
483
|
+
const message = typeof parsed === 'string' ? parsed : (parsed === null || parsed === void 0 ? void 0 : parsed.message) || (parsed === null || parsed === void 0 ? void 0 : parsed.error) || 'MCP tool call failed';
|
|
484
|
+
throw new Error(message);
|
|
485
|
+
}
|
|
486
|
+
if (result.structuredContent !== undefined) {
|
|
487
|
+
return result.structuredContent;
|
|
488
|
+
}
|
|
489
|
+
const text = (_result$content2 = result.content) === null || _result$content2 === void 0 || (_result$content2 = _result$content2.find(item => (item === null || item === void 0 ? void 0 : item.type) === 'text')) === null || _result$content2 === void 0 ? void 0 : _result$content2.text;
|
|
490
|
+
if (typeof text === 'string') {
|
|
491
|
+
var _safeParseJSON;
|
|
492
|
+
return (_safeParseJSON = safeParseJSON(text)) !== null && _safeParseJSON !== void 0 ? _safeParseJSON : text;
|
|
493
|
+
}
|
|
494
|
+
if (result.content !== undefined) {
|
|
495
|
+
return result.content;
|
|
496
|
+
}
|
|
497
|
+
return result;
|
|
498
|
+
} catch (error) {
|
|
499
|
+
event.resolveMcpResponse = () => undefined;
|
|
500
|
+
event.rejectMcpResponse = () => undefined;
|
|
501
|
+
throw error;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
280
504
|
};
|
|
281
505
|
const [error] = await tryCatch(() => mock.hooks.callHook('sendToAIStream', ctx));
|
|
282
506
|
if (error) {
|
|
@@ -291,98 +515,80 @@ mock.hooks.hook('sendEventEnd', async context => {
|
|
|
291
515
|
return;
|
|
292
516
|
}
|
|
293
517
|
if (ctx.data.length === 0) {
|
|
294
|
-
event.replyEvent(EventType.EVENT_START);
|
|
295
518
|
await mock.sleep(100);
|
|
296
|
-
if (
|
|
519
|
+
if (!isEventActive()) {
|
|
297
520
|
return;
|
|
298
521
|
}
|
|
299
|
-
|
|
300
|
-
session.currentEvent = null;
|
|
522
|
+
await finishEvent();
|
|
301
523
|
return;
|
|
302
524
|
}
|
|
303
|
-
|
|
304
|
-
const wordDelayMs = ctx.wordDelayMs || 100;
|
|
305
|
-
const words = splitString(text);
|
|
306
|
-
if (event.controller.signal.aborted || session.closed) {
|
|
525
|
+
if (streamEnded) {
|
|
307
526
|
return;
|
|
308
527
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
event.replyText(StreamFlag.START);
|
|
316
|
-
for (let i = 0; i < words.length; i++) {
|
|
317
|
-
const word = words[i];
|
|
318
|
-
await mock.sleep(wordDelayMs);
|
|
319
|
-
if (event.controller.signal.aborted || session.closed) {
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
323
|
-
bizType: ReceivedTextPacketType.NLG,
|
|
324
|
-
eof: ReceivedTextPacketEof.CONTINUE,
|
|
325
|
-
bizId,
|
|
326
|
-
data: {
|
|
327
|
-
content: word,
|
|
328
|
-
appendMode: 'append',
|
|
329
|
-
finish: false
|
|
330
|
-
}
|
|
528
|
+
if (!hasStreamedResponse) {
|
|
529
|
+
var _ctx$responseSkills;
|
|
530
|
+
const text = ctx.responseText || '⚠️ No mock text response matched!';
|
|
531
|
+
await mock.sleep(500);
|
|
532
|
+
await ctx.writeText(text, {
|
|
533
|
+
wordDelayMs: ctx.wordDelayMs
|
|
331
534
|
});
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
338
|
-
bizType: ReceivedTextPacketType.NLG,
|
|
339
|
-
eof: ReceivedTextPacketEof.END,
|
|
340
|
-
bizId,
|
|
341
|
-
data: {
|
|
342
|
-
content: '',
|
|
343
|
-
appendMode: 'append',
|
|
344
|
-
finish: true
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
await mock.sleep(10);
|
|
348
|
-
if (event.controller.signal.aborted || session.closed) {
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
if ((_ctx$responseSkills = ctx.responseSkills) !== null && _ctx$responseSkills !== void 0 && _ctx$responseSkills.length) {
|
|
352
|
-
for (let i = 0; i < ctx.responseSkills.length; i++) {
|
|
353
|
-
const skill = ctx.responseSkills[i];
|
|
354
|
-
await mock.sleep(100);
|
|
355
|
-
if (event.controller.signal.aborted || session.closed) {
|
|
356
|
-
return;
|
|
535
|
+
if ((_ctx$responseSkills = ctx.responseSkills) !== null && _ctx$responseSkills !== void 0 && _ctx$responseSkills.length) {
|
|
536
|
+
for (let i = 0; i < ctx.responseSkills.length; i++) {
|
|
537
|
+
await ctx.writeSkill(ctx.responseSkills[i], {
|
|
538
|
+
delayMs: 100
|
|
539
|
+
});
|
|
357
540
|
}
|
|
358
|
-
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
359
|
-
bizType: ReceivedTextPacketType.SKILL,
|
|
360
|
-
eof: ReceivedTextPacketEof.END,
|
|
361
|
-
bizId,
|
|
362
|
-
data: skill
|
|
363
|
-
});
|
|
364
541
|
}
|
|
365
542
|
}
|
|
366
|
-
|
|
367
|
-
await mock.sleep(500);
|
|
368
|
-
if (event.controller.signal.aborted || session.closed) {
|
|
543
|
+
if (streamEnded || !isEventActive()) {
|
|
369
544
|
return;
|
|
370
545
|
}
|
|
371
|
-
|
|
372
|
-
session.currentEvent = null;
|
|
546
|
+
await finishEvent(500);
|
|
373
547
|
})();
|
|
374
548
|
});
|
|
375
549
|
mock.hooks.hook('sendEventPayloadEnd', async context => {
|
|
376
|
-
|
|
550
|
+
const map = mock.data.get('sessionMap');
|
|
551
|
+
const session = map.get(context.options.sessionId);
|
|
552
|
+
if (!session || !session.currentEvent || session.currentEvent.eventId !== context.options.eventId) {
|
|
553
|
+
context.result = true;
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
377
556
|
context.result = true;
|
|
378
557
|
await mock.sleep(100);
|
|
379
558
|
});
|
|
380
559
|
mock.hooks.hook('sendEventChatBreak', async context => {
|
|
381
|
-
const
|
|
560
|
+
const map = mock.data.get('sessionMap');
|
|
561
|
+
const session = map.get(context.options.sessionId);
|
|
562
|
+
if (!(session !== null && session !== void 0 && session.currentEvent) || session.currentEvent.eventId !== context.options.eventId) {
|
|
563
|
+
context.result = true;
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
session.currentEvent.rejectMcpResponse(new Error('sendEventChatBreak'));
|
|
382
567
|
session.currentEvent.controller.abort(new Error('sendEventChatBreak'));
|
|
383
568
|
session.currentEvent = null;
|
|
384
569
|
context.result = true;
|
|
385
570
|
});
|
|
571
|
+
mock.hooks.hook('sendEvent', async context => {
|
|
572
|
+
var _context$options;
|
|
573
|
+
if (((_context$options = context.options) === null || _context$options === void 0 ? void 0 : _context$options.eventType) !== EventType.MCP_CMD) {
|
|
574
|
+
context.result = true;
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const session = getSession(context.options.sessionId);
|
|
578
|
+
if (session.currentEvent && session.currentEvent.eventId === context.options.eventId) {
|
|
579
|
+
session.currentEvent.resolveMcpResponse(context.options.payload);
|
|
580
|
+
} else {
|
|
581
|
+
const sentEvents = mock.data.get('sentEvents') || [];
|
|
582
|
+
sentEvents.push({
|
|
583
|
+
sessionId: context.options.sessionId,
|
|
584
|
+
eventId: context.options.eventId,
|
|
585
|
+
eventType: context.options.eventType,
|
|
586
|
+
payload: context.options.payload
|
|
587
|
+
});
|
|
588
|
+
mock.data.set('sentEvents', sentEvents);
|
|
589
|
+
}
|
|
590
|
+
context.result = true;
|
|
591
|
+
});
|
|
386
592
|
mock.hooks.hook('startRecordAndSendAudioData', async context => {
|
|
387
593
|
const session = getSession(context.options.sessionId);
|
|
388
594
|
if (!session.currentEvent) {
|
|
@@ -584,10 +790,10 @@ mock.hooks.hook('queryRecordList', context => {
|
|
|
584
790
|
mock.hooks.hook('deleteRecordList', context => {
|
|
585
791
|
const options = context.options;
|
|
586
792
|
const records = filterRecords(options);
|
|
587
|
-
records.map(
|
|
793
|
+
records.map(_ref2 => {
|
|
588
794
|
let {
|
|
589
795
|
id
|
|
590
|
-
} =
|
|
796
|
+
} = _ref2;
|
|
591
797
|
return mock.deleteRecord(id);
|
|
592
798
|
});
|
|
593
799
|
context.result = true;
|
package/dist/utils/errors.js
CHANGED
|
@@ -96,9 +96,11 @@ export async function tryCatchTTT(fn) {
|
|
|
96
96
|
code,
|
|
97
97
|
message
|
|
98
98
|
} = error;
|
|
99
|
-
|
|
99
|
+
const originalCode = code;
|
|
100
|
+
message = "".concat(message, " (original_code: ").concat(originalCode || '-', ")");
|
|
100
101
|
code = transformErrorCode(code);
|
|
101
102
|
const e = removeTopStackFrame(new AIStreamError(message, code));
|
|
103
|
+
e.originalCode = originalCode;
|
|
102
104
|
return [e, null];
|
|
103
105
|
}
|
|
104
106
|
return [null, result];
|
package/dist/utils/mock.d.ts
CHANGED
|
@@ -16,6 +16,20 @@ export interface SendToAIStreamContext {
|
|
|
16
16
|
responseText: string | undefined;
|
|
17
17
|
wordDelayMs: number;
|
|
18
18
|
responseSkills?: ReceivedTextSkillPacketBody[];
|
|
19
|
+
writeText: (text: string, options?: {
|
|
20
|
+
delayMs?: number;
|
|
21
|
+
wordDelayMs?: number;
|
|
22
|
+
}) => Promise<void>;
|
|
23
|
+
writeSkill: (skill: ReceivedTextSkillPacketBody, options?: {
|
|
24
|
+
delayMs?: number;
|
|
25
|
+
}) => Promise<void>;
|
|
26
|
+
end: (options?: {
|
|
27
|
+
delayMs?: number;
|
|
28
|
+
}) => Promise<void>;
|
|
29
|
+
callMCPTool: (toolName: string, params?: Record<string, any>, options?: {
|
|
30
|
+
delayMs?: number;
|
|
31
|
+
waitResponse?: boolean;
|
|
32
|
+
}) => Promise<any>;
|
|
19
33
|
}
|
|
20
34
|
interface AsrDetectionContext {
|
|
21
35
|
responseText: string | undefined;
|
|
@@ -40,9 +54,9 @@ declare const mock: {
|
|
|
40
54
|
emitter: Emitter;
|
|
41
55
|
hooks: import("hookable").Hookable<MockHooks, string>;
|
|
42
56
|
getRecords: <T = any>() => Row<T>[];
|
|
43
|
-
getRecord: <
|
|
44
|
-
setRecord: <
|
|
45
|
-
updateRecord: <
|
|
57
|
+
getRecord: <T = any>(id: number) => Row<T> | undefined;
|
|
58
|
+
setRecord: <T = any>(entry: Row<T>) => void;
|
|
59
|
+
updateRecord: <T = any>(entry: Row<T>) => void;
|
|
46
60
|
deleteRecord: (id: number) => void;
|
|
47
61
|
clearRecords: () => void;
|
|
48
62
|
getId: () => number;
|
package/dist/utils/object.d.ts
CHANGED
package/dist/utils/object.js
CHANGED
|
@@ -22,27 +22,4 @@ export const createResolvable = () => {
|
|
|
22
22
|
};
|
|
23
23
|
});
|
|
24
24
|
return ret;
|
|
25
|
-
};
|
|
26
|
-
export function deepMerge(target, source) {
|
|
27
|
-
if (typeof target !== 'object' || target === null) {
|
|
28
|
-
return source; // 如果目标不是对象,直接返回来源
|
|
29
|
-
}
|
|
30
|
-
if (typeof source !== 'object' || source === null) {
|
|
31
|
-
return target; // 如果来源不是对象,返回目标
|
|
32
|
-
}
|
|
33
|
-
for (const key in source) {
|
|
34
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
35
|
-
const sourceValue = source[key];
|
|
36
|
-
const targetValue = target[key];
|
|
37
|
-
|
|
38
|
-
// 如果是对象,递归合并
|
|
39
|
-
if (typeof sourceValue === 'object' && sourceValue !== null) {
|
|
40
|
-
target[key] = deepMerge(Array.isArray(targetValue) ? [] : targetValue || {}, sourceValue);
|
|
41
|
-
} else {
|
|
42
|
-
// 否则直接赋值
|
|
43
|
-
target[key] = sourceValue;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return target;
|
|
48
|
-
}
|
|
25
|
+
};
|
package/dist/utils/observer.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AudioBody, ConnectStateBody, EventBody, FileBody, ImageBody, SessionStateBody, TextBody, VideoBody } from '../AIStreamTypes';
|
|
1
|
+
import { AudioBody, ConnectStateBody, EventBody, FileBody, ImageBody, SessionEventBody, SessionStateBody, TextBody, VideoBody } from '../AIStreamTypes';
|
|
2
2
|
export type AIStreamDataEntry = {
|
|
3
3
|
type: 'text';
|
|
4
4
|
body: TextBody;
|
|
@@ -23,10 +23,14 @@ export type AIStreamDataEntry = {
|
|
|
23
23
|
} | {
|
|
24
24
|
type: 'sessionState';
|
|
25
25
|
body: SessionStateBody;
|
|
26
|
+
} | {
|
|
27
|
+
type: 'sessionEvent';
|
|
28
|
+
body: SessionEventBody;
|
|
26
29
|
};
|
|
27
30
|
export interface AIStreamObserverOptions {
|
|
28
|
-
sessionState?: boolean;
|
|
29
31
|
connectionState?: boolean;
|
|
32
|
+
sessionState?: boolean;
|
|
33
|
+
sessionEvent?: boolean;
|
|
30
34
|
event?: boolean;
|
|
31
35
|
text?: boolean;
|
|
32
36
|
audio?: boolean;
|
package/dist/utils/observer.js
CHANGED
|
@@ -5,7 +5,7 @@ import "core-js/modules/web.dom-collections.iterator.js";
|
|
|
5
5
|
import { ConnectState, EventType, SessionState, StreamFlag } from '../AIStreamTypes';
|
|
6
6
|
import { listenAudioReceived, listenConnectStateChanged, listenEventReceived, listenImageReceived, listenSessionStateChanged, listenTextReceived } from './ttt';
|
|
7
7
|
import logger from './logger';
|
|
8
|
-
const types = ['connectionState', 'sessionState', 'event', 'text', 'audio', 'video', 'file', 'image'];
|
|
8
|
+
const types = ['connectionState', 'sessionState', 'sessionEvent', 'event', 'text', 'audio', 'video', 'file', 'image'];
|
|
9
9
|
export class AIStreamObserverPool {
|
|
10
10
|
constructor() {
|
|
11
11
|
_defineProperty(this, "isStarted", false);
|
|
@@ -13,6 +13,7 @@ export class AIStreamObserverPool {
|
|
|
13
13
|
this.observerMap = {
|
|
14
14
|
connectionState: new Set(),
|
|
15
15
|
sessionState: new Set(),
|
|
16
|
+
sessionEvent: new Set(),
|
|
16
17
|
event: new Set(),
|
|
17
18
|
text: new Set(),
|
|
18
19
|
audio: new Set(),
|
|
@@ -33,7 +34,7 @@ export class AIStreamObserverPool {
|
|
|
33
34
|
state = "".concat(ConnectState[entry.body.connectState], " ").concat(entry.body.code || '');
|
|
34
35
|
} else if (entry.type === 'sessionState') {
|
|
35
36
|
state = "".concat(SessionState[entry.body.sessionState], " ").concat(entry.body.code || '');
|
|
36
|
-
} else if (entry.type === 'event') {
|
|
37
|
+
} else if (entry.type === 'event' || entry.type === 'sessionEvent') {
|
|
37
38
|
state = "".concat(EventType[entry.body.eventType]);
|
|
38
39
|
} else if ((_entry$body = entry.body) !== null && _entry$body !== void 0 && _entry$body.streamFlag) {
|
|
39
40
|
state = "".concat(StreamFlag[entry.body.streamFlag]);
|
|
@@ -66,10 +67,19 @@ export class AIStreamObserverPool {
|
|
|
66
67
|
})), listenSessionStateChanged(body => handle({
|
|
67
68
|
type: 'sessionState',
|
|
68
69
|
body
|
|
69
|
-
})), listenEventReceived(body =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
})), listenEventReceived(body => {
|
|
71
|
+
if (body.eventType === EventType.MCP_CMD) {
|
|
72
|
+
handle({
|
|
73
|
+
type: 'sessionEvent',
|
|
74
|
+
body
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
handle({
|
|
78
|
+
type: 'event',
|
|
79
|
+
body
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}), listenTextReceived(body => handle({
|
|
73
83
|
type: 'text',
|
|
74
84
|
body
|
|
75
85
|
})), listenAudioReceived(body => handle({
|
|
@@ -91,6 +101,7 @@ export class AIStreamObserverPool {
|
|
|
91
101
|
this.observerMap = {
|
|
92
102
|
connectionState: new Set(),
|
|
93
103
|
sessionState: new Set(),
|
|
104
|
+
sessionEvent: new Set(),
|
|
94
105
|
event: new Set(),
|
|
95
106
|
text: new Set(),
|
|
96
107
|
audio: new Set(),
|
|
@@ -13,6 +13,6 @@ interface AsyncTTTFnParams<P> {
|
|
|
13
13
|
}
|
|
14
14
|
export declare const getEnableMock: () => boolean;
|
|
15
15
|
export declare const setEnableMock: (enable: boolean) => boolean;
|
|
16
|
-
export declare function promisify<T extends AsyncTTTFnParams<any>>(fn: (options: any) => void, enableMock?: boolean): (options?: Omit<T,
|
|
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]>;
|
|
17
17
|
export declare function listening<T>(on: (listener: (params: T) => void) => void, off: (listener: (params: T) => void) => void, enableMock?: boolean): (listener: (params: T) => void) => () => void;
|
|
18
18
|
export {};
|