@ray-js/t-agent-plugin-aistream 0.2.8-beta.1 → 0.2.8-beta.3
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 +70 -11
- package/README.md +916 -706
- package/dist/AIStreamTypes.d.ts +36 -17
- package/dist/asr/AsrAgent.d.ts +4 -4
- package/dist/asr/AsrAgent.js +8 -5
- package/dist/buildIn/withBuildIn.d.ts +1 -0
- package/dist/buildIn/withBuildIn.js +162 -25
- package/dist/polyfill.js +1 -0
- package/dist/utils/AIStream.d.ts +16 -3
- package/dist/utils/AIStream.js +84 -41
- package/dist/utils/defaultMock.js +90 -3
- package/dist/utils/errors.js +3 -1
- package/dist/utils/mock.d.ts +18 -3
- package/dist/utils/observer.d.ts +6 -0
- package/dist/utils/observer.js +100 -32
- package/dist/utils/promisify.d.ts +1 -1
- package/dist/utils/sendMessage.d.ts +0 -1
- package/dist/utils/sendMessage.js +26 -13
- package/dist/utils/ttt.d.ts +5 -4
- package/dist/utils/ttt.js +2 -0
- package/dist/withAIStream.d.ts +22 -14
- package/dist/withAIStream.js +94 -58
- package/package.json +5 -4
package/dist/AIStreamTypes.d.ts
CHANGED
|
@@ -692,18 +692,18 @@ export declare enum AIStreamErrorCode {
|
|
|
692
692
|
ASR_EMPTY = "asr-empty"
|
|
693
693
|
}
|
|
694
694
|
export declare enum AIStreamAppErrorCode {
|
|
695
|
-
GENERIC_ERROR = 39001
|
|
696
|
-
INVALID_PARAMS = 39002
|
|
697
|
-
HTTP_REQUEST_FAILED = 39003
|
|
698
|
-
CONNECTION_INVALID = 39004
|
|
699
|
-
SESSION_ID_INVALID = 39005
|
|
700
|
-
EVENT_ID_INVALID = 39006
|
|
701
|
-
DATA_CHANNEL_NOT_FOUND = 39007
|
|
702
|
-
INVALID_DATA_PACKET = 39008
|
|
703
|
-
FILE_DATA_READ_ERROR = 39009
|
|
704
|
-
DATA_SEND_FAILED = 39010
|
|
705
|
-
CONNECTION_CLOSED_BY_REMOTE = 39012
|
|
706
|
-
AUDIO_UNEXPECTEDLY_RESET = 39014
|
|
695
|
+
GENERIC_ERROR = 39001,// 通用错误,用于一些杂项错误
|
|
696
|
+
INVALID_PARAMS = 39002,// 参数不合法,详见备注
|
|
697
|
+
HTTP_REQUEST_FAILED = 39003,// HTTP 请求失败
|
|
698
|
+
CONNECTION_INVALID = 39004,// Connection 未连接
|
|
699
|
+
SESSION_ID_INVALID = 39005,// SessionId 不存在
|
|
700
|
+
EVENT_ID_INVALID = 39006,// EventId 为空
|
|
701
|
+
DATA_CHANNEL_NOT_FOUND = 39007,// DataChannel 不存在
|
|
702
|
+
INVALID_DATA_PACKET = 39008,// 数据包不合法
|
|
703
|
+
FILE_DATA_READ_ERROR = 39009,// 文件数据读取异常
|
|
704
|
+
DATA_SEND_FAILED = 39010,// 发送数据失败
|
|
705
|
+
CONNECTION_CLOSED_BY_REMOTE = 39012,// Connection 被远端关闭
|
|
706
|
+
AUDIO_UNEXPECTEDLY_RESET = 39014,// 音频组件被重置
|
|
707
707
|
RECONNECT = 39016
|
|
708
708
|
}
|
|
709
709
|
export declare enum AIStreamServerErrorCode {
|
|
@@ -846,7 +846,7 @@ export type AudioBody = {
|
|
|
846
846
|
/** 接收数据通道 Code */
|
|
847
847
|
dataChannel: string;
|
|
848
848
|
/** 数据流类型: 0-仅一包, 1-传输开始, 2-传输中, 3-传输结束 */
|
|
849
|
-
streamFlag:
|
|
849
|
+
streamFlag: StreamFlag;
|
|
850
850
|
/** 音频缓存路径 */
|
|
851
851
|
path: string;
|
|
852
852
|
/** SessionId 列表, 云端返回,可能为空 */
|
|
@@ -1031,7 +1031,7 @@ export type RecordAmplitudesBody = {
|
|
|
1031
1031
|
amplitudes: number[];
|
|
1032
1032
|
};
|
|
1033
1033
|
export declare enum NetworkType {
|
|
1034
|
-
NONE = "none"
|
|
1034
|
+
NONE = "none",// 无网络
|
|
1035
1035
|
CELL_2G = "2g",
|
|
1036
1036
|
CELL_3G = "3g",
|
|
1037
1037
|
CELL_4G = "4g",
|
|
@@ -1199,6 +1199,10 @@ export type CreateSessionParams = {
|
|
|
1199
1199
|
* 音频视频图片文件都只有一路时,例如 bizCode 65537,接收只有 文本、音频 ["text", "audio"]
|
|
1200
1200
|
*/
|
|
1201
1201
|
revDataChannels: string[];
|
|
1202
|
+
/**
|
|
1203
|
+
* 图片或文件的基本 url
|
|
1204
|
+
*/
|
|
1205
|
+
baseCacheDir: string;
|
|
1202
1206
|
}) => void;
|
|
1203
1207
|
fail?: (params: {
|
|
1204
1208
|
errorMsg: string;
|
|
@@ -1703,8 +1707,8 @@ export type InsertRecordParams = {
|
|
|
1703
1707
|
complete?: () => void;
|
|
1704
1708
|
};
|
|
1705
1709
|
export declare enum ReceivedTextPacketType {
|
|
1706
|
-
ASR = "ASR"
|
|
1707
|
-
SKILL = "SKILL"
|
|
1710
|
+
ASR = "ASR",// ASR 识别的文本
|
|
1711
|
+
SKILL = "SKILL",// 数据
|
|
1708
1712
|
NLG = "NLG"
|
|
1709
1713
|
}
|
|
1710
1714
|
export declare enum ReceivedTextPacketEof {
|
|
@@ -1731,7 +1735,7 @@ export type ReceivedTextNlgPacket = ReceivedTextPacketBase<ReceivedTextPacketTyp
|
|
|
1731
1735
|
finish: boolean;
|
|
1732
1736
|
}>;
|
|
1733
1737
|
export declare enum BuildInSkillCode {
|
|
1734
|
-
SEARCH_KNOWLEDGE = "searchKnowledge"
|
|
1738
|
+
SEARCH_KNOWLEDGE = "searchKnowledge",// 知识库搜索
|
|
1735
1739
|
SMART_HOME = "smart_home"
|
|
1736
1740
|
}
|
|
1737
1741
|
export type ReceivedTextSkillPacketBody<Code = BuildInSkillCode | string, G = any, C = any> = {
|
|
@@ -1784,3 +1788,18 @@ export type ReceivedSmartHomeSkill = ReceivedTextSkillPacketBody<BuildInSkillCod
|
|
|
1784
1788
|
};
|
|
1785
1789
|
action: ReceivedSmartHomeSkillAction;
|
|
1786
1790
|
}, any>;
|
|
1791
|
+
export type ReceivedAudioAttachment = {
|
|
1792
|
+
streamFlag: StreamFlag;
|
|
1793
|
+
path: string;
|
|
1794
|
+
pts: number;
|
|
1795
|
+
codecType: number;
|
|
1796
|
+
sampleRate: number;
|
|
1797
|
+
channels: number;
|
|
1798
|
+
bitDepth: number;
|
|
1799
|
+
};
|
|
1800
|
+
export type ReceivedImageAttachment = {
|
|
1801
|
+
streamFlag: StreamFlag;
|
|
1802
|
+
path: string;
|
|
1803
|
+
width: number;
|
|
1804
|
+
height: number;
|
|
1805
|
+
};
|
package/dist/asr/AsrAgent.d.ts
CHANGED
|
@@ -36,9 +36,9 @@ export interface AsrAgentOptions {
|
|
|
36
36
|
eventIdPrefix?: string;
|
|
37
37
|
}
|
|
38
38
|
export declare enum AsrAgentStatus {
|
|
39
|
-
PENDING = "pending"
|
|
40
|
-
RECORDING = "recording"
|
|
41
|
-
ASR = "asr"
|
|
39
|
+
PENDING = "pending",// 等待开始
|
|
40
|
+
RECORDING = "recording",// 录音中
|
|
41
|
+
ASR = "asr",// ASR 处理中
|
|
42
42
|
ABORTING = "aborting"
|
|
43
43
|
}
|
|
44
44
|
export declare class AsrAgent {
|
|
@@ -73,5 +73,5 @@ export declare class AsrAgent {
|
|
|
73
73
|
start(): Promise<void>;
|
|
74
74
|
stop(): Promise<void>;
|
|
75
75
|
abort(): Promise<void>;
|
|
76
|
-
dispose(): void
|
|
76
|
+
dispose(): Promise<void>;
|
|
77
77
|
}
|
package/dist/asr/AsrAgent.js
CHANGED
|
@@ -96,9 +96,8 @@ export class AsrAgent {
|
|
|
96
96
|
return this.streamConn;
|
|
97
97
|
}
|
|
98
98
|
async createSession() {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if ((_this$activeSession = this.activeSession) !== null && _this$activeSession !== void 0 && _this$activeSession.sessionId) {
|
|
99
|
+
// 连接断开后 sessionId 会被清空,但同一个 session 对象仍然可以重新 ensureSession。
|
|
100
|
+
if (this.activeSession && !this.activeSession.disposed) {
|
|
102
101
|
return this.activeSession;
|
|
103
102
|
}
|
|
104
103
|
const {
|
|
@@ -339,7 +338,7 @@ export class AsrAgent {
|
|
|
339
338
|
}
|
|
340
339
|
return this._abort();
|
|
341
340
|
}
|
|
342
|
-
dispose() {
|
|
341
|
+
async dispose() {
|
|
343
342
|
if (this.recordDurationTimer) {
|
|
344
343
|
clearTimeout(this.recordDurationTimer);
|
|
345
344
|
this.recordDurationTimer = null;
|
|
@@ -347,8 +346,12 @@ export class AsrAgent {
|
|
|
347
346
|
if (this.disposed) {
|
|
348
347
|
return;
|
|
349
348
|
}
|
|
349
|
+
this.disposed = true;
|
|
350
350
|
if (this.activeSession) {
|
|
351
|
-
this.activeSession.close();
|
|
351
|
+
await this.activeSession.close();
|
|
352
|
+
}
|
|
353
|
+
if (this.streamConn) {
|
|
354
|
+
await this.streamConn.close();
|
|
352
355
|
}
|
|
353
356
|
this.audioStream = null;
|
|
354
357
|
this.activeEvent = null;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import "core-js/modules/esnext.iterator.constructor.js";
|
|
2
|
+
import "core-js/modules/esnext.iterator.for-each.js";
|
|
3
|
+
import "core-js/modules/web.dom-collections.iterator.js";
|
|
4
|
+
import { AudioPlayChangedState, BuildInSkillCode, ReceivedSmartHomeSkillAction, StreamFlag } from '../AIStreamTypes';
|
|
5
|
+
import { listenAudioPlayChanged, startPlayAudio, stopPlayAudio } from '../utils';
|
|
6
|
+
|
|
7
|
+
/** 用于驱动 BubbleToolTile 播放的音频参数 */
|
|
8
|
+
|
|
9
|
+
export function withBuildIn(options) {
|
|
3
10
|
return _agent => {
|
|
4
11
|
if (!_agent.plugins.aiStream || !_agent.plugins.ui) {
|
|
5
12
|
throw new Error('withBuildIn must be used after withAIStream and withUI');
|
|
@@ -12,30 +19,18 @@ export function withBuildIn() {
|
|
|
12
19
|
createMessage
|
|
13
20
|
} = agent;
|
|
14
21
|
const {
|
|
15
|
-
onSkillsEnd
|
|
22
|
+
onSkillsEnd,
|
|
23
|
+
onAttachmentsEnd,
|
|
24
|
+
onAttachmentCompose
|
|
16
25
|
} = agent.plugins.aiStream;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// const projectConfig = config[agentId];
|
|
26
|
-
// if (projectConfig) {
|
|
27
|
-
// logger.debug('getMiniAppConfig projectConfig', {
|
|
28
|
-
// agentId,
|
|
29
|
-
// projectConfig,
|
|
30
|
-
// });
|
|
31
|
-
// await session.set('AIAssistant.projectConfig', projectConfig);
|
|
32
|
-
// }
|
|
33
|
-
// } catch (error) {
|
|
34
|
-
// logger.warn('getMiniAppConfig error', error);
|
|
35
|
-
// }
|
|
36
|
-
// }
|
|
37
|
-
// }
|
|
38
|
-
// });
|
|
26
|
+
onAgentStart(async () => {
|
|
27
|
+
// 是否开启自动播放
|
|
28
|
+
await session.set('AIStream.audioAutoPlay', (options === null || options === void 0 ? void 0 : options.audioAutoPlay) || false);
|
|
29
|
+
// 是否正在播放
|
|
30
|
+
await session.set('AIStream.audioPlaying', false);
|
|
31
|
+
// 正在播放的消息 id
|
|
32
|
+
await session.set('AIStream.playingMessageId', null);
|
|
33
|
+
});
|
|
39
34
|
|
|
40
35
|
// 关联文档
|
|
41
36
|
|
|
@@ -155,6 +150,148 @@ export function withBuildIn() {
|
|
|
155
150
|
}
|
|
156
151
|
});
|
|
157
152
|
})();
|
|
153
|
+
// 图片
|
|
154
|
+
(() => {
|
|
155
|
+
onAttachmentsEnd((parts, respMsg) => {
|
|
156
|
+
const streamSession = session.get('AIStream.streamSession');
|
|
157
|
+
const prefix = streamSession.baseCacheDir;
|
|
158
|
+
parts.forEach(i => {
|
|
159
|
+
if (i.attachmentType === 'image') {
|
|
160
|
+
const d = i.attachment;
|
|
161
|
+
if (d.streamFlag === StreamFlag.ONLY_ONE || d.streamFlag === StreamFlag.END) {
|
|
162
|
+
respMsg.addTile('image', {
|
|
163
|
+
src: "".concat(prefix).concat(d.path)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
})();
|
|
170
|
+
// 语音
|
|
171
|
+
(() => {
|
|
172
|
+
// 当前正在播放的消息 id 保存在 session 中,保证同一时刻只播一条;
|
|
173
|
+
// 组件里的 playing 状态从 session 的 audioPlaying / playingMessageId 派生。
|
|
174
|
+
// playing 是纯运行时状态,不落到 tile.data(避免被持久化)。
|
|
175
|
+
|
|
176
|
+
const setPlayingMessage = async messageId => {
|
|
177
|
+
await session.set('AIStream.playingMessageId', messageId);
|
|
178
|
+
await session.set('AIStream.audioPlaying', !!messageId);
|
|
179
|
+
};
|
|
180
|
+
const playAudio = audio => {
|
|
181
|
+
return startPlayAudio({
|
|
182
|
+
path: audio.path,
|
|
183
|
+
codecType: audio.codecType,
|
|
184
|
+
sampleRate: audio.sampleRate,
|
|
185
|
+
channels: audio.channels,
|
|
186
|
+
bitDepth: audio.bitDepth,
|
|
187
|
+
pts: audio.pts
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
const stopCurrent = async () => {
|
|
191
|
+
if (!session.get('AIStream.playingMessageId')) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
await stopPlayAudio();
|
|
195
|
+
await setPlayingMessage(null);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// 允许 UI 主动停止当前播放(如顶部“播放中”按钮)
|
|
199
|
+
agent.plugins.ui.onEvent('audioPlayStop', () => {
|
|
200
|
+
stopCurrent();
|
|
201
|
+
});
|
|
202
|
+
onAttachmentCompose((part, respMsg) => {
|
|
203
|
+
if (part.attachmentType === 'audio') {
|
|
204
|
+
const audio = part.attachment;
|
|
205
|
+
if (audio.streamFlag === StreamFlag.START) {
|
|
206
|
+
const audioAutoPlay = !!session.get('AIStream.audioAutoPlay');
|
|
207
|
+
if (audioAutoPlay) {
|
|
208
|
+
var _respMsg$id;
|
|
209
|
+
startPlayAudio(audio);
|
|
210
|
+
setPlayingMessage((_respMsg$id = respMsg === null || respMsg === void 0 ? void 0 : respMsg.id) !== null && _respMsg$id !== void 0 ? _respMsg$id : null);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// 音频包结束时,往气泡里插入一个语音播放按钮(BubbleToolTile)。
|
|
217
|
+
// 只持久化播放所需的音频参数,不含 playing 状态。
|
|
218
|
+
//
|
|
219
|
+
// 注意:音频按流式分多包下发,格式头(codecType/sampleRate/channels/bitDepth)
|
|
220
|
+
// 只在 START 包里携带,END 包通常只带最终的 path/pts。因此不能只取 END 包,
|
|
221
|
+
// 需要按音频流 id(part.id 在同一段音频的所有分包间一致)聚合,
|
|
222
|
+
// 把 START 包的格式参数与 END 包的 path 合并后再建 tile,
|
|
223
|
+
// 否则持久化后点击播放时格式参数会全部丢失,只剩 path。
|
|
224
|
+
onAttachmentsEnd((parts, respMsg) => {
|
|
225
|
+
const audioMap = new Map();
|
|
226
|
+
parts.forEach(i => {
|
|
227
|
+
var _audio$path, _audio$pts, _audio$codecType, _audio$sampleRate, _audio$channels, _audio$bitDepth;
|
|
228
|
+
if (i.attachmentType !== 'audio') {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const audio = i.attachment;
|
|
232
|
+
const entry = audioMap.get(i.id) || {
|
|
233
|
+
audio: {},
|
|
234
|
+
complete: false
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// 合并各分包:后到的非空字段覆盖,空字段保留先前值(END 包不会用 undefined 覆盖掉格式参数)
|
|
238
|
+
entry.audio = {
|
|
239
|
+
path: (_audio$path = audio.path) !== null && _audio$path !== void 0 ? _audio$path : entry.audio.path,
|
|
240
|
+
pts: (_audio$pts = audio.pts) !== null && _audio$pts !== void 0 ? _audio$pts : entry.audio.pts,
|
|
241
|
+
codecType: (_audio$codecType = audio.codecType) !== null && _audio$codecType !== void 0 ? _audio$codecType : entry.audio.codecType,
|
|
242
|
+
sampleRate: (_audio$sampleRate = audio.sampleRate) !== null && _audio$sampleRate !== void 0 ? _audio$sampleRate : entry.audio.sampleRate,
|
|
243
|
+
channels: (_audio$channels = audio.channels) !== null && _audio$channels !== void 0 ? _audio$channels : entry.audio.channels,
|
|
244
|
+
bitDepth: (_audio$bitDepth = audio.bitDepth) !== null && _audio$bitDepth !== void 0 ? _audio$bitDepth : entry.audio.bitDepth
|
|
245
|
+
};
|
|
246
|
+
if (audio.streamFlag === StreamFlag.ONLY_ONE || audio.streamFlag === StreamFlag.END) {
|
|
247
|
+
entry.complete = true;
|
|
248
|
+
// END 包的 path 是最终完整文件,确保覆盖
|
|
249
|
+
if (audio.path) {
|
|
250
|
+
entry.audio.path = audio.path;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
audioMap.set(i.id, entry);
|
|
254
|
+
});
|
|
255
|
+
audioMap.forEach(_ref => {
|
|
256
|
+
let {
|
|
257
|
+
audio,
|
|
258
|
+
complete
|
|
259
|
+
} = _ref;
|
|
260
|
+
if (complete) {
|
|
261
|
+
respMsg.bubble.addTile('bubbleTool', {
|
|
262
|
+
audio
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// 播放结束 / 异常时,复位全局播放态
|
|
269
|
+
listenAudioPlayChanged(body => {
|
|
270
|
+
if (body.state === AudioPlayChangedState.COMPLETED || body.state === AudioPlayChangedState.ERROR) {
|
|
271
|
+
setPlayingMessage(null);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// 处理 UI 发来的播放/停止指令
|
|
276
|
+
onTileEvent(async (tile, payload) => {
|
|
277
|
+
var _tile$data;
|
|
278
|
+
if (tile.type !== 'bubbleTool' || (payload === null || payload === void 0 ? void 0 : payload.action) !== 'toggle') {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const audio = (_tile$data = tile.data) === null || _tile$data === void 0 ? void 0 : _tile$data.audio;
|
|
282
|
+
const messageId = tile.message.id;
|
|
283
|
+
const wasPlayingThis = session.get('AIStream.playingMessageId') === messageId;
|
|
284
|
+
|
|
285
|
+
// 先停掉当前正在播放的(无论是不是同一条)
|
|
286
|
+
await stopCurrent();
|
|
287
|
+
|
|
288
|
+
// 点击的是非播放态的按钮 -> 开始播放
|
|
289
|
+
if (!wasPlayingThis && audio) {
|
|
290
|
+
await playAudio(audio);
|
|
291
|
+
await setPlayingMessage(messageId);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
})();
|
|
158
295
|
return {};
|
|
159
296
|
};
|
|
160
297
|
}
|
package/dist/polyfill.js
CHANGED
package/dist/utils/AIStream.d.ts
CHANGED
|
@@ -12,19 +12,31 @@ export declare class AIStreamClient {
|
|
|
12
12
|
pool: AIStreamObserverPool;
|
|
13
13
|
private cached;
|
|
14
14
|
getConnection(options: AIStreamConnectionOptions): AIStreamConnection;
|
|
15
|
+
releaseConnection(connection: AIStreamConnection): Promise<void>;
|
|
16
|
+
disposeIfIdle(connection: AIStreamConnection): Promise<void>;
|
|
15
17
|
}
|
|
16
18
|
export declare class AIStreamConnection {
|
|
17
19
|
private readonly pool;
|
|
20
|
+
private readonly client;
|
|
21
|
+
readonly cacheKey: string;
|
|
18
22
|
readonly options: AIStreamConnectionOptions;
|
|
19
23
|
private connectionId;
|
|
20
24
|
state: ConnectState;
|
|
21
|
-
|
|
25
|
+
refCount: number;
|
|
26
|
+
private allSessions;
|
|
27
|
+
private sessionsById;
|
|
22
28
|
private promise;
|
|
23
29
|
private observer;
|
|
24
|
-
constructor(pool: AIStreamObserverPool, options: AIStreamConnectionOptions);
|
|
30
|
+
constructor(pool: AIStreamObserverPool, client: AIStreamClient, cacheKey: string, options: AIStreamConnectionOptions);
|
|
31
|
+
retain(): void;
|
|
32
|
+
release(): void;
|
|
25
33
|
_ensureConnected(): Promise<void>;
|
|
26
34
|
private onStateChanged;
|
|
27
35
|
private cleanup;
|
|
36
|
+
registerSession(session: AIStreamSession, sessionId: string): void;
|
|
37
|
+
unregisterSession(session: AIStreamSession): void;
|
|
38
|
+
hasActiveSessions(): boolean;
|
|
39
|
+
disposeTransport(): Promise<void>;
|
|
28
40
|
createSession(options: AIStreamSessionOptions): AIStreamSession;
|
|
29
41
|
closeSession(session: AIStreamSession): Promise<void>;
|
|
30
42
|
close(): Promise<void>;
|
|
@@ -64,13 +76,14 @@ export declare class AIStreamSession {
|
|
|
64
76
|
sessionId: string | null;
|
|
65
77
|
sendDataChannels: string[];
|
|
66
78
|
revDataChannels: string[];
|
|
79
|
+
baseCacheDir: string;
|
|
67
80
|
disposed: boolean;
|
|
68
81
|
private activeEvent?;
|
|
69
82
|
private activeObserver?;
|
|
70
83
|
private promise;
|
|
71
84
|
tokenExtParamsResolvable: import("./object").Resolvable<{
|
|
72
85
|
[key: string]: any;
|
|
73
|
-
chatId?: string
|
|
86
|
+
chatId?: string;
|
|
74
87
|
}>;
|
|
75
88
|
private listeners;
|
|
76
89
|
constructor(connection: AIStreamConnection, pool: AIStreamObserverPool, options: AIStreamSessionOptions);
|
package/dist/utils/AIStream.js
CHANGED
|
@@ -22,48 +22,67 @@ export class AIStreamClient {
|
|
|
22
22
|
const key = "".concat(options.clientType, "|").concat(options.deviceId || '');
|
|
23
23
|
let connection = this.cached.get(key);
|
|
24
24
|
if (!connection) {
|
|
25
|
-
connection = new AIStreamConnection(this.pool, options);
|
|
25
|
+
connection = new AIStreamConnection(this.pool, this, key, options);
|
|
26
|
+
this.cached.set(key, connection);
|
|
26
27
|
}
|
|
28
|
+
connection.retain();
|
|
27
29
|
return connection;
|
|
28
30
|
}
|
|
31
|
+
async releaseConnection(connection) {
|
|
32
|
+
connection.release();
|
|
33
|
+
await this.disposeIfIdle(connection);
|
|
34
|
+
}
|
|
35
|
+
async disposeIfIdle(connection) {
|
|
36
|
+
if (connection.refCount > 0 || connection.hasActiveSessions()) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const cached = this.cached.get(connection.cacheKey);
|
|
40
|
+
if (cached === connection) {
|
|
41
|
+
this.cached.delete(connection.cacheKey);
|
|
42
|
+
}
|
|
43
|
+
await connection.disposeTransport();
|
|
44
|
+
}
|
|
29
45
|
}
|
|
30
46
|
export class AIStreamConnection {
|
|
31
|
-
constructor(pool, options) {
|
|
47
|
+
constructor(pool, client, cacheKey, options) {
|
|
32
48
|
_defineProperty(this, "connectionId", null);
|
|
33
49
|
_defineProperty(this, "state", ConnectState.INIT);
|
|
34
|
-
_defineProperty(this, "
|
|
50
|
+
_defineProperty(this, "refCount", 0);
|
|
51
|
+
_defineProperty(this, "allSessions", new Set());
|
|
52
|
+
_defineProperty(this, "sessionsById", new Map());
|
|
35
53
|
_defineProperty(this, "promise", null);
|
|
36
54
|
_defineProperty(this, "observer", null);
|
|
37
55
|
_defineProperty(this, "onStateChanged", entry => {
|
|
38
|
-
// 一个小程序只能启动一个 connection,所以这里不判断 id
|
|
39
56
|
if (entry.type === 'connectionState') {
|
|
40
57
|
this.state = entry.body.connectState;
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
session._onStateChanged(entry);
|
|
44
|
-
}
|
|
58
|
+
this.allSessions.forEach(session => {
|
|
59
|
+
session._onStateChanged(entry);
|
|
45
60
|
});
|
|
46
61
|
if (entry.body.connectState === ConnectState.DISCONNECTED || entry.body.connectState === ConnectState.CLOSED || entry.body.connectState === ConnectState.CONNECTING || entry.body.connectState === ConnectState.AUTHORIZING) {
|
|
47
62
|
// 断开事件触发时只做清理,因为连接已经关掉了
|
|
48
63
|
this.cleanup();
|
|
49
64
|
}
|
|
50
65
|
} else if (entry.type === 'sessionState') {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
session._onStateChanged(entry);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
66
|
+
var _this$sessionsById$ge;
|
|
67
|
+
(_this$sessionsById$ge = this.sessionsById.get(entry.body.sessionId)) === null || _this$sessionsById$ge === void 0 || _this$sessionsById$ge._onStateChanged(entry);
|
|
56
68
|
} else if (entry.type === 'sessionEvent') {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
session._onSessionEvent(entry);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
69
|
+
var _this$sessionsById$ge2;
|
|
70
|
+
(_this$sessionsById$ge2 = this.sessionsById.get(entry.body.sessionId)) === null || _this$sessionsById$ge2 === void 0 || _this$sessionsById$ge2._onSessionEvent(entry);
|
|
62
71
|
}
|
|
63
72
|
});
|
|
64
73
|
this.pool = pool;
|
|
74
|
+
this.client = client;
|
|
75
|
+
this.cacheKey = cacheKey;
|
|
65
76
|
this.options = options;
|
|
66
77
|
}
|
|
78
|
+
retain() {
|
|
79
|
+
this.refCount += 1;
|
|
80
|
+
}
|
|
81
|
+
release() {
|
|
82
|
+
if (this.refCount > 0) {
|
|
83
|
+
this.refCount -= 1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
67
86
|
_ensureConnected() {
|
|
68
87
|
if (this.promise) {
|
|
69
88
|
return this.promise;
|
|
@@ -130,12 +149,35 @@ export class AIStreamConnection {
|
|
|
130
149
|
this.state = ConnectState.CLOSED;
|
|
131
150
|
this.connectionId = null;
|
|
132
151
|
this.promise = null;
|
|
133
|
-
this.
|
|
134
|
-
|
|
152
|
+
this.sessionsById.clear();
|
|
153
|
+
this.allSessions.forEach(session => session.cleanup());
|
|
154
|
+
}
|
|
155
|
+
registerSession(session, sessionId) {
|
|
156
|
+
this.sessionsById.set(sessionId, session);
|
|
157
|
+
}
|
|
158
|
+
unregisterSession(session) {
|
|
159
|
+
if (session.sessionId) {
|
|
160
|
+
const current = this.sessionsById.get(session.sessionId);
|
|
161
|
+
if (current === session) {
|
|
162
|
+
this.sessionsById.delete(session.sessionId);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
hasActiveSessions() {
|
|
167
|
+
return this.allSessions.size > 0;
|
|
168
|
+
}
|
|
169
|
+
async disposeTransport() {
|
|
170
|
+
const connectionId = this.connectionId;
|
|
171
|
+
this.cleanup();
|
|
172
|
+
if (connectionId && this.options.clientType === ConnectClientType.DEVICE) {
|
|
173
|
+
await disconnect({
|
|
174
|
+
connectionId
|
|
175
|
+
});
|
|
176
|
+
}
|
|
135
177
|
}
|
|
136
178
|
createSession(options) {
|
|
137
179
|
const session = new AIStreamSession(this, this.pool, options);
|
|
138
|
-
this.
|
|
180
|
+
this.allSessions.add(session);
|
|
139
181
|
return session;
|
|
140
182
|
}
|
|
141
183
|
async closeSession(session) {
|
|
@@ -145,28 +187,22 @@ export class AIStreamConnection {
|
|
|
145
187
|
code: AIStreamServerErrorCode.OK
|
|
146
188
|
});
|
|
147
189
|
}
|
|
148
|
-
this.
|
|
190
|
+
this.unregisterSession(session);
|
|
191
|
+
this.allSessions.delete(session);
|
|
192
|
+
await this.client.disposeIfIdle(this);
|
|
149
193
|
}
|
|
150
|
-
|
|
151
|
-
// 断连
|
|
152
194
|
async close() {
|
|
153
|
-
|
|
154
|
-
// 只有设备端才需要断开连接,App 端由 App 来维持
|
|
155
|
-
if (this.options.clientType === ConnectClientType.DEVICE) {
|
|
156
|
-
await disconnect({
|
|
157
|
-
connectionId: this.connectionId
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
this.state = ConnectState.CLOSED;
|
|
161
|
-
this.cleanup();
|
|
162
|
-
}
|
|
195
|
+
await this.client.releaseConnection(this);
|
|
163
196
|
}
|
|
164
197
|
}
|
|
198
|
+
// 这是为了防止代码扫描出来
|
|
199
|
+
const prefix = 'https://';
|
|
165
200
|
export class AIStreamSession {
|
|
166
201
|
constructor(connection, pool, options) {
|
|
167
202
|
_defineProperty(this, "sessionId", null);
|
|
168
203
|
_defineProperty(this, "sendDataChannels", []);
|
|
169
204
|
_defineProperty(this, "revDataChannels", []);
|
|
205
|
+
_defineProperty(this, "baseCacheDir", "".concat(prefix, "thing.miniapp.com/__tmp__/"));
|
|
170
206
|
_defineProperty(this, "disposed", false);
|
|
171
207
|
_defineProperty(this, "promise", null);
|
|
172
208
|
_defineProperty(this, "tokenExtParamsResolvable", createResolvable());
|
|
@@ -248,15 +284,17 @@ export class AIStreamSession {
|
|
|
248
284
|
var _await$this$tokenExtP;
|
|
249
285
|
chatId = (_await$this$tokenExtP = await this.tokenExtParamsResolvable.promise) === null || _await$this$tokenExtP === void 0 ? void 0 : _await$this$tokenExtP.chatId;
|
|
250
286
|
}
|
|
251
|
-
const options =
|
|
287
|
+
const options = {
|
|
252
288
|
bizTag: BizTag.DEFAULT,
|
|
253
|
-
ownerId
|
|
254
|
-
|
|
289
|
+
ownerId,
|
|
290
|
+
solutionCode: this.options.solutionCode,
|
|
291
|
+
api: this.options.api,
|
|
292
|
+
apiVersion: this.options.apiVersion,
|
|
255
293
|
extParams: _objectSpread(_objectSpread({}, this.options.extParams), {}, {
|
|
256
294
|
// chatId 之前可能是 undefined,这里要覆盖掉
|
|
257
295
|
chatId
|
|
258
296
|
})
|
|
259
|
-
}
|
|
297
|
+
};
|
|
260
298
|
const [error, result] = await tryCatchTTT(() => queryAgentToken(options));
|
|
261
299
|
if (error) {
|
|
262
300
|
this.promise = null;
|
|
@@ -274,8 +312,8 @@ export class AIStreamSession {
|
|
|
274
312
|
this.tokenExtParamsResolvable.resolve({});
|
|
275
313
|
}
|
|
276
314
|
let userData = {};
|
|
277
|
-
if (options.getSessionUserData) {
|
|
278
|
-
userData = await options.getSessionUserData();
|
|
315
|
+
if (this.options.getSessionUserData) {
|
|
316
|
+
userData = await this.options.getSessionUserData();
|
|
279
317
|
}
|
|
280
318
|
let finErr;
|
|
281
319
|
for (let i = 0; i < 30; i++) {
|
|
@@ -287,7 +325,7 @@ export class AIStreamSession {
|
|
|
287
325
|
}));
|
|
288
326
|
if (err) {
|
|
289
327
|
finErr = err;
|
|
290
|
-
if (err.
|
|
328
|
+
if (err.originalCode === AIStreamAppErrorCode.RECONNECT) {
|
|
291
329
|
// 200 毫秒后再试
|
|
292
330
|
await new Promise(resolve => {
|
|
293
331
|
setTimeout(resolve, 200);
|
|
@@ -302,6 +340,10 @@ export class AIStreamSession {
|
|
|
302
340
|
this.sessionId = res.sessionId;
|
|
303
341
|
this.sendDataChannels = res.sendDataChannels;
|
|
304
342
|
this.revDataChannels = res.revDataChannels;
|
|
343
|
+
if (res.baseCacheDir) {
|
|
344
|
+
this.baseCacheDir = res.baseCacheDir;
|
|
345
|
+
}
|
|
346
|
+
this.connection.registerSession(this, res.sessionId);
|
|
305
347
|
this.promise = null;
|
|
306
348
|
return;
|
|
307
349
|
}
|
|
@@ -396,6 +438,7 @@ export class AIStreamSession {
|
|
|
396
438
|
cleanup() {
|
|
397
439
|
logger.debug('AIStreamSession cleanup');
|
|
398
440
|
this.cleanupEvent();
|
|
441
|
+
this.connection.unregisterSession(this);
|
|
399
442
|
this.sessionId = null;
|
|
400
443
|
this.sendDataChannels = [];
|
|
401
444
|
this.revDataChannels = [];
|