@ray-js/t-agent-plugin-aistream 0.2.3-beta-1 → 0.2.3-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.
@@ -1241,7 +1241,9 @@ export type StopRecordAndSendAudioDataParams = {
1241
1241
  dataChannel?: string;
1242
1242
  /** 扩展属性 */
1243
1243
  userData?: Attribute[];
1244
- success?: (params: null) => void;
1244
+ success?: (params: {
1245
+ filePath: string;
1246
+ } | null) => void;
1245
1247
  fail?: (params: {
1246
1248
  errorMsg: string;
1247
1249
  errorCode: string | number;
@@ -1342,6 +1344,18 @@ export type SendTextDataParams = {
1342
1344
  }) => void;
1343
1345
  complete?: () => void;
1344
1346
  };
1347
+ export type InitAudioRecorderParams = {
1348
+ success?: (params: null) => void;
1349
+ fail?: (params: {
1350
+ errorMsg: string;
1351
+ errorCode: string | number;
1352
+ innerError: {
1353
+ errorCode: string | number;
1354
+ errorMsg: string;
1355
+ };
1356
+ }) => void;
1357
+ complete?: () => void;
1358
+ };
1345
1359
  export type SendFileDataParams = {
1346
1360
  /** 会话 id */
1347
1361
  sessionId: string;
@@ -0,0 +1,57 @@
1
+ import { ConnectClientType } from '../AIStreamTypes';
2
+ export interface AsrAgentOptions {
3
+ agentId: string;
4
+ /** 获取 agent token 的参数 */
5
+ tokenOptions?: {
6
+ api: string;
7
+ version: string;
8
+ extParams?: Record<string, any>;
9
+ };
10
+ /** 是否在构造时就初始化连接和录音参数 */
11
+ earlyStart?: boolean;
12
+ homeId?: number;
13
+ clientType?: ConnectClientType;
14
+ deviceId?: string;
15
+ onMessage?: (message: {
16
+ text: string;
17
+ } | {
18
+ filePath: string;
19
+ }) => void;
20
+ onFinish?: () => void;
21
+ onError?: (error: any) => void;
22
+ /** 主动中断或者超过录制时长,会调用onAbort */
23
+ onAbort?: () => void;
24
+ recordingOptions?: {
25
+ /** 是否需要保存音频 */
26
+ needSave?: boolean;
27
+ /** 保存的音频采样率,单位:hz */
28
+ sampleRate?: number;
29
+ /** 最长录制时长,单位:毫秒 */
30
+ maxDuration?: number;
31
+ };
32
+ enableLog?: boolean;
33
+ }
34
+ export declare class AsrAgent {
35
+ /** 数据链接,一个agent保持一个链接就可以 */
36
+ private streamConn;
37
+ /** 当前请求的session */
38
+ private activeSession;
39
+ private activeEvent;
40
+ /** 音频流监听 */
41
+ private audioStream;
42
+ options: AsrAgentOptions;
43
+ /** 录音时长定时器 */
44
+ private recordDurationTimer;
45
+ constructor(options: AsrAgentOptions);
46
+ /** 获取录音权限 */
47
+ getRecordScope(): Promise<boolean>;
48
+ /** 获取数据链接,一般只有一个链接就可以 */
49
+ private getConnection;
50
+ private createSession;
51
+ /** 开始录音时长监听 */
52
+ private startRecordTimer;
53
+ start(): Promise<void>;
54
+ stop(isAbort?: boolean): Promise<void>;
55
+ abort(): Promise<void>;
56
+ dispose(): void;
57
+ }
@@ -0,0 +1,218 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
3
+ import "core-js/modules/es.json.stringify.js";
4
+ import { authorize, getCurrentHomeInfo, initAudioRecorder } from '../utils';
5
+ import { AIStreamAttributePayloadType, AIStreamAttributeType, ConnectClientType, ConnectState, ReceivedTextPacketType, SessionState } from '../AIStreamTypes';
6
+ import { DEFAULT_TOKEN_API, DEFAULT_TOKEN_API_VERSION, globalAIStreamClient } from '../global';
7
+ import { Logger, safeParseJSON } from '@ray-js/t-agent';
8
+ import logger from '../utils/logger';
9
+ export class AsrAgent {
10
+ constructor(options) {
11
+ /** 数据链接,一个agent保持一个链接就可以 */
12
+ /** 当前请求的session */
13
+ /** 音频流监听 */
14
+ /** 录音时长定时器 */
15
+ _defineProperty(this, "recordDurationTimer", null);
16
+ this.options = options;
17
+ if (this.options.earlyStart) {
18
+ // 如果需要提前启动,直接初始化和创建连接
19
+ this.createSession().then(() => initAudioRecorder()).catch(error => {
20
+ logger.error('EarlyStart Failed to create ASR session:', error);
21
+ });
22
+ }
23
+ if (this.options.enableLog) {
24
+ Logger.setLogLevel('debug');
25
+ }
26
+ }
27
+
28
+ /** 获取录音权限 */
29
+ getRecordScope() {
30
+ return authorize({
31
+ scope: 'scope.record'
32
+ }).then(() => true, () => false);
33
+ }
34
+
35
+ /** 获取数据链接,一般只有一个链接就可以 */
36
+ getConnection(clientType, deviceId) {
37
+ if (this.streamConn) {
38
+ return this.streamConn;
39
+ }
40
+ // 创建 activeSession
41
+ this.streamConn = globalAIStreamClient.getConnection({
42
+ clientType: clientType || ConnectClientType.APP,
43
+ deviceId: deviceId
44
+ });
45
+ return this.streamConn;
46
+ }
47
+ async createSession() {
48
+ var _this$activeSession;
49
+ // 如果有激活的 Session,直接返回
50
+ if ((_this$activeSession = this.activeSession) !== null && _this$activeSession !== void 0 && _this$activeSession.sessionId) {
51
+ return this.activeSession;
52
+ }
53
+ const {
54
+ clientType,
55
+ deviceId,
56
+ tokenOptions,
57
+ agentId
58
+ } = this.options;
59
+ const streamConn = this.getConnection(clientType, deviceId);
60
+ let homeId = this.options.homeId;
61
+ if (!homeId) {
62
+ const info = await getCurrentHomeInfo();
63
+ homeId = +info.homeId;
64
+ }
65
+ this.activeSession = streamConn.createSession({
66
+ ownerId: clientType === ConnectClientType.DEVICE ? deviceId : "".concat(homeId),
67
+ api: (tokenOptions === null || tokenOptions === void 0 ? void 0 : tokenOptions.api) || DEFAULT_TOKEN_API,
68
+ apiVersion: (tokenOptions === null || tokenOptions === void 0 ? void 0 : tokenOptions.version) || DEFAULT_TOKEN_API_VERSION,
69
+ solutionCode: agentId,
70
+ extParams: _objectSpread({
71
+ onlyAsr: true
72
+ }, tokenOptions === null || tokenOptions === void 0 ? void 0 : tokenOptions.extParams)
73
+ });
74
+ return this.activeSession;
75
+ }
76
+
77
+ /** 开始录音时长监听 */
78
+ startRecordTimer() {
79
+ const {
80
+ maxDuration
81
+ } = this.options.recordingOptions || {};
82
+ if (maxDuration) {
83
+ if (this.recordDurationTimer) {
84
+ clearTimeout(this.recordDurationTimer);
85
+ }
86
+ this.recordDurationTimer = setTimeout(() => {
87
+ this.stop(true);
88
+ }, maxDuration);
89
+ }
90
+ }
91
+ async start() {
92
+ const hasScope = await this.getRecordScope();
93
+ if (!hasScope) {
94
+ throw new Error('authorize failed');
95
+ }
96
+ const {
97
+ onMessage,
98
+ onFinish,
99
+ onError
100
+ } = this.options || {};
101
+ const activeSession = await this.createSession();
102
+ const activeEvent = await activeSession.startEvent({
103
+ userData: [{
104
+ type: AIStreamAttributeType.AI_CHAT,
105
+ payloadType: AIStreamAttributePayloadType.STRING,
106
+ value: JSON.stringify({
107
+ 'processing.interrupt': 'false',
108
+ 'asr.enableVad': 'false'
109
+ })
110
+ }]
111
+ });
112
+ this.activeEvent = activeEvent;
113
+ const {
114
+ recordingOptions
115
+ } = this.options || {};
116
+ const audioStream = activeEvent.stream(_objectSpread({
117
+ type: 'audio'
118
+ }, recordingOptions));
119
+ this.audioStream = audioStream;
120
+ activeEvent.on('data', entry => {
121
+ if (entry.type === 'text') {
122
+ const packet = safeParseJSON(entry.body.text);
123
+ if (!packet || !packet.data) {
124
+ return;
125
+ }
126
+ if (packet.bizType !== ReceivedTextPacketType.ASR) {
127
+ return;
128
+ }
129
+ typeof onMessage === 'function' && onMessage(packet.data);
130
+ } else if (entry.type === 'connectionState') {
131
+ if (entry.body.connectState === ConnectState.DISCONNECTED || entry.body.connectState === ConnectState.CLOSED) {
132
+ this.stop();
133
+ typeof onError === 'function' && onError(new Error('Connection closed'));
134
+ }
135
+ } else if (entry.type === 'sessionState') {
136
+ if (entry.body.sessionState === SessionState.CLOSED || entry.body.sessionState === SessionState.CREATE_FAILED) {
137
+ this.stop();
138
+ typeof onError === 'function' && onError(new Error('Session closed'));
139
+ }
140
+ }
141
+ });
142
+ let finished = false;
143
+ activeEvent.on('finish', () => {
144
+ if (finished) {
145
+ return;
146
+ }
147
+ this.stop();
148
+ typeof onFinish === 'function' && onFinish();
149
+ finished = true;
150
+ });
151
+ activeEvent.on('close', () => {
152
+ if (finished) {
153
+ return;
154
+ }
155
+ this.stop();
156
+ typeof onFinish === 'function' && onFinish();
157
+ finished = true;
158
+ });
159
+ activeEvent.on('error', error => {
160
+ typeof onError === 'function' && onError(error);
161
+ });
162
+ await audioStream.start();
163
+ this.startRecordTimer();
164
+ }
165
+ async stop(isAbort) {
166
+ if (this.recordDurationTimer) {
167
+ clearTimeout(this.recordDurationTimer);
168
+ this.recordDurationTimer = null;
169
+ }
170
+ if (this.audioStream) {
171
+ const result = await this.audioStream.stop();
172
+ if (result !== null && result !== void 0 && result.filePath) {
173
+ this.options.onMessage({
174
+ filePath: result.filePath
175
+ });
176
+ }
177
+ this.audioStream = null;
178
+ }
179
+ if (this.activeEvent) {
180
+ if (isAbort) {
181
+ await this.activeEvent.abort();
182
+ } else {
183
+ await this.activeEvent.end();
184
+ }
185
+ this.activeEvent = null;
186
+ }
187
+ if (isAbort) {
188
+ const {
189
+ onAbort
190
+ } = this.options || {};
191
+ typeof onAbort === 'function' && onAbort();
192
+ }
193
+ }
194
+ async abort() {
195
+ await this.stop(true);
196
+ }
197
+ dispose() {
198
+ if (this.recordDurationTimer) {
199
+ clearTimeout(this.recordDurationTimer);
200
+ this.recordDurationTimer = null;
201
+ }
202
+ if (this.audioStream && this.audioStream.started) {
203
+ this.audioStream.stop();
204
+ }
205
+ this.audioStream = null;
206
+ if (this.activeEvent) {
207
+ this.activeEvent.abort();
208
+ this.activeEvent = null;
209
+ }
210
+ if (this.activeSession) {
211
+ this.activeSession.close();
212
+ this.activeSession = null;
213
+ }
214
+ if (this.streamConn) {
215
+ this.streamConn = null;
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,31 @@
1
+ import { AsrAgent, AsrAgentOptions } from './AsrAgent';
2
+ /**
3
+ * 创建一个AsrAgent实例,用于语音转文本
4
+ * @example
5
+ * const asrAgent = createAsrAgent({
6
+ * agentId: 'your_agent_id',
7
+ * clientType: ConnectClientType.APP,
8
+ * deviceId: 'deviceId',
9
+ * maxDuration: 60,
10
+ * onMessage: (message) => {
11
+ * console.log('onMessage', message);
12
+ * },
13
+ * onFinish: () => {
14
+ * console.log('onFinish');
15
+ * },
16
+ * onError: (error) => {
17
+ * console.log('onError', error);
18
+ * },
19
+ * onAbort: () => {
20
+ * console.log('onAbort');
21
+ * },
22
+ * });
23
+ * // 开始录音
24
+ * asrAgent.start()
25
+ * // 停止录音
26
+ * asrAgent.stop()
27
+ * // 中断录音
28
+ * asrAgent.abort()
29
+ * @returns
30
+ */
31
+ export declare function createAsrAgent(options: AsrAgentOptions): AsrAgent;
@@ -0,0 +1,35 @@
1
+ import { AsrAgent } from './AsrAgent';
2
+
3
+ /**
4
+ * 创建一个AsrAgent实例,用于语音转文本
5
+ * @example
6
+ * const asrAgent = createAsrAgent({
7
+ * agentId: 'your_agent_id',
8
+ * clientType: ConnectClientType.APP,
9
+ * deviceId: 'deviceId',
10
+ * maxDuration: 60,
11
+ * onMessage: (message) => {
12
+ * console.log('onMessage', message);
13
+ * },
14
+ * onFinish: () => {
15
+ * console.log('onFinish');
16
+ * },
17
+ * onError: (error) => {
18
+ * console.log('onError', error);
19
+ * },
20
+ * onAbort: () => {
21
+ * console.log('onAbort');
22
+ * },
23
+ * });
24
+ * // 开始录音
25
+ * asrAgent.start()
26
+ * // 停止录音
27
+ * asrAgent.stop()
28
+ * // 中断录音
29
+ * asrAgent.abort()
30
+ * @returns
31
+ */
32
+ export function createAsrAgent(options) {
33
+ const asrAgent = new AsrAgent(options);
34
+ return asrAgent;
35
+ }
@@ -1,6 +1,4 @@
1
1
  import "core-js/modules/web.dom-collections.iterator.js";
2
- import { getMiniAppConfig } from '../utils';
3
- import logger from '../utils/logger';
4
2
  import { BuildInSkillCode, ReceivedSmartHomeSkillAction } from '../AIStreamTypes';
5
3
  export function withBuildIn() {
6
4
  return _agent => {
@@ -17,27 +15,28 @@ export function withBuildIn() {
17
15
  const {
18
16
  onSkillsEnd
19
17
  } = agent.plugins.aiStream;
20
- onAgentStart(async () => {
21
- const agentId = session.get('AIStream.agentId');
22
- if (agentId) {
23
- const _conf = await session.get('AIAssistant.projectConfig');
24
- if (!_conf) {
25
- try {
26
- const config = (await getMiniAppConfig({})).config || {};
27
- const projectConfig = config[agentId];
28
- if (projectConfig) {
29
- logger.debug('getMiniAppConfig projectConfig', {
30
- agentId,
31
- projectConfig
32
- });
33
- await session.set('AIAssistant.projectConfig', projectConfig);
34
- }
35
- } catch (error) {
36
- logger.warn('getMiniAppConfig error', error);
37
- }
38
- }
39
- }
40
- });
18
+
19
+ // onAgentStart(async () => {
20
+ // const agentId = session.get('AIStream.agentId');
21
+ // if (agentId) {
22
+ // const _conf = session.get('AIStream.projectConfig');
23
+ // if (!_conf) {
24
+ // try {
25
+ // const config: Record<string, ProjectConfig> = (await getMiniAppConfig({})).config || {};
26
+ // const projectConfig = config[agentId];
27
+ // if (projectConfig) {
28
+ // logger.debug('getMiniAppConfig projectConfig', {
29
+ // agentId,
30
+ // projectConfig,
31
+ // });
32
+ // await session.set('AIAssistant.projectConfig', projectConfig);
33
+ // }
34
+ // } catch (error) {
35
+ // logger.warn('getMiniAppConfig error', error);
36
+ // }
37
+ // }
38
+ // }
39
+ // });
41
40
 
42
41
  // 关联文档
43
42
 
@@ -87,6 +87,8 @@ type AIStreamEventSource = {
87
87
  type: 'audio';
88
88
  dataChannel?: string;
89
89
  userData?: Attribute[];
90
+ needSave?: boolean;
91
+ sampleRate?: number;
90
92
  } | {
91
93
  type: 'video';
92
94
  dataChannel?: string;
@@ -98,7 +100,9 @@ export interface AIStreamEventStream {
98
100
  dataChannel: string;
99
101
  started: boolean;
100
102
  start: () => Promise<void>;
101
- stop: () => Promise<void>;
103
+ stop: () => Promise<{
104
+ filePath?: string;
105
+ } | null>;
102
106
  }
103
107
  export declare class AIStreamEvent {
104
108
  readonly eventId: string;
@@ -473,15 +473,16 @@ export class AIStreamEvent {
473
473
  },
474
474
  stop: async () => {
475
475
  if (!stream.started || this.closed) {
476
- return;
476
+ return null;
477
477
  }
478
478
  // 一定要等录音开始,才能结束
479
479
  if (startPromise) {
480
480
  await tryCatchTTT(() => startPromise);
481
481
  startPromise = null;
482
482
  }
483
+ let result = null;
483
484
  if (source.type === 'audio') {
484
- stopRecordAndSendAudioData({
485
+ result = await stopRecordAndSendAudioData({
485
486
  sessionId: this.sessionId,
486
487
  dataChannel,
487
488
  userData: source.userData
@@ -502,6 +503,7 @@ export class AIStreamEvent {
502
503
  });
503
504
  delete this.streams[dataChannel];
504
505
  stream.started = false;
506
+ return result;
505
507
  }
506
508
  };
507
509
  this.streams[dataChannel] = stream;
@@ -16,6 +16,7 @@ function splitString(input) {
16
16
  return input.match(/[a-zA-Z0-9]+\s*|[\u4e00-\u9fff]+\s*|[^\w\s\u4e00-\u9fff]+\s*|[\s]+/g) || [];
17
17
  }
18
18
  mock.data.set('sessionMap', new Map());
19
+ mock.data.set('tokenMap', new Map());
19
20
  const getSession = (sessionId, eventId) => {
20
21
  const connection = getCurrentConnection();
21
22
  if (!connection) {
@@ -94,8 +95,15 @@ mock.hooks.hook('disconnect', context => {
94
95
  mock.data.set('sessionMap', new Map());
95
96
  });
96
97
  mock.hooks.hook('queryAgentToken', context => {
98
+ var _context$options$extP, _context$options$extP2;
99
+ const agentToken = generateId();
100
+ const map = mock.data.get('tokenMap');
101
+ map.set(agentToken, {
102
+ onlyAsr: ((_context$options$extP = context.options.extParams) === null || _context$options$extP === void 0 ? void 0 : _context$options$extP.onlyAsr) || false,
103
+ needTts: ((_context$options$extP2 = context.options.extParams) === null || _context$options$extP2 === void 0 ? void 0 : _context$options$extP2.needTts) || false
104
+ });
97
105
  context.result = {
98
- agentToken: generateId(),
106
+ agentToken,
99
107
  bizConfig: {
100
108
  bizCode: BizCode.CHAT,
101
109
  sendData: ['audio', 'video', 'text', 'image'],
@@ -114,12 +122,15 @@ mock.hooks.hook('createSession', context => {
114
122
  throw new TTTError('not connect', AIStreamAppErrorCode.CONNECTION_INVALID);
115
123
  }
116
124
  const map = mock.data.get('sessionMap');
125
+ const tokenMap = mock.data.get('tokenMap');
126
+ const tokenConfig = tokenMap.get(context.options.agentToken);
117
127
  const session = {
118
128
  closed: false,
119
129
  sessionId: generateId(),
120
130
  sendDataChannels: ['audio', 'video', 'text', 'image'],
121
131
  revDataChannels: ['text', 'audio'],
122
- currentEvent: null
132
+ currentEvent: null,
133
+ tokenConfig
123
134
  };
124
135
  map.set(session.sessionId, session);
125
136
  context.result = {
@@ -238,6 +249,11 @@ mock.hooks.hook('sendEventEnd', async context => {
238
249
  (async () => {
239
250
  var _ctx$responseSkills;
240
251
  await event.asr.promise;
252
+ if (session.tokenConfig.onlyAsr) {
253
+ event.replyEvent(EventType.EVENT_END);
254
+ session.currentEvent = null;
255
+ return;
256
+ }
241
257
  const ctx = {
242
258
  data: event.data,
243
259
  responseText: ''
@@ -9,6 +9,6 @@ export * from './AIStream';
9
9
  export * from './observer';
10
10
  export * from './sendMessage';
11
11
  export * from './version';
12
- export * from './createAsrAgent';
12
+ export * from '../asr/createAsrAgent';
13
13
  export * from './actions';
14
14
  export { mock };
@@ -9,6 +9,6 @@ export * from './AIStream';
9
9
  export * from './observer';
10
10
  export * from './sendMessage';
11
11
  export * from './version';
12
- export * from './createAsrAgent';
12
+ export * from '../asr/createAsrAgent';
13
13
  export * from './actions';
14
14
  export { mock };
@@ -1,4 +1,4 @@
1
- import { ApiRequestByAtopParams, ApiRequestByHighwayParams, AudioBody, AuthorizeParams, AuthorizePolicyStatusParams, CanIUseRouterParams, CloseSessionParams, ConnectParams, ConnectStateBody, CreateSessionParams, DeleteRecordListParams, DisconnectParams, EventBody, EventChannelMessageParams, GetAppInfoParams, GetCurrentHomeInfoParams, GetMiniAppConfigParams, GetAccountInfoParams, ImageBody, InsertRecordParams, NavigateToMiniProgramParams, OpenInnerH5Params, OpenMiniWidgetParams, QueryAgentTokenParams, QueryRecordListParams, RecordAmplitudesBody, RegisterChannelParams, RouterParams, SendEventChatBreakParams, SendEventEndParams, SendEventPayloadEndParams, SendEventStartParams, SendImageDataParams, SendTextDataParams, SessionStateBody, StartRecordAndSendAudioDataParams, StopRecordAndSendAudioDataParams, TextBody, UpdateRecordParams, IsConnectedParams, GetNetworkTypeParams, StartPlayAudioParams } from '../AIStreamTypes';
1
+ import { ApiRequestByAtopParams, ApiRequestByHighwayParams, AudioBody, AuthorizeParams, AuthorizePolicyStatusParams, CanIUseRouterParams, CloseSessionParams, ConnectParams, ConnectStateBody, CreateSessionParams, DeleteRecordListParams, DisconnectParams, EventBody, EventChannelMessageParams, GetAppInfoParams, GetCurrentHomeInfoParams, GetMiniAppConfigParams, GetAccountInfoParams, ImageBody, InsertRecordParams, NavigateToMiniProgramParams, OpenInnerH5Params, OpenMiniWidgetParams, QueryAgentTokenParams, QueryRecordListParams, RecordAmplitudesBody, RegisterChannelParams, RouterParams, SendEventChatBreakParams, SendEventEndParams, SendEventPayloadEndParams, SendEventStartParams, SendImageDataParams, SendTextDataParams, SessionStateBody, StartRecordAndSendAudioDataParams, StopRecordAndSendAudioDataParams, TextBody, UpdateRecordParams, IsConnectedParams, GetNetworkTypeParams, StartPlayAudioParams, InitAudioRecorderParams } from '../AIStreamTypes';
2
2
  export declare const getMiniAppConfig: (options?: Omit<GetMiniAppConfigParams, "success" | "fail"> | undefined) => Promise<{
3
3
  config: any;
4
4
  }>;
@@ -79,13 +79,16 @@ export declare const sendEventPayloadEnd: (options?: Omit<SendEventPayloadEndPar
79
79
  export declare const sendEventEnd: (options?: Omit<SendEventEndParams, "success" | "fail"> | undefined) => Promise<null>;
80
80
  export declare const sendEventChatBreak: (options?: Omit<SendEventChatBreakParams, "success" | "fail"> | undefined) => Promise<null>;
81
81
  export declare const startRecordAndSendAudioData: (options?: Omit<StartRecordAndSendAudioDataParams, "success" | "fail"> | undefined) => Promise<null>;
82
- export declare const stopRecordAndSendAudioData: (options?: Omit<StopRecordAndSendAudioDataParams, "success" | "fail"> | undefined) => Promise<null>;
82
+ export declare const stopRecordAndSendAudioData: (options?: Omit<StopRecordAndSendAudioDataParams, "success" | "fail"> | undefined) => Promise<{
83
+ filePath: string;
84
+ } | null>;
83
85
  export declare const sendImageData: (options?: Omit<SendImageDataParams, "success" | "fail"> | undefined) => Promise<null>;
84
86
  export declare const sendTextData: (options?: Omit<SendTextDataParams, "success" | "fail"> | undefined) => Promise<null>;
85
87
  export declare const registerRecordAmplitudes: (options?: Omit<{
86
88
  count: number;
87
89
  }, "success" | "fail"> | undefined) => Promise<never>;
88
90
  export declare const unregisterVoiceAmplitudes: (options?: Omit<any, "success" | "fail"> | undefined) => Promise<unknown>;
91
+ export declare const initAudioRecorder: (options?: Omit<InitAudioRecorderParams, "success" | "fail"> | undefined) => Promise<null>;
89
92
  export declare const startPlayAudio: (options?: Omit<StartPlayAudioParams, "success" | "fail"> | undefined) => Promise<null>;
90
93
  export declare const listenEventReceived: (listener: (params: EventBody) => void) => () => void;
91
94
  export declare const listenAudioPlayEnd: (listener: (params: {
package/dist/utils/ttt.js CHANGED
@@ -56,6 +56,7 @@ export const sendImageData = promisify(ty.aistream.sendImageData, true);
56
56
  export const sendTextData = promisify(ty.aistream.sendTextData, true);
57
57
  export const registerRecordAmplitudes = promisify(ty.aistream.registerRecordAmplitudes, true);
58
58
  export const unregisterVoiceAmplitudes = promisify(ty.aistream.unregisterVoiceAmplitudes, true);
59
+ export const initAudioRecorder = promisify(ty.aistream.initAudioRecorder, true);
59
60
  export const startPlayAudio = promisify(ty.aistream.startPlayAudio, true);
60
61
 
61
62
  // export const sendFileData = promisify<SendFileDataParams>(ty.aistream.sendFileData);
@@ -190,6 +190,28 @@ export function withAIStream() {
190
190
  await removeMessage(message);
191
191
  }
192
192
  });
193
+ const clearAllMessages = async () => {
194
+ // 删除内存中的消息
195
+ const messages = agent.session.messages.values();
196
+ await Promise.all(Array.from(messages).map(message => message.remove()));
197
+ // 清空缓存的数据
198
+ const historyStore = getHistoryStore();
199
+ if (historyStore) {
200
+ await historyStore.removeAll();
201
+ }
202
+ };
203
+ ui.hook('onClearHistory', async context => {
204
+ try {
205
+ await clearAllMessages();
206
+ context.result = {
207
+ success: true
208
+ };
209
+ } catch (e) {
210
+ context.result = {
211
+ success: false
212
+ };
213
+ }
214
+ });
193
215
  const send = (blocks, signal, extraOptions) => {
194
216
  const streamSession = session.get('AIStream.streamSession');
195
217
  const result = sendBlocksToAIStream({
@@ -344,7 +366,6 @@ export function withAIStream() {
344
366
  let valid = false;
345
367
  if (message.bubble.text) {
346
368
  valid = true;
347
- await message.persist();
348
369
  } else if (message.bubble.status === BubbleTileStatus.NORMAL) {
349
370
  valid = false;
350
371
  } else {
@@ -518,16 +539,7 @@ export function withAIStream() {
518
539
  chat,
519
540
  options,
520
541
  removeMessage,
521
- clearAllMessages: async () => {
522
- // 删除内存中的消息
523
- const messages = agent.session.messages.values();
524
- await Promise.all(Array.from(messages).map(message => message.remove()));
525
- // 清空缓存的数据
526
- const historyStore = getHistoryStore();
527
- if (historyStore) {
528
- await historyStore.removeAll();
529
- }
530
- },
542
+ clearAllMessages,
531
543
  feedback,
532
544
  onSkillCompose: fn => {
533
545
  return hooks.hook('onSkillCompose', fn);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/t-agent-plugin-aistream",
3
- "version": "0.2.3-beta-1",
3
+ "version": "0.2.3-beta-3",
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": "ed9810075246e1261af2fdaa41550070313a98dc"
38
+ "gitHead": "84e4e7b2ec8f91fe56b3b8499d549175893f9fe3"
39
39
  }
@@ -1,79 +0,0 @@
1
- import { ConnectClientType } from '../AIStreamTypes';
2
- export interface AsrAgentOptions {
3
- agentId: string;
4
- tokenApi?: string;
5
- tokenApiVersion?: string;
6
- clientType?: ConnectClientType;
7
- deviceId?: string;
8
- /** 最长录制时长,单位:秒 */
9
- maxDuration?: number;
10
- onMessage?: (message: {
11
- text: string;
12
- }) => void;
13
- onFinish?: () => void;
14
- onError?: (error: any) => void;
15
- /** 主动中断或者超过录制时长,会调用onAbort */
16
- onAbort?: () => void;
17
- }
18
- declare class AsrAgent {
19
- /** 数据链接,一个agent保持一个链接就可以 */
20
- private streamConn;
21
- /** 当前请求的session,每次请求都需要是一个新的session */
22
- private activeSession;
23
- private activeEvent;
24
- /** 音频流监听 */
25
- private audioStream;
26
- options: AsrAgentOptions;
27
- /** 录音时长定时器 */
28
- private recordDurationTimer;
29
- constructor(options: any);
30
- /** 获取录音权限 */
31
- getRecordScope(): Promise<boolean>;
32
- /** 获取数据链接,一般只有一个链接就可以 */
33
- private getConnection;
34
- private createSession;
35
- /** 开始录音时长监听 */
36
- private startRecordTimer;
37
- start(): Promise<void>;
38
- stop(isAbort?: boolean): Promise<void>;
39
- abort(): Promise<void>;
40
- }
41
- /**
42
- * 创建一个AsrAgent实例,用于语音转文本
43
- * @param options AsrAgentOptions
44
- * @param options.agentId 必填 语音转文本的agentId
45
- * @param options.tokenApi 语音转文本的tokenApi
46
- * @param options.tokenApiVersion 语音转文本的tokenApiVersion
47
- * @param options.clientType 语音转文本的clientType
48
- * @param options.deviceId 语音转文本的deviceId
49
- * @example
50
- * const asrAgent = createAsrAgent({
51
- * agentId: 'asr-agent',
52
- * tokenApi: 'xxxx',
53
- * tokenApiVersion: '1.0',
54
- * clientType: ConnectClientType.APP,
55
- * deviceId: 'deviceId',
56
- * maxDuration: 60,
57
- * onMessage: (message) => {
58
- * console.log('onMessage', message);
59
- * },
60
- * onFinish: () => {
61
- * console.log('onFinish');
62
- * },
63
- * onError: (error) => {
64
- * console.log('onError', error);
65
- * },
66
- * onAbort: () => {
67
- * console.log('onAbort');
68
- * },
69
- * });
70
- * // 开始录音
71
- * asrAgent.start()
72
- * // 停止录音
73
- * asrAgent.stop()
74
- * // 中断录音
75
- * asrAgent.abort()
76
- * @returns
77
- */
78
- export declare function createAsrAgent(options: AsrAgentOptions): AsrAgent;
79
- export {};
@@ -1,198 +0,0 @@
1
- import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
2
- import "core-js/modules/es.json.stringify.js";
3
- import { authorize } from './ttt';
4
- import { DEFAULT_TOKEN_API, DEFAULT_TOKEN_API_VERSION, globalAIStreamClient } from '../global';
5
- import { ConnectClientType, AIStreamAttributePayloadType, AIStreamAttributeType } from '../AIStreamTypes';
6
- import logger from './logger';
7
- class AsrAgent {
8
- constructor(options) {
9
- /** 数据链接,一个agent保持一个链接就可以 */
10
- /** 当前请求的session,每次请求都需要是一个新的session */
11
- /** 音频流监听 */
12
- /** 录音时长定时器 */
13
- _defineProperty(this, "recordDurationTimer", null);
14
- this.options = options;
15
- }
16
-
17
- /** 获取录音权限 */
18
- getRecordScope() {
19
- return authorize({
20
- scope: 'scope.record'
21
- }).then(() => true, () => false);
22
- }
23
-
24
- /** 获取数据链接,一般只有一个链接就可以 */
25
- getConnection(clientType, deviceId) {
26
- if (this.streamConn) {
27
- return this.streamConn;
28
- }
29
- // 创建 activeSession
30
- this.streamConn = globalAIStreamClient.getConnection({
31
- clientType: clientType || ConnectClientType.APP,
32
- deviceId: deviceId
33
- });
34
- return this.streamConn;
35
- }
36
- createSession() {
37
- var _this$activeSession;
38
- // 每次新的请求,都需要重新创建一个Session
39
- const {
40
- clientType,
41
- deviceId,
42
- tokenApi,
43
- tokenApiVersion,
44
- agentId
45
- } = this.options;
46
- const streamConn = this.getConnection(clientType, deviceId);
47
- // 如果有激活的,需要释放
48
- if ((_this$activeSession = this.activeSession) !== null && _this$activeSession !== void 0 && _this$activeSession.sessionId) {
49
- this.activeSession.close();
50
- }
51
- const activeSession = streamConn.createSession({
52
- api: tokenApi || DEFAULT_TOKEN_API,
53
- apiVersion: tokenApiVersion || DEFAULT_TOKEN_API_VERSION,
54
- solutionCode: agentId,
55
- extParams: {
56
- onlyAsr: true
57
- }
58
- });
59
- this.activeSession = activeSession;
60
- return this.activeSession;
61
- }
62
-
63
- /** 开始录音时长监听 */
64
- startRecordTimer() {
65
- const {
66
- maxDuration
67
- } = this.options;
68
- if (maxDuration) {
69
- if (this.recordDurationTimer) {
70
- clearTimeout(this.recordDurationTimer);
71
- }
72
- this.recordDurationTimer = setTimeout(() => {
73
- this.stop(true);
74
- }, maxDuration * 1000);
75
- }
76
- }
77
- async start() {
78
- const hasScope = await this.getRecordScope();
79
- if (!hasScope) {
80
- throw new Error('authorize failed');
81
- }
82
- const {
83
- onMessage,
84
- onFinish,
85
- onError
86
- } = this.options || {};
87
- const activeSession = this.createSession();
88
- const activeEvent = await activeSession.startEvent({
89
- userData: [{
90
- type: AIStreamAttributeType.AI_CHAT,
91
- payloadType: AIStreamAttributePayloadType.STRING,
92
- value: JSON.stringify({
93
- 'processing.interrupt': 'false',
94
- 'asr.enableVad': 'false'
95
- })
96
- }]
97
- });
98
- this.activeEvent = activeEvent;
99
- const audioStream = activeEvent.stream({
100
- type: 'audio'
101
- });
102
- this.audioStream = audioStream;
103
- await audioStream.start();
104
- this.startRecordTimer();
105
- activeEvent.on('data', entry => {
106
- if (entry.type === 'text') {
107
- console.log('text', entry, JSON.parse(entry.body.text));
108
- let data = {
109
- text: ''
110
- };
111
- try {
112
- data = JSON.parse(entry.body.text) || {};
113
- } catch (error) {
114
- logger.error('JSON.parse error', error);
115
- }
116
- typeof onMessage === 'function' && onMessage(data);
117
- }
118
- });
119
- activeEvent.on('finish', () => {
120
- this.stop();
121
- typeof onFinish === 'function' && onFinish();
122
- });
123
- activeEvent.on('error', error => {
124
- typeof onError === 'function' && onError(error);
125
- });
126
- }
127
- async stop(isAbort) {
128
- var _this$activeSession2;
129
- if (this.recordDurationTimer) {
130
- clearTimeout(this.recordDurationTimer);
131
- this.recordDurationTimer = null;
132
- }
133
- if (this.audioStream) {
134
- await this.audioStream.stop();
135
- this.audioStream = null;
136
- }
137
- if (this.activeEvent) {
138
- if (isAbort) {
139
- await this.activeEvent.abort();
140
- } else {
141
- await this.activeEvent.end();
142
- }
143
- this.activeEvent = null;
144
- }
145
- if (isAbort) {
146
- const {
147
- onAbort
148
- } = this.options || {};
149
- typeof onAbort === 'function' && onAbort();
150
- }
151
- await ((_this$activeSession2 = this.activeSession) === null || _this$activeSession2 === void 0 ? void 0 : _this$activeSession2.close());
152
- }
153
- async abort() {
154
- await this.stop(true);
155
- }
156
- }
157
-
158
- /**
159
- * 创建一个AsrAgent实例,用于语音转文本
160
- * @param options AsrAgentOptions
161
- * @param options.agentId 必填 语音转文本的agentId
162
- * @param options.tokenApi 语音转文本的tokenApi
163
- * @param options.tokenApiVersion 语音转文本的tokenApiVersion
164
- * @param options.clientType 语音转文本的clientType
165
- * @param options.deviceId 语音转文本的deviceId
166
- * @example
167
- * const asrAgent = createAsrAgent({
168
- * agentId: 'asr-agent',
169
- * tokenApi: 'xxxx',
170
- * tokenApiVersion: '1.0',
171
- * clientType: ConnectClientType.APP,
172
- * deviceId: 'deviceId',
173
- * maxDuration: 60,
174
- * onMessage: (message) => {
175
- * console.log('onMessage', message);
176
- * },
177
- * onFinish: () => {
178
- * console.log('onFinish');
179
- * },
180
- * onError: (error) => {
181
- * console.log('onError', error);
182
- * },
183
- * onAbort: () => {
184
- * console.log('onAbort');
185
- * },
186
- * });
187
- * // 开始录音
188
- * asrAgent.start()
189
- * // 停止录音
190
- * asrAgent.stop()
191
- * // 中断录音
192
- * asrAgent.abort()
193
- * @returns
194
- */
195
- export function createAsrAgent(options) {
196
- const asrAgent = new AsrAgent(options);
197
- return asrAgent;
198
- }