@ray-js/t-agent-plugin-aistream 0.2.3-beta-3 → 0.2.3-beta-5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AIStreamTypes.d.ts +79 -3
- package/dist/AIStreamTypes.js +8 -0
- package/dist/asr/AsrAgent.d.ts +24 -6
- package/dist/asr/AsrAgent.js +162 -67
- package/dist/asr/index.d.ts +3 -0
- package/dist/asr/index.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/utils/AIStream.d.ts +3 -5
- package/dist/utils/AIStream.js +8 -4
- package/dist/utils/defaultMock.js +28 -25
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/utils/ttt.d.ts +4 -7
- package/dist/utils/ttt.js +2 -1
- package/package.json +2 -2
package/dist/AIStreamTypes.d.ts
CHANGED
|
@@ -791,6 +791,18 @@ export type StartPlayAudioParams = {
|
|
|
791
791
|
}) => void;
|
|
792
792
|
complete?: () => void;
|
|
793
793
|
};
|
|
794
|
+
export type StopPlayAudioParams = {
|
|
795
|
+
success?: (params: null) => void;
|
|
796
|
+
fail?: (params: {
|
|
797
|
+
errorMsg: string;
|
|
798
|
+
errorCode: string | number;
|
|
799
|
+
innerError: {
|
|
800
|
+
errorCode: string | number;
|
|
801
|
+
errorMsg: string;
|
|
802
|
+
};
|
|
803
|
+
}) => void;
|
|
804
|
+
complete?: () => void;
|
|
805
|
+
};
|
|
794
806
|
export type Attribute = {
|
|
795
807
|
/** Attribute 类型 */
|
|
796
808
|
type: AIStreamAttributeType;
|
|
@@ -862,6 +874,67 @@ export type AudioBody = {
|
|
|
862
874
|
*/
|
|
863
875
|
bitDepth?: number;
|
|
864
876
|
};
|
|
877
|
+
export declare enum AudioPlayChangedState {
|
|
878
|
+
/** 播放开始 */
|
|
879
|
+
START = 1,
|
|
880
|
+
/** 缓冲中 */
|
|
881
|
+
BUFFERING = 2,
|
|
882
|
+
/** 缓存完成继续播放 */
|
|
883
|
+
BUFFERED = 3,
|
|
884
|
+
/** 播放完成 */
|
|
885
|
+
COMPLETED = 4,
|
|
886
|
+
/** 播放异常 */
|
|
887
|
+
ERROR = 5
|
|
888
|
+
}
|
|
889
|
+
export type AudioPlayChangedBody = {
|
|
890
|
+
path: string;
|
|
891
|
+
/** 播放状态: 1-播放开始, 2-缓冲中, 3-缓存完成 继续播放, 4-播放完成, 5-播放异常 */
|
|
892
|
+
state: AudioPlayChangedState;
|
|
893
|
+
/** 播放异常码: 1-主动停止播放, 2-播放被异常中断, ... */
|
|
894
|
+
code?: number;
|
|
895
|
+
};
|
|
896
|
+
export interface AIStreamAudioFile {
|
|
897
|
+
/** 音频缓存路径 */
|
|
898
|
+
path: string;
|
|
899
|
+
/**
|
|
900
|
+
* 音频编码类型,表示音频数据的编码格式
|
|
901
|
+
* - 100: ADPCM
|
|
902
|
+
* - 101: PCM
|
|
903
|
+
* - 102: AACRaw
|
|
904
|
+
* - 103: AACADTS
|
|
905
|
+
* - 104: AACLATM
|
|
906
|
+
* - 105: G711U
|
|
907
|
+
* - 106: G711A
|
|
908
|
+
* - 107: G726
|
|
909
|
+
* - 108: SPEEX
|
|
910
|
+
* - 109: MP3
|
|
911
|
+
* - 110: G722
|
|
912
|
+
* - 111: Opus
|
|
913
|
+
*/
|
|
914
|
+
codecType: number;
|
|
915
|
+
/**
|
|
916
|
+
* 音频采样率,单位Hz
|
|
917
|
+
* 表示每秒采样次数,常见值:8000, 16000, 44100等
|
|
918
|
+
*/
|
|
919
|
+
sampleRate: number;
|
|
920
|
+
/**
|
|
921
|
+
* 音频位深,表示每个采样点的位数
|
|
922
|
+
* 常见值:8, 16, 24, 32等
|
|
923
|
+
*/
|
|
924
|
+
bitDepth: number;
|
|
925
|
+
/**
|
|
926
|
+
* 音频通道数
|
|
927
|
+
* - 0: 单声道
|
|
928
|
+
* - 1: 立体声
|
|
929
|
+
*/
|
|
930
|
+
channels: number;
|
|
931
|
+
}
|
|
932
|
+
export type RecordAndSendAudioFailBody = {
|
|
933
|
+
/**
|
|
934
|
+
* 录制发送异常: 1-发送数据异常, 2-录制音频异常
|
|
935
|
+
*/
|
|
936
|
+
code: 0 | 1;
|
|
937
|
+
};
|
|
865
938
|
export type VideoBody = {
|
|
866
939
|
/** 接收数据通道 */
|
|
867
940
|
dataChannel: string;
|
|
@@ -1217,6 +1290,10 @@ export type SendEventChatBreakParams = {
|
|
|
1217
1290
|
*@description 开始录制并发送音频数据
|
|
1218
1291
|
*/
|
|
1219
1292
|
export type StartRecordAndSendAudioDataParams = {
|
|
1293
|
+
saveFile?: boolean;
|
|
1294
|
+
recordInitParams?: {
|
|
1295
|
+
sampleRate?: number;
|
|
1296
|
+
};
|
|
1220
1297
|
/** 会话 id */
|
|
1221
1298
|
sessionId: string;
|
|
1222
1299
|
/** 下发数据通道,当音频只有单路的时候,可以不传 */
|
|
@@ -1241,9 +1318,7 @@ export type StopRecordAndSendAudioDataParams = {
|
|
|
1241
1318
|
dataChannel?: string;
|
|
1242
1319
|
/** 扩展属性 */
|
|
1243
1320
|
userData?: Attribute[];
|
|
1244
|
-
success?: (params:
|
|
1245
|
-
filePath: string;
|
|
1246
|
-
} | null) => void;
|
|
1321
|
+
success?: (params: AIStreamAudioFile | null) => void;
|
|
1247
1322
|
fail?: (params: {
|
|
1248
1323
|
errorMsg: string;
|
|
1249
1324
|
errorCode: string | number;
|
|
@@ -1345,6 +1420,7 @@ export type SendTextDataParams = {
|
|
|
1345
1420
|
complete?: () => void;
|
|
1346
1421
|
};
|
|
1347
1422
|
export type InitAudioRecorderParams = {
|
|
1423
|
+
sampleRate?: number;
|
|
1348
1424
|
success?: (params: null) => void;
|
|
1349
1425
|
fail?: (params: {
|
|
1350
1426
|
errorMsg: string;
|
package/dist/AIStreamTypes.js
CHANGED
|
@@ -187,6 +187,14 @@ export let AIStreamAttributePayloadType = /*#__PURE__*/function (AIStreamAttribu
|
|
|
187
187
|
AIStreamAttributePayloadType[AIStreamAttributePayloadType["STRING"] = 6] = "STRING";
|
|
188
188
|
return AIStreamAttributePayloadType;
|
|
189
189
|
}({});
|
|
190
|
+
export let AudioPlayChangedState = /*#__PURE__*/function (AudioPlayChangedState) {
|
|
191
|
+
AudioPlayChangedState[AudioPlayChangedState["START"] = 1] = "START";
|
|
192
|
+
AudioPlayChangedState[AudioPlayChangedState["BUFFERING"] = 2] = "BUFFERING";
|
|
193
|
+
AudioPlayChangedState[AudioPlayChangedState["BUFFERED"] = 3] = "BUFFERED";
|
|
194
|
+
AudioPlayChangedState[AudioPlayChangedState["COMPLETED"] = 4] = "COMPLETED";
|
|
195
|
+
AudioPlayChangedState[AudioPlayChangedState["ERROR"] = 5] = "ERROR";
|
|
196
|
+
return AudioPlayChangedState;
|
|
197
|
+
}({});
|
|
190
198
|
export let NetworkType = /*#__PURE__*/function (NetworkType) {
|
|
191
199
|
NetworkType["NONE"] = "none";
|
|
192
200
|
NetworkType["CELL_2G"] = "2g";
|
package/dist/asr/AsrAgent.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConnectClientType } from '../AIStreamTypes';
|
|
1
|
+
import { AIStreamAudioFile, ConnectClientType } from '../AIStreamTypes';
|
|
2
2
|
export interface AsrAgentOptions {
|
|
3
3
|
agentId: string;
|
|
4
4
|
/** 获取 agent token 的参数 */
|
|
@@ -13,17 +13,20 @@ export interface AsrAgentOptions {
|
|
|
13
13
|
clientType?: ConnectClientType;
|
|
14
14
|
deviceId?: string;
|
|
15
15
|
onMessage?: (message: {
|
|
16
|
+
type: 'text';
|
|
16
17
|
text: string;
|
|
17
18
|
} | {
|
|
18
|
-
|
|
19
|
+
type: 'file';
|
|
20
|
+
file: AIStreamAudioFile;
|
|
19
21
|
}) => void;
|
|
20
22
|
onFinish?: () => void;
|
|
21
23
|
onError?: (error: any) => void;
|
|
22
24
|
/** 主动中断或者超过录制时长,会调用onAbort */
|
|
23
25
|
onAbort?: () => void;
|
|
26
|
+
onStatusChanged?: (status: AsrAgentStatus) => void;
|
|
24
27
|
recordingOptions?: {
|
|
25
28
|
/** 是否需要保存音频 */
|
|
26
|
-
|
|
29
|
+
saveFile?: boolean;
|
|
27
30
|
/** 保存的音频采样率,单位:hz */
|
|
28
31
|
sampleRate?: number;
|
|
29
32
|
/** 最长录制时长,单位:毫秒 */
|
|
@@ -31,6 +34,12 @@ export interface AsrAgentOptions {
|
|
|
31
34
|
};
|
|
32
35
|
enableLog?: boolean;
|
|
33
36
|
}
|
|
37
|
+
export declare enum AsrAgentStatus {
|
|
38
|
+
PENDING = "pending",
|
|
39
|
+
RECORDING = "recording",
|
|
40
|
+
ASR = "asr",
|
|
41
|
+
ABORTING = "aborting"
|
|
42
|
+
}
|
|
34
43
|
export declare class AsrAgent {
|
|
35
44
|
/** 数据链接,一个agent保持一个链接就可以 */
|
|
36
45
|
private streamConn;
|
|
@@ -39,19 +48,28 @@ export declare class AsrAgent {
|
|
|
39
48
|
private activeEvent;
|
|
40
49
|
/** 音频流监听 */
|
|
41
50
|
private audioStream;
|
|
42
|
-
options
|
|
51
|
+
private options;
|
|
43
52
|
/** 录音时长定时器 */
|
|
44
53
|
private recordDurationTimer;
|
|
54
|
+
private startPromise;
|
|
55
|
+
private authorized;
|
|
56
|
+
_status: AsrAgentStatus;
|
|
57
|
+
private disposed;
|
|
58
|
+
get status(): AsrAgentStatus;
|
|
59
|
+
set status(status: AsrAgentStatus);
|
|
45
60
|
constructor(options: AsrAgentOptions);
|
|
46
61
|
/** 获取录音权限 */
|
|
47
|
-
getRecordScope
|
|
62
|
+
private getRecordScope;
|
|
48
63
|
/** 获取数据链接,一般只有一个链接就可以 */
|
|
49
64
|
private getConnection;
|
|
50
65
|
private createSession;
|
|
51
66
|
/** 开始录音时长监听 */
|
|
52
67
|
private startRecordTimer;
|
|
68
|
+
private _start;
|
|
69
|
+
private _abort;
|
|
70
|
+
private _stop;
|
|
53
71
|
start(): Promise<void>;
|
|
54
|
-
stop(
|
|
72
|
+
stop(): Promise<void>;
|
|
55
73
|
abort(): Promise<void>;
|
|
56
74
|
dispose(): void;
|
|
57
75
|
}
|
package/dist/asr/AsrAgent.js
CHANGED
|
@@ -1,22 +1,54 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
2
|
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
3
3
|
import "core-js/modules/es.json.stringify.js";
|
|
4
|
+
import "core-js/modules/es.promise.finally.js";
|
|
5
|
+
import "core-js/modules/web.dom-collections.iterator.js";
|
|
4
6
|
import { authorize, getCurrentHomeInfo, initAudioRecorder } from '../utils';
|
|
5
7
|
import { AIStreamAttributePayloadType, AIStreamAttributeType, ConnectClientType, ConnectState, ReceivedTextPacketType, SessionState } from '../AIStreamTypes';
|
|
6
8
|
import { DEFAULT_TOKEN_API, DEFAULT_TOKEN_API_VERSION, globalAIStreamClient } from '../global';
|
|
7
9
|
import { Logger, safeParseJSON } from '@ray-js/t-agent';
|
|
8
10
|
import logger from '../utils/logger';
|
|
11
|
+
import { tryCatch } from '../utils/misc';
|
|
12
|
+
export let AsrAgentStatus = /*#__PURE__*/function (AsrAgentStatus) {
|
|
13
|
+
AsrAgentStatus["PENDING"] = "pending";
|
|
14
|
+
AsrAgentStatus["RECORDING"] = "recording";
|
|
15
|
+
AsrAgentStatus["ASR"] = "asr";
|
|
16
|
+
AsrAgentStatus["ABORTING"] = "aborting";
|
|
17
|
+
return AsrAgentStatus;
|
|
18
|
+
}({});
|
|
9
19
|
export class AsrAgent {
|
|
20
|
+
get status() {
|
|
21
|
+
return this._status;
|
|
22
|
+
}
|
|
23
|
+
set status(status) {
|
|
24
|
+
this._status = status;
|
|
25
|
+
const {
|
|
26
|
+
onStatusChanged
|
|
27
|
+
} = this.options || {};
|
|
28
|
+
if (typeof onStatusChanged === 'function') {
|
|
29
|
+
onStatusChanged(status);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
10
32
|
constructor(options) {
|
|
11
33
|
/** 数据链接,一个agent保持一个链接就可以 */
|
|
12
34
|
/** 当前请求的session */
|
|
13
35
|
/** 音频流监听 */
|
|
14
36
|
/** 录音时长定时器 */
|
|
15
37
|
_defineProperty(this, "recordDurationTimer", null);
|
|
38
|
+
_defineProperty(this, "startPromise", null);
|
|
39
|
+
_defineProperty(this, "authorized", null);
|
|
40
|
+
_defineProperty(this, "_status", AsrAgentStatus.PENDING);
|
|
41
|
+
_defineProperty(this, "disposed", false);
|
|
16
42
|
this.options = options;
|
|
17
|
-
if (
|
|
43
|
+
if (options.earlyStart) {
|
|
44
|
+
const {
|
|
45
|
+
recordingOptions
|
|
46
|
+
} = options;
|
|
47
|
+
const sampleRate = recordingOptions === null || recordingOptions === void 0 ? void 0 : recordingOptions.sampleRate;
|
|
18
48
|
// 如果需要提前启动,直接初始化和创建连接
|
|
19
|
-
this.createSession().then(
|
|
49
|
+
this.createSession().then(session => session.ensureSession()).then(() => initAudioRecorder(sampleRate ? {
|
|
50
|
+
sampleRate
|
|
51
|
+
} : {})).catch(error => {
|
|
20
52
|
logger.error('EarlyStart Failed to create ASR session:', error);
|
|
21
53
|
});
|
|
22
54
|
}
|
|
@@ -27,9 +59,15 @@ export class AsrAgent {
|
|
|
27
59
|
|
|
28
60
|
/** 获取录音权限 */
|
|
29
61
|
getRecordScope() {
|
|
62
|
+
if (this.authorized !== null) {
|
|
63
|
+
return Promise.resolve(this.authorized);
|
|
64
|
+
}
|
|
30
65
|
return authorize({
|
|
31
66
|
scope: 'scope.record'
|
|
32
|
-
}).then(() => true, () => false)
|
|
67
|
+
}).then(() => true, () => false).then(authorized => {
|
|
68
|
+
this.authorized = authorized;
|
|
69
|
+
return authorized;
|
|
70
|
+
});
|
|
33
71
|
}
|
|
34
72
|
|
|
35
73
|
/** 获取数据链接,一般只有一个链接就可以 */
|
|
@@ -84,38 +122,73 @@ export class AsrAgent {
|
|
|
84
122
|
clearTimeout(this.recordDurationTimer);
|
|
85
123
|
}
|
|
86
124
|
this.recordDurationTimer = setTimeout(() => {
|
|
87
|
-
this.
|
|
125
|
+
this._stop();
|
|
88
126
|
}, maxDuration);
|
|
89
127
|
}
|
|
90
128
|
}
|
|
91
|
-
async
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
throw new Error('authorize failed');
|
|
95
|
-
}
|
|
129
|
+
async _start() {
|
|
130
|
+
this.status = AsrAgentStatus.RECORDING;
|
|
131
|
+
let finished = false;
|
|
96
132
|
const {
|
|
97
133
|
onMessage,
|
|
98
134
|
onFinish,
|
|
99
|
-
onError
|
|
135
|
+
onError,
|
|
136
|
+
onAbort
|
|
100
137
|
} = this.options || {};
|
|
138
|
+
const finish = error => {
|
|
139
|
+
if (this.disposed) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (finished) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
this.audioStream = null;
|
|
146
|
+
this.activeEvent = null;
|
|
147
|
+
finished = true;
|
|
148
|
+
const prevStatus = this.status;
|
|
149
|
+
this.status = AsrAgentStatus.PENDING;
|
|
150
|
+
if (prevStatus === AsrAgentStatus.ABORTING) {
|
|
151
|
+
typeof onAbort === 'function' && onAbort();
|
|
152
|
+
} else if (error) {
|
|
153
|
+
typeof onError === 'function' && onError(error);
|
|
154
|
+
} else {
|
|
155
|
+
typeof onFinish === 'function' && onFinish();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const hasScope = await this.getRecordScope();
|
|
159
|
+
if (!hasScope) {
|
|
160
|
+
finish(new Error('No record permission granted'));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
101
163
|
const activeSession = await this.createSession();
|
|
102
|
-
const
|
|
164
|
+
const attribute = {
|
|
165
|
+
'processing.interrupt': 'false',
|
|
166
|
+
'asr.enableVad': 'false'
|
|
167
|
+
};
|
|
168
|
+
const [startEventError, activeEvent] = await tryCatch(() => activeSession.startEvent({
|
|
103
169
|
userData: [{
|
|
104
170
|
type: AIStreamAttributeType.AI_CHAT,
|
|
105
171
|
payloadType: AIStreamAttributePayloadType.STRING,
|
|
106
|
-
value: JSON.stringify(
|
|
107
|
-
'processing.interrupt': 'false',
|
|
108
|
-
'asr.enableVad': 'false'
|
|
109
|
-
})
|
|
172
|
+
value: JSON.stringify(attribute)
|
|
110
173
|
}]
|
|
111
|
-
});
|
|
174
|
+
}));
|
|
175
|
+
if (startEventError) {
|
|
176
|
+
finish(startEventError);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
112
179
|
this.activeEvent = activeEvent;
|
|
113
180
|
const {
|
|
114
181
|
recordingOptions
|
|
115
182
|
} = this.options || {};
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
183
|
+
const {
|
|
184
|
+
sampleRate,
|
|
185
|
+
saveFile
|
|
186
|
+
} = recordingOptions || {};
|
|
187
|
+
const audioStream = activeEvent.stream({
|
|
188
|
+
type: 'audio',
|
|
189
|
+
sampleRate,
|
|
190
|
+
saveFile
|
|
191
|
+
});
|
|
119
192
|
this.audioStream = audioStream;
|
|
120
193
|
activeEvent.on('data', entry => {
|
|
121
194
|
if (entry.type === 'text') {
|
|
@@ -126,93 +199,115 @@ export class AsrAgent {
|
|
|
126
199
|
if (packet.bizType !== ReceivedTextPacketType.ASR) {
|
|
127
200
|
return;
|
|
128
201
|
}
|
|
129
|
-
typeof onMessage === 'function' && onMessage(
|
|
202
|
+
typeof onMessage === 'function' && onMessage({
|
|
203
|
+
type: 'text',
|
|
204
|
+
text: packet.data.text
|
|
205
|
+
});
|
|
130
206
|
} else if (entry.type === 'connectionState') {
|
|
131
207
|
if (entry.body.connectState === ConnectState.DISCONNECTED || entry.body.connectState === ConnectState.CLOSED) {
|
|
132
|
-
|
|
133
|
-
typeof onError === 'function' && onError(new Error('Connection closed'));
|
|
208
|
+
finish(new Error('Connection closed'));
|
|
134
209
|
}
|
|
135
210
|
} else if (entry.type === 'sessionState') {
|
|
136
211
|
if (entry.body.sessionState === SessionState.CLOSED || entry.body.sessionState === SessionState.CREATE_FAILED) {
|
|
137
|
-
|
|
138
|
-
typeof onError === 'function' && onError(new Error('Session closed'));
|
|
212
|
+
finish(new Error('Session closed'));
|
|
139
213
|
}
|
|
140
214
|
}
|
|
141
215
|
});
|
|
142
|
-
let finished = false;
|
|
143
216
|
activeEvent.on('finish', () => {
|
|
144
|
-
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
this.stop();
|
|
148
|
-
typeof onFinish === 'function' && onFinish();
|
|
149
|
-
finished = true;
|
|
217
|
+
finish();
|
|
150
218
|
});
|
|
151
219
|
activeEvent.on('close', () => {
|
|
152
|
-
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
this.stop();
|
|
156
|
-
typeof onFinish === 'function' && onFinish();
|
|
157
|
-
finished = true;
|
|
220
|
+
finish();
|
|
158
221
|
});
|
|
159
222
|
activeEvent.on('error', error => {
|
|
160
|
-
|
|
223
|
+
finish(error);
|
|
161
224
|
});
|
|
162
|
-
await audioStream.start();
|
|
225
|
+
const [error] = await tryCatch(() => audioStream.start());
|
|
226
|
+
if (error) {
|
|
227
|
+
finish(error);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
163
230
|
this.startRecordTimer();
|
|
164
231
|
}
|
|
165
|
-
async
|
|
232
|
+
async _abort() {
|
|
233
|
+
this.status = AsrAgentStatus.ABORTING;
|
|
166
234
|
if (this.recordDurationTimer) {
|
|
167
235
|
clearTimeout(this.recordDurationTimer);
|
|
168
236
|
this.recordDurationTimer = null;
|
|
169
237
|
}
|
|
238
|
+
if (this.startPromise) {
|
|
239
|
+
await this.startPromise;
|
|
240
|
+
}
|
|
241
|
+
this.activeEvent.abort();
|
|
242
|
+
}
|
|
243
|
+
async _stop() {
|
|
244
|
+
if (this.recordDurationTimer) {
|
|
245
|
+
clearTimeout(this.recordDurationTimer);
|
|
246
|
+
this.recordDurationTimer = null;
|
|
247
|
+
}
|
|
248
|
+
if (this.startPromise) {
|
|
249
|
+
await this.startPromise;
|
|
250
|
+
}
|
|
170
251
|
if (this.audioStream) {
|
|
171
|
-
const
|
|
172
|
-
if (
|
|
252
|
+
const file = await this.audioStream.stop();
|
|
253
|
+
if (file !== null && file !== void 0 && file.path && typeof this.options.onMessage === 'function') {
|
|
173
254
|
this.options.onMessage({
|
|
174
|
-
|
|
255
|
+
type: 'file',
|
|
256
|
+
file
|
|
175
257
|
});
|
|
176
258
|
}
|
|
177
|
-
this.audioStream = null;
|
|
178
259
|
}
|
|
179
260
|
if (this.activeEvent) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
} else {
|
|
183
|
-
await this.activeEvent.end();
|
|
184
|
-
}
|
|
185
|
-
this.activeEvent = null;
|
|
261
|
+
await this.activeEvent.end();
|
|
262
|
+
this.status = AsrAgentStatus.ASR;
|
|
186
263
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
264
|
+
}
|
|
265
|
+
start() {
|
|
266
|
+
if (this.disposed) {
|
|
267
|
+
throw new Error('AsrAgent has been disposed');
|
|
268
|
+
}
|
|
269
|
+
if (this.status !== AsrAgentStatus.PENDING) {
|
|
270
|
+
throw new Error('AsrAgent is already started or in progress');
|
|
271
|
+
}
|
|
272
|
+
if (!this.startPromise) {
|
|
273
|
+
this.startPromise = this._start().finally(() => {
|
|
274
|
+
this.startPromise = null;
|
|
275
|
+
});
|
|
192
276
|
}
|
|
277
|
+
return this.startPromise;
|
|
278
|
+
}
|
|
279
|
+
async stop() {
|
|
280
|
+
if (this.disposed) {
|
|
281
|
+
throw new Error('AsrAgent has been disposed');
|
|
282
|
+
}
|
|
283
|
+
if (this.status !== AsrAgentStatus.RECORDING) {
|
|
284
|
+
throw new Error('AsrAgent is not in recording state');
|
|
285
|
+
}
|
|
286
|
+
await this._stop();
|
|
193
287
|
}
|
|
194
288
|
async abort() {
|
|
195
|
-
|
|
289
|
+
if (this.disposed) {
|
|
290
|
+
throw new Error('AsrAgent has been disposed');
|
|
291
|
+
}
|
|
292
|
+
if (this.status !== AsrAgentStatus.RECORDING && this.status !== AsrAgentStatus.ASR) {
|
|
293
|
+
throw new Error('AsrAgent is not in recording or asr state');
|
|
294
|
+
}
|
|
295
|
+
return this._abort();
|
|
196
296
|
}
|
|
197
297
|
dispose() {
|
|
198
298
|
if (this.recordDurationTimer) {
|
|
199
299
|
clearTimeout(this.recordDurationTimer);
|
|
200
300
|
this.recordDurationTimer = null;
|
|
201
301
|
}
|
|
202
|
-
if (this.
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
this.audioStream = null;
|
|
206
|
-
if (this.activeEvent) {
|
|
207
|
-
this.activeEvent.abort();
|
|
208
|
-
this.activeEvent = null;
|
|
302
|
+
if (this.disposed) {
|
|
303
|
+
return;
|
|
209
304
|
}
|
|
210
305
|
if (this.activeSession) {
|
|
211
306
|
this.activeSession.close();
|
|
212
|
-
this.activeSession = null;
|
|
213
|
-
}
|
|
214
|
-
if (this.streamConn) {
|
|
215
|
-
this.streamConn = null;
|
|
216
307
|
}
|
|
308
|
+
this.audioStream = null;
|
|
309
|
+
this.activeEvent = null;
|
|
310
|
+
this.streamConn = null;
|
|
311
|
+
this.activeSession = null;
|
|
217
312
|
}
|
|
218
313
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/utils/AIStream.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Attribute, BizTag, ConnectClientType, ConnectState, FileFormat, VideoCameraType } from '../AIStreamTypes';
|
|
1
|
+
import { AIStreamAudioFile, Attribute, BizTag, ConnectClientType, ConnectState, FileFormat, VideoCameraType } from '../AIStreamTypes';
|
|
2
2
|
import { AIStreamDataEntry, AIStreamObserverPool } from './observer';
|
|
3
3
|
import { AIStreamError } from './errors';
|
|
4
4
|
interface AIStreamConnectionOptions {
|
|
@@ -87,7 +87,7 @@ type AIStreamEventSource = {
|
|
|
87
87
|
type: 'audio';
|
|
88
88
|
dataChannel?: string;
|
|
89
89
|
userData?: Attribute[];
|
|
90
|
-
|
|
90
|
+
saveFile?: boolean;
|
|
91
91
|
sampleRate?: number;
|
|
92
92
|
} | {
|
|
93
93
|
type: 'video';
|
|
@@ -100,9 +100,7 @@ export interface AIStreamEventStream {
|
|
|
100
100
|
dataChannel: string;
|
|
101
101
|
started: boolean;
|
|
102
102
|
start: () => Promise<void>;
|
|
103
|
-
stop: () => Promise<
|
|
104
|
-
filePath?: string;
|
|
105
|
-
} | null>;
|
|
103
|
+
stop: () => Promise<AIStreamAudioFile | null>;
|
|
106
104
|
}
|
|
107
105
|
export declare class AIStreamEvent {
|
|
108
106
|
readonly eventId: string;
|
package/dist/utils/AIStream.js
CHANGED
|
@@ -459,7 +459,11 @@ export class AIStreamEvent {
|
|
|
459
459
|
startPromise = startRecordAndSendAudioData({
|
|
460
460
|
sessionId: this.sessionId,
|
|
461
461
|
dataChannel,
|
|
462
|
-
userData: source.userData
|
|
462
|
+
userData: source.userData,
|
|
463
|
+
recordInitParams: source.sampleRate ? {
|
|
464
|
+
sampleRate: source.sampleRate
|
|
465
|
+
} : undefined,
|
|
466
|
+
saveFile: source.saveFile
|
|
463
467
|
});
|
|
464
468
|
await startPromise;
|
|
465
469
|
} else if (source.type === 'video') {
|
|
@@ -480,9 +484,9 @@ export class AIStreamEvent {
|
|
|
480
484
|
await tryCatchTTT(() => startPromise);
|
|
481
485
|
startPromise = null;
|
|
482
486
|
}
|
|
483
|
-
let
|
|
487
|
+
let file = null;
|
|
484
488
|
if (source.type === 'audio') {
|
|
485
|
-
|
|
489
|
+
file = await stopRecordAndSendAudioData({
|
|
486
490
|
sessionId: this.sessionId,
|
|
487
491
|
dataChannel,
|
|
488
492
|
userData: source.userData
|
|
@@ -503,7 +507,7 @@ export class AIStreamEvent {
|
|
|
503
507
|
});
|
|
504
508
|
delete this.streams[dataChannel];
|
|
505
509
|
stream.started = false;
|
|
506
|
-
return
|
|
510
|
+
return file;
|
|
507
511
|
}
|
|
508
512
|
};
|
|
509
513
|
this.streams[dataChannel] = stream;
|
|
@@ -4,6 +4,7 @@ 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.for-each.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';
|
|
@@ -140,19 +141,19 @@ mock.hooks.hook('createSession', context => {
|
|
|
140
141
|
};
|
|
141
142
|
|
|
142
143
|
// 用于测试断开连接;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
144
|
+
setTimeout(() => {
|
|
145
|
+
dispatch('onConnectStateChanged', {
|
|
146
|
+
connectionId: '',
|
|
147
|
+
connectState: ConnectState.DISCONNECTED,
|
|
148
|
+
code: 200
|
|
149
|
+
});
|
|
150
|
+
mock.data.set('currentConnection', null);
|
|
151
|
+
const map = mock.data.get('sessionMap');
|
|
152
|
+
map.forEach(s => {
|
|
153
|
+
s.closed = true;
|
|
154
|
+
});
|
|
155
|
+
mock.data.set('sessionMap', new Map());
|
|
156
|
+
}, 5000);
|
|
156
157
|
|
|
157
158
|
// 用于测试断开会话
|
|
158
159
|
// setTimeout(() => {
|
|
@@ -168,6 +169,8 @@ mock.hooks.hook('createSession', context => {
|
|
|
168
169
|
mock.hooks.hook('closeSession', context => {
|
|
169
170
|
const map = mock.data.get('sessionMap');
|
|
170
171
|
const sessionId = context.options.sessionId;
|
|
172
|
+
const session = map.get(sessionId);
|
|
173
|
+
session.closed = true;
|
|
171
174
|
map.delete(sessionId);
|
|
172
175
|
context.result = true;
|
|
173
176
|
});
|
|
@@ -273,7 +276,7 @@ mock.hooks.hook('sendEventEnd', async context => {
|
|
|
273
276
|
if (ctx.data.length === 0) {
|
|
274
277
|
event.replyEvent(EventType.EVENT_START);
|
|
275
278
|
await mock.sleep(100);
|
|
276
|
-
if (event.controller.signal.aborted) {
|
|
279
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
277
280
|
return;
|
|
278
281
|
}
|
|
279
282
|
event.replyEvent(EventType.EVENT_END);
|
|
@@ -282,19 +285,19 @@ mock.hooks.hook('sendEventEnd', async context => {
|
|
|
282
285
|
}
|
|
283
286
|
const text = ctx.responseText || '⚠️ No mock text response matched!';
|
|
284
287
|
const words = splitString(text);
|
|
285
|
-
if (event.controller.signal.aborted) {
|
|
288
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
286
289
|
return;
|
|
287
290
|
}
|
|
288
291
|
event.replyEvent(EventType.EVENT_START);
|
|
289
292
|
await mock.sleep(500);
|
|
290
|
-
if (event.controller.signal.aborted) {
|
|
293
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
291
294
|
return;
|
|
292
295
|
}
|
|
293
296
|
const bizId = generateId();
|
|
294
297
|
event.replyText(StreamFlag.START);
|
|
295
298
|
for (const word of words) {
|
|
296
299
|
await mock.sleep(100);
|
|
297
|
-
if (event.controller.signal.aborted) {
|
|
300
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
298
301
|
return;
|
|
299
302
|
}
|
|
300
303
|
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
@@ -309,7 +312,7 @@ mock.hooks.hook('sendEventEnd', async context => {
|
|
|
309
312
|
});
|
|
310
313
|
}
|
|
311
314
|
await mock.sleep(100);
|
|
312
|
-
if (event.controller.signal.aborted) {
|
|
315
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
313
316
|
return;
|
|
314
317
|
}
|
|
315
318
|
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
@@ -323,13 +326,13 @@ mock.hooks.hook('sendEventEnd', async context => {
|
|
|
323
326
|
}
|
|
324
327
|
});
|
|
325
328
|
await mock.sleep(10);
|
|
326
|
-
if (event.controller.signal.aborted) {
|
|
329
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
327
330
|
return;
|
|
328
331
|
}
|
|
329
332
|
if ((_ctx$responseSkills = ctx.responseSkills) !== null && _ctx$responseSkills !== void 0 && _ctx$responseSkills.length) {
|
|
330
333
|
for (const skill of ctx.responseSkills) {
|
|
331
334
|
await mock.sleep(100);
|
|
332
|
-
if (event.controller.signal.aborted) {
|
|
335
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
333
336
|
return;
|
|
334
337
|
}
|
|
335
338
|
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
@@ -342,7 +345,7 @@ mock.hooks.hook('sendEventEnd', async context => {
|
|
|
342
345
|
}
|
|
343
346
|
event.replyText(StreamFlag.END);
|
|
344
347
|
await mock.sleep(500);
|
|
345
|
-
if (event.controller.signal.aborted) {
|
|
348
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
346
349
|
return;
|
|
347
350
|
}
|
|
348
351
|
event.replyEvent(EventType.EVENT_END);
|
|
@@ -400,13 +403,13 @@ mock.hooks.hook('startRecordAndSendAudioData', async context => {
|
|
|
400
403
|
let text = '';
|
|
401
404
|
finishController.signal.addEventListener('abort', async () => {
|
|
402
405
|
var _finishResolve;
|
|
403
|
-
if (event.controller.signal.aborted) {
|
|
406
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
404
407
|
return;
|
|
405
408
|
}
|
|
406
409
|
|
|
407
410
|
// 终止识别到出完整结果的延迟
|
|
408
411
|
await mock.sleep(500);
|
|
409
|
-
if (event.controller.signal.aborted) {
|
|
412
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
410
413
|
return;
|
|
411
414
|
}
|
|
412
415
|
event.replyText(StreamFlag.IN_PROGRESS, {
|
|
@@ -418,7 +421,7 @@ mock.hooks.hook('startRecordAndSendAudioData', async context => {
|
|
|
418
421
|
}
|
|
419
422
|
});
|
|
420
423
|
await mock.sleep(100);
|
|
421
|
-
if (event.controller.signal.aborted) {
|
|
424
|
+
if (event.controller.signal.aborted || session.closed) {
|
|
422
425
|
return;
|
|
423
426
|
}
|
|
424
427
|
event.replyText(StreamFlag.END);
|
|
@@ -470,7 +473,7 @@ mock.hooks.hook('stopRecordAndSendAudioData', async context => {
|
|
|
470
473
|
throw new TTTError('stopRecordAndSendAudioData event not exists', AIStreamAppErrorCode.SESSION_ID_INVALID);
|
|
471
474
|
}
|
|
472
475
|
session.currentEvent.asr.finishController.abort(new Error('stopRecordAndSendAudioData'));
|
|
473
|
-
context.result =
|
|
476
|
+
context.result = null;
|
|
474
477
|
await mock.sleep(100);
|
|
475
478
|
});
|
|
476
479
|
mock.hooks.hook('startRecordAndSendVideoData', context => {
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
package/dist/utils/ttt.d.ts
CHANGED
|
@@ -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, InitAudioRecorderParams } 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, StopPlayAudioParams, AudioPlayChangedBody } from '../AIStreamTypes';
|
|
2
2
|
export declare const getMiniAppConfig: (options?: Omit<GetMiniAppConfigParams, "success" | "fail"> | undefined) => Promise<{
|
|
3
3
|
config: any;
|
|
4
4
|
}>;
|
|
@@ -79,9 +79,7 @@ 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<
|
|
83
|
-
filePath: string;
|
|
84
|
-
} | null>;
|
|
82
|
+
export declare const stopRecordAndSendAudioData: (options?: Omit<StopRecordAndSendAudioDataParams, "success" | "fail"> | undefined) => Promise<import("../AIStreamTypes").AIStreamAudioFile | null>;
|
|
85
83
|
export declare const sendImageData: (options?: Omit<SendImageDataParams, "success" | "fail"> | undefined) => Promise<null>;
|
|
86
84
|
export declare const sendTextData: (options?: Omit<SendTextDataParams, "success" | "fail"> | undefined) => Promise<null>;
|
|
87
85
|
export declare const registerRecordAmplitudes: (options?: Omit<{
|
|
@@ -90,10 +88,9 @@ export declare const registerRecordAmplitudes: (options?: Omit<{
|
|
|
90
88
|
export declare const unregisterVoiceAmplitudes: (options?: Omit<any, "success" | "fail"> | undefined) => Promise<unknown>;
|
|
91
89
|
export declare const initAudioRecorder: (options?: Omit<InitAudioRecorderParams, "success" | "fail"> | undefined) => Promise<null>;
|
|
92
90
|
export declare const startPlayAudio: (options?: Omit<StartPlayAudioParams, "success" | "fail"> | undefined) => Promise<null>;
|
|
91
|
+
export declare const stopPlayAudio: (options?: Omit<StopPlayAudioParams, "success" | "fail"> | undefined) => Promise<null>;
|
|
93
92
|
export declare const listenEventReceived: (listener: (params: EventBody) => void) => () => void;
|
|
94
|
-
export declare const
|
|
95
|
-
path: string;
|
|
96
|
-
}) => void) => () => void;
|
|
93
|
+
export declare const listenAudioPlayChanged: (listener: (params: AudioPlayChangedBody) => void) => () => void;
|
|
97
94
|
export declare const listenAudioReceived: (listener: (params: AudioBody) => void) => () => void;
|
|
98
95
|
export declare const listenTextReceived: (listener: (params: TextBody) => void) => () => void;
|
|
99
96
|
export declare const listenImageReceived: (listener: (params: ImageBody) => void) => () => void;
|
package/dist/utils/ttt.js
CHANGED
|
@@ -58,11 +58,12 @@ export const registerRecordAmplitudes = promisify(ty.aistream.registerRecordAmpl
|
|
|
58
58
|
export const unregisterVoiceAmplitudes = promisify(ty.aistream.unregisterVoiceAmplitudes, true);
|
|
59
59
|
export const initAudioRecorder = promisify(ty.aistream.initAudioRecorder, true);
|
|
60
60
|
export const startPlayAudio = promisify(ty.aistream.startPlayAudio, true);
|
|
61
|
+
export const stopPlayAudio = promisify(ty.aistream.stopPlayAudio, true);
|
|
61
62
|
|
|
62
63
|
// export const sendFileData = promisify<SendFileDataParams>(ty.aistream.sendFileData);
|
|
63
64
|
|
|
64
65
|
export const listenEventReceived = listening(ty.aistream.onEventReceived, ty.aistream.offEventReceived, true);
|
|
65
|
-
export const
|
|
66
|
+
export const listenAudioPlayChanged = listening(ty.aistream.onAudioPlayChanged, ty.aistream.onAudioPlayChanged, true);
|
|
66
67
|
export const listenAudioReceived = listening(ty.aistream.onAudioReceived, ty.aistream.offAudioReceived, true);
|
|
67
68
|
|
|
68
69
|
// export const listenVideoReceived = listening<VideoBody>(
|
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-
|
|
3
|
+
"version": "0.2.3-beta-5",
|
|
4
4
|
"author": "Tuya.inc",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/url-parse": "^1.4.11"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "a99e7ff0c22890381a0ba9a193060ab6d69cb915"
|
|
39
39
|
}
|