@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.
@@ -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: number;
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
+ };
@@ -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
  }
@@ -96,9 +96,8 @@ export class AsrAgent {
96
96
  return this.streamConn;
97
97
  }
98
98
  async createSession() {
99
- var _this$activeSession;
100
- // 如果有激活的 Session,直接返回
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,6 @@
1
1
  import { ChatAgent } from '@ray-js/t-agent';
2
2
  interface WithBuildInOptions {
3
+ audioAutoPlay?: boolean;
3
4
  }
4
5
  export declare function withBuildIn(options?: WithBuildInOptions): (_agent: ChatAgent) => {};
5
6
  export {};
@@ -1,5 +1,12 @@
1
- import { BuildInSkillCode, ReceivedSmartHomeSkillAction } from '../AIStreamTypes';
2
- export function withBuildIn() {
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
- // onAgentStart(async () => {
19
- // const agentId = session.get('AIStream.agentId');
20
- // if (agentId) {
21
- // const _conf = session.get('AIStream.projectConfig');
22
- // if (!_conf) {
23
- // try {
24
- // const config: Record<string, ProjectConfig> = (await getMiniAppConfig({})).config || {};
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
@@ -1,4 +1,5 @@
1
1
  import "core-js/modules/web.queue-microtask.js";
2
+ /* istanbul ignore file */
2
3
  // @ts-ignore
3
4
  globalThis.queueMicrotask = callback => {
4
5
  Promise.resolve().then(callback).catch(err => {
@@ -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
- private activeSessions;
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 | undefined;
86
+ chatId?: string;
74
87
  }>;
75
88
  private listeners;
76
89
  constructor(connection: AIStreamConnection, pool: AIStreamObserverPool, options: AIStreamSessionOptions);
@@ -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, "activeSessions", new Set());
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.activeSessions.forEach(session => {
42
- if (session.sessionId) {
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
- this.activeSessions.forEach(session => {
52
- if (session.sessionId && session.sessionId === entry.body.sessionId) {
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
- this.activeSessions.forEach(session => {
58
- if (session.sessionId && session.sessionId === entry.body.sessionId) {
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.activeSessions.forEach(s => s.cleanup());
134
- // session 不清空,closeSession 被调用时才清空
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.activeSessions.add(session);
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.activeSessions.delete(session);
190
+ this.unregisterSession(session);
191
+ this.allSessions.delete(session);
192
+ await this.client.disposeIfIdle(this);
149
193
  }
150
-
151
- // 断连
152
194
  async close() {
153
- if (this.connectionId) {
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 = _objectSpread(_objectSpread({
287
+ const options = {
252
288
  bizTag: BizTag.DEFAULT,
253
- ownerId
254
- }, this.options), {}, {
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.code === AIStreamAppErrorCode.RECONNECT) {
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 = [];