@mastra/voice-google 0.14.0 → 0.14.1
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/CHANGELOG.md +12 -0
- package/dist/_types/@internal_voice/dist/_types/@internal_ai-sdk-v5/dist/index.d.ts +19 -220
- package/dist/_types/@internal_voice/dist/_types/@internal_core/dist/base/index.d.ts +27 -27
- package/dist/_types/@internal_voice/dist/_types/@internal_core/dist/index-S1lgaKO7.d.ts +218 -0
- package/dist/_types/@internal_voice/dist/_types/@internal_core/dist/request-context/index.d.ts +122 -105
- package/dist/_types/@internal_voice/dist/_types/@internal_core/dist/types/index.d.ts +4 -2
- package/dist/docs/SKILL.md +5 -5
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/{docs-voice-overview.md → guides-voice-overview.md} +85 -131
- package/dist/docs/references/integrations-voice-google.md +688 -0
- package/dist/index.cjs +547 -531
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +546 -529
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
- package/dist/_types/@internal_voice/dist/_types/@internal_core/dist/logger/index.d.ts +0 -217
- package/dist/docs/references/reference-voice-google.md +0 -274
package/dist/index.js
CHANGED
|
@@ -1,542 +1,559 @@
|
|
|
1
|
-
import { PassThrough } from
|
|
2
|
-
import { SpeechClient, v2 } from
|
|
3
|
-
import { TextToSpeechClient } from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { PassThrough } from "stream";
|
|
2
|
+
import { SpeechClient, v2 } from "@google-cloud/speech";
|
|
3
|
+
import { TextToSpeechClient } from "@google-cloud/text-to-speech";
|
|
4
|
+
//#region ../../packages/_internal-core/dist/logger/index.js
|
|
5
|
+
const RegisteredLogger = {
|
|
6
|
+
AGENT: "AGENT",
|
|
7
|
+
OBSERVABILITY: "OBSERVABILITY",
|
|
8
|
+
AUTH: "AUTH",
|
|
9
|
+
BROWSER: "BROWSER",
|
|
10
|
+
NETWORK: "NETWORK",
|
|
11
|
+
WORKFLOW: "WORKFLOW",
|
|
12
|
+
LLM: "LLM",
|
|
13
|
+
TTS: "TTS",
|
|
14
|
+
VOICE: "VOICE",
|
|
15
|
+
VECTOR: "VECTOR",
|
|
16
|
+
BUNDLER: "BUNDLER",
|
|
17
|
+
DEPLOYER: "DEPLOYER",
|
|
18
|
+
MEMORY: "MEMORY",
|
|
19
|
+
STORAGE: "STORAGE",
|
|
20
|
+
EMBEDDINGS: "EMBEDDINGS",
|
|
21
|
+
MCP_SERVER: "MCP_SERVER",
|
|
22
|
+
SERVER_CACHE: "SERVER_CACHE",
|
|
23
|
+
SERVER: "SERVER",
|
|
24
|
+
WORKSPACE: "WORKSPACE",
|
|
25
|
+
CHANNEL: "CHANNEL"
|
|
26
|
+
};
|
|
27
|
+
const LogLevel = {
|
|
28
|
+
DEBUG: "debug",
|
|
29
|
+
INFO: "info",
|
|
30
|
+
WARN: "warn",
|
|
31
|
+
ERROR: "error",
|
|
32
|
+
NONE: "silent"
|
|
33
|
+
};
|
|
15
34
|
var MastraLogger = class {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
35
|
+
name;
|
|
36
|
+
level;
|
|
37
|
+
transports;
|
|
38
|
+
constructor(options = {}) {
|
|
39
|
+
this.name = options.name || "Mastra";
|
|
40
|
+
this.level = options.level || LogLevel.ERROR;
|
|
41
|
+
this.transports = new Map(Object.entries(options.transports || {}));
|
|
42
|
+
}
|
|
43
|
+
getTransports() {
|
|
44
|
+
return this.transports;
|
|
45
|
+
}
|
|
46
|
+
trackException(_error, _metadata) {}
|
|
47
|
+
async listLogs(transportId, params) {
|
|
48
|
+
if (!transportId || !this.transports.has(transportId)) return {
|
|
49
|
+
logs: [],
|
|
50
|
+
total: 0,
|
|
51
|
+
page: params?.page ?? 1,
|
|
52
|
+
perPage: params?.perPage ?? 100,
|
|
53
|
+
hasMore: false
|
|
54
|
+
};
|
|
55
|
+
return this.transports.get(transportId).listLogs?.(params) ?? {
|
|
56
|
+
logs: [],
|
|
57
|
+
total: 0,
|
|
58
|
+
page: params?.page ?? 1,
|
|
59
|
+
perPage: params?.perPage ?? 100,
|
|
60
|
+
hasMore: false
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async listLogsByRunId({ transportId, runId, fromDate, toDate, logLevel, filters, page, perPage }) {
|
|
64
|
+
if (!transportId || !this.transports.has(transportId) || !runId) return {
|
|
65
|
+
logs: [],
|
|
66
|
+
total: 0,
|
|
67
|
+
page: page ?? 1,
|
|
68
|
+
perPage: perPage ?? 100,
|
|
69
|
+
hasMore: false
|
|
70
|
+
};
|
|
71
|
+
return this.transports.get(transportId).listLogsByRunId?.({
|
|
72
|
+
runId,
|
|
73
|
+
fromDate,
|
|
74
|
+
toDate,
|
|
75
|
+
logLevel,
|
|
76
|
+
filters,
|
|
77
|
+
page,
|
|
78
|
+
perPage
|
|
79
|
+
}) ?? {
|
|
80
|
+
logs: [],
|
|
81
|
+
total: 0,
|
|
82
|
+
page: page ?? 1,
|
|
83
|
+
perPage: perPage ?? 100,
|
|
84
|
+
hasMore: false
|
|
85
|
+
};
|
|
86
|
+
}
|
|
62
87
|
};
|
|
63
|
-
var ConsoleLogger = class
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
88
|
+
var ConsoleLogger = class ConsoleLogger extends MastraLogger {
|
|
89
|
+
component;
|
|
90
|
+
filter;
|
|
91
|
+
constructor(options = {}) {
|
|
92
|
+
super(options);
|
|
93
|
+
this.component = options.component;
|
|
94
|
+
this.filter = options.filter;
|
|
95
|
+
}
|
|
96
|
+
child(componentOrBindings) {
|
|
97
|
+
const component = typeof componentOrBindings === "string" ? componentOrBindings : componentOrBindings?.component ?? this.component;
|
|
98
|
+
return new ConsoleLogger({
|
|
99
|
+
name: this.name,
|
|
100
|
+
level: this.level,
|
|
101
|
+
component,
|
|
102
|
+
filter: this.filter
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
shouldLog(level, message, args) {
|
|
106
|
+
if (!this.filter) return true;
|
|
107
|
+
try {
|
|
108
|
+
return this.filter({
|
|
109
|
+
component: this.component,
|
|
110
|
+
level,
|
|
111
|
+
message,
|
|
112
|
+
args
|
|
113
|
+
});
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.error(`[Logger] Filter error for component=${this.component} level=${level}:`, e);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
prefix() {
|
|
120
|
+
return this.component ? `[${this.component}] ` : "";
|
|
121
|
+
}
|
|
122
|
+
debug(message, ...args) {
|
|
123
|
+
if (this.level === LogLevel.DEBUG && this.shouldLog(LogLevel.DEBUG, message, args)) console.info(`${this.prefix()}${message}`, ...args);
|
|
124
|
+
}
|
|
125
|
+
info(message, ...args) {
|
|
126
|
+
if ((this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.INFO, message, args)) console.info(`${this.prefix()}${message}`, ...args);
|
|
127
|
+
}
|
|
128
|
+
warn(message, ...args) {
|
|
129
|
+
if ((this.level === LogLevel.WARN || this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.WARN, message, args)) console.warn(`${this.prefix()}${message}`, ...args);
|
|
130
|
+
}
|
|
131
|
+
error(message, ...args) {
|
|
132
|
+
if ((this.level === LogLevel.ERROR || this.level === LogLevel.WARN || this.level === LogLevel.INFO || this.level === LogLevel.DEBUG) && this.shouldLog(LogLevel.ERROR, message, args)) console.error(`${this.prefix()}${message}`, ...args);
|
|
133
|
+
}
|
|
134
|
+
async listLogs(_transportId, _params) {
|
|
135
|
+
return {
|
|
136
|
+
logs: [],
|
|
137
|
+
total: 0,
|
|
138
|
+
page: _params?.page ?? 1,
|
|
139
|
+
perPage: _params?.perPage ?? 100,
|
|
140
|
+
hasMore: false
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async listLogsByRunId(_args) {
|
|
144
|
+
return {
|
|
145
|
+
logs: [],
|
|
146
|
+
total: 0,
|
|
147
|
+
page: _args.page ?? 1,
|
|
148
|
+
perPage: _args.perPage ?? 100,
|
|
149
|
+
hasMore: false
|
|
150
|
+
};
|
|
151
|
+
}
|
|
118
152
|
};
|
|
119
|
-
|
|
120
|
-
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region ../../packages/_internal-core/dist/base/index.js
|
|
121
155
|
var MastraBase = class {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
*/
|
|
154
|
-
__setLogger(logger) {
|
|
155
|
-
this.logger = "child" in logger && typeof logger.child === "function" ? logger.child({ component: this.component }) : logger;
|
|
156
|
-
}
|
|
156
|
+
component = RegisteredLogger.LLM;
|
|
157
|
+
logger;
|
|
158
|
+
name;
|
|
159
|
+
#rawConfig;
|
|
160
|
+
constructor({ component, name, rawConfig }) {
|
|
161
|
+
this.component = component || RegisteredLogger.LLM;
|
|
162
|
+
this.name = name;
|
|
163
|
+
this.#rawConfig = rawConfig;
|
|
164
|
+
this.logger = new ConsoleLogger({ name: `${this.component} - ${this.name}` });
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Returns the raw storage configuration this primitive was created from,
|
|
168
|
+
* or undefined if it was created from code.
|
|
169
|
+
*/
|
|
170
|
+
toRawConfig() {
|
|
171
|
+
return this.#rawConfig;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Sets the raw storage configuration for this primitive.
|
|
175
|
+
* @internal
|
|
176
|
+
*/
|
|
177
|
+
__setRawConfig(rawConfig) {
|
|
178
|
+
this.#rawConfig = rawConfig;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Set the logger for the agent
|
|
182
|
+
* @param logger
|
|
183
|
+
*/
|
|
184
|
+
__setLogger(logger) {
|
|
185
|
+
this.logger = "child" in logger && typeof logger.child === "function" ? logger.child({ component: this.component }) : logger;
|
|
186
|
+
}
|
|
157
187
|
};
|
|
158
|
-
|
|
159
|
-
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region ../../packages/_internals/voice/dist/aisdk-C_K-VCwq.js
|
|
160
190
|
var MastraVoice = class extends MastraBase {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return Promise.resolve({ enabled: false });
|
|
263
|
-
}
|
|
191
|
+
listeningModel;
|
|
192
|
+
speechModel;
|
|
193
|
+
speaker;
|
|
194
|
+
realtimeConfig;
|
|
195
|
+
constructor({ listeningModel, speechModel, speaker, realtimeConfig, name } = {}) {
|
|
196
|
+
super({
|
|
197
|
+
component: "VOICE",
|
|
198
|
+
name
|
|
199
|
+
});
|
|
200
|
+
this.listeningModel = listeningModel;
|
|
201
|
+
this.speechModel = speechModel;
|
|
202
|
+
this.speaker = speaker;
|
|
203
|
+
this.realtimeConfig = realtimeConfig;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Custom serialization for tracing/observability spans.
|
|
207
|
+
* Excludes `apiKey` from listeningModel / speechModel / realtimeConfig
|
|
208
|
+
* and any provider-specific state held by subclasses. Subclasses that
|
|
209
|
+
* need to expose additional non-sensitive fields can override.
|
|
210
|
+
*/
|
|
211
|
+
serializeForSpan() {
|
|
212
|
+
return {
|
|
213
|
+
component: "VOICE",
|
|
214
|
+
name: this.name,
|
|
215
|
+
speaker: this.speaker,
|
|
216
|
+
listeningModel: this.listeningModel ? { name: this.listeningModel.name } : void 0,
|
|
217
|
+
speechModel: this.speechModel ? { name: this.speechModel.name } : void 0,
|
|
218
|
+
realtimeModel: this.realtimeConfig?.model
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
updateConfig(_options) {
|
|
222
|
+
this.logger.debug("updateConfig not implemented by this voice provider");
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Initializes a WebSocket or WebRTC connection for real-time communication
|
|
226
|
+
* @returns Promise that resolves when the connection is established
|
|
227
|
+
*/
|
|
228
|
+
async connect(_options) {
|
|
229
|
+
this.logger.debug("connect not implemented by this voice provider");
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Relay audio data to the voice provider for real-time processing
|
|
233
|
+
* @param audioData Audio data to relay
|
|
234
|
+
*/
|
|
235
|
+
async send(_audioData) {
|
|
236
|
+
this.logger.debug("relay not implemented by this voice provider");
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Trigger voice providers to respond
|
|
240
|
+
*/
|
|
241
|
+
async answer(_options) {
|
|
242
|
+
this.logger.debug("answer not implemented by this voice provider");
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Equip the voice provider with instructions
|
|
246
|
+
* @param instructions Instructions to add
|
|
247
|
+
*/
|
|
248
|
+
addInstructions(_instructions) {}
|
|
249
|
+
/**
|
|
250
|
+
* Equip the voice provider with tools
|
|
251
|
+
* @param tools Array of tools to add
|
|
252
|
+
*/
|
|
253
|
+
addTools(_tools) {}
|
|
254
|
+
/**
|
|
255
|
+
* Disconnect from the WebSocket or WebRTC connection
|
|
256
|
+
*/
|
|
257
|
+
close() {
|
|
258
|
+
this.logger.debug("close not implemented by this voice provider");
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Register an event listener
|
|
262
|
+
* @param event Event name (e.g., 'speaking', 'writing', 'error')
|
|
263
|
+
* @param callback Callback function that receives event data
|
|
264
|
+
*/
|
|
265
|
+
on(_event, _callback) {
|
|
266
|
+
this.logger.debug("on not implemented by this voice provider");
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Remove an event listener
|
|
270
|
+
* @param event Event name (e.g., 'speaking', 'writing', 'error')
|
|
271
|
+
* @param callback Callback function to remove
|
|
272
|
+
*/
|
|
273
|
+
off(_event, _callback) {
|
|
274
|
+
this.logger.debug("off not implemented by this voice provider");
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Get available speakers/voices
|
|
278
|
+
* @returns Array of available voice IDs and their metadata
|
|
279
|
+
*/
|
|
280
|
+
getSpeakers() {
|
|
281
|
+
this.logger.debug("getSpeakers not implemented by this voice provider");
|
|
282
|
+
return Promise.resolve([]);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get available speakers/voices
|
|
286
|
+
* @returns Array of available voice IDs and their metadata
|
|
287
|
+
*/
|
|
288
|
+
getListener() {
|
|
289
|
+
this.logger.debug("getListener not implemented by this voice provider");
|
|
290
|
+
return Promise.resolve({ enabled: false });
|
|
291
|
+
}
|
|
264
292
|
};
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (keyFilename) {
|
|
281
|
-
resolved.keyFilename = keyFilename;
|
|
282
|
-
}
|
|
283
|
-
const credentials = modelConfig?.credentials ?? fallback.credentials;
|
|
284
|
-
if (credentials) {
|
|
285
|
-
resolved.credentials = credentials;
|
|
286
|
-
}
|
|
287
|
-
return resolved;
|
|
293
|
+
//#endregion
|
|
294
|
+
//#region src/index.ts
|
|
295
|
+
const resolveAuthConfig = (modelConfig, fallback, vertexConfig) => {
|
|
296
|
+
const resolved = {};
|
|
297
|
+
if (vertexConfig?.vertexAI) {
|
|
298
|
+
const projectId = vertexConfig.project || process.env.GOOGLE_CLOUD_PROJECT;
|
|
299
|
+
if (projectId) resolved.projectId = projectId;
|
|
300
|
+
}
|
|
301
|
+
const apiKey = modelConfig?.apiKey ?? fallback.apiKey;
|
|
302
|
+
if (apiKey && !vertexConfig?.vertexAI) resolved.apiKey = apiKey;
|
|
303
|
+
const keyFilename = modelConfig?.keyFilename ?? fallback.keyFilename;
|
|
304
|
+
if (keyFilename) resolved.keyFilename = keyFilename;
|
|
305
|
+
const credentials = modelConfig?.credentials ?? fallback.credentials;
|
|
306
|
+
if (credentials) resolved.credentials = credentials;
|
|
307
|
+
return resolved;
|
|
288
308
|
};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
if (config.apiKey && !vertexConfig?.vertexAI) {
|
|
298
|
-
options.apiKey = config.apiKey;
|
|
299
|
-
}
|
|
300
|
-
if (config.projectId) {
|
|
301
|
-
options.projectId = config.projectId;
|
|
302
|
-
}
|
|
303
|
-
return options;
|
|
309
|
+
const buildAuthOptions = (config, vertexConfig) => {
|
|
310
|
+
const options = {};
|
|
311
|
+
if (config.credentials) options.credentials = config.credentials;
|
|
312
|
+
if (config.keyFilename) options.keyFilename = config.keyFilename;
|
|
313
|
+
if (config.apiKey && !vertexConfig?.vertexAI) options.apiKey = config.apiKey;
|
|
314
|
+
if (config.projectId) options.projectId = config.projectId;
|
|
315
|
+
return options;
|
|
304
316
|
};
|
|
305
|
-
|
|
317
|
+
const DEFAULT_VOICE = "en-US-Casual-K";
|
|
318
|
+
/**
|
|
319
|
+
* GoogleVoice class provides Text-to-Speech and Speech-to-Text capabilities using Google Cloud services.
|
|
320
|
+
* Supports both standard Google Cloud API authentication and Vertex AI mode for enterprise deployments.
|
|
321
|
+
*
|
|
322
|
+
* @class GoogleVoice
|
|
323
|
+
* @extends MastraVoice
|
|
324
|
+
*
|
|
325
|
+
* @example Standard usage with API key
|
|
326
|
+
* ```typescript
|
|
327
|
+
* const voice = new GoogleVoice({
|
|
328
|
+
* speechModel: { apiKey: 'your-api-key' },
|
|
329
|
+
* speaker: 'en-US-Studio-O',
|
|
330
|
+
* });
|
|
331
|
+
* ```
|
|
332
|
+
*
|
|
333
|
+
* @example Vertex AI mode (recommended for production)
|
|
334
|
+
* ```typescript
|
|
335
|
+
* const voice = new GoogleVoice({
|
|
336
|
+
* vertexAI: true,
|
|
337
|
+
* project: 'your-gcp-project',
|
|
338
|
+
* location: 'us-central1',
|
|
339
|
+
* speaker: 'en-US-Studio-O',
|
|
340
|
+
* });
|
|
341
|
+
* ```
|
|
342
|
+
*
|
|
343
|
+
* @example Vertex AI with service account
|
|
344
|
+
* ```typescript
|
|
345
|
+
* const voice = new GoogleVoice({
|
|
346
|
+
* vertexAI: true,
|
|
347
|
+
* project: 'your-gcp-project',
|
|
348
|
+
* speechModel: {
|
|
349
|
+
* keyFilename: '/path/to/service-account.json',
|
|
350
|
+
* },
|
|
351
|
+
* });
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
306
354
|
var GoogleVoice = class extends MastraVoice {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
config.model = "long";
|
|
508
|
-
}
|
|
509
|
-
let recognizer = options.recognizer;
|
|
510
|
-
if (!recognizer) {
|
|
511
|
-
const project = this.project || await this.getV2SpeechClient().getProjectId();
|
|
512
|
-
recognizer = `projects/${project}/locations/global/recognizers/_`;
|
|
513
|
-
}
|
|
514
|
-
const request = {
|
|
515
|
-
recognizer,
|
|
516
|
-
config,
|
|
517
|
-
content: buffer
|
|
518
|
-
};
|
|
519
|
-
const client = this.getV2SpeechClient();
|
|
520
|
-
const [response] = await client.recognize(request);
|
|
521
|
-
return this.extractTranscription(response?.results);
|
|
522
|
-
}
|
|
523
|
-
extractTranscription(results) {
|
|
524
|
-
if (!results || results.length === 0) {
|
|
525
|
-
throw new Error("No transcription results returned");
|
|
526
|
-
}
|
|
527
|
-
const transcription = results.map((result) => {
|
|
528
|
-
if (!result.alternatives || result.alternatives.length === 0) {
|
|
529
|
-
return "";
|
|
530
|
-
}
|
|
531
|
-
return result.alternatives[0].transcript || "";
|
|
532
|
-
}).filter((text) => text.length > 0).join(" ");
|
|
533
|
-
if (!transcription) {
|
|
534
|
-
throw new Error("No valid transcription found in results");
|
|
535
|
-
}
|
|
536
|
-
return transcription;
|
|
537
|
-
}
|
|
355
|
+
ttsClient;
|
|
356
|
+
speechClient;
|
|
357
|
+
speechClientV2;
|
|
358
|
+
speechOptionsV2;
|
|
359
|
+
vertexAI;
|
|
360
|
+
project;
|
|
361
|
+
location;
|
|
362
|
+
/**
|
|
363
|
+
* Creates an instance of GoogleVoice
|
|
364
|
+
* @param {GoogleVoiceConfig} config - Configuration options
|
|
365
|
+
* @param {GoogleModelConfig} [config.speechModel] - Configuration for speech synthesis
|
|
366
|
+
* @param {GoogleModelConfig} [config.listeningModel] - Configuration for speech recognition
|
|
367
|
+
* @param {string} [config.speaker] - Default voice ID to use for speech synthesis
|
|
368
|
+
* @param {boolean} [config.vertexAI] - Enable Vertex AI mode
|
|
369
|
+
* @param {string} [config.project] - Google Cloud project ID (required for Vertex AI)
|
|
370
|
+
* @param {string} [config.location] - Google Cloud region (default: 'us-central1')
|
|
371
|
+
*/
|
|
372
|
+
constructor({ listeningModel, speechModel, speaker, vertexAI = false, project, location } = {}) {
|
|
373
|
+
const defaultApiKey = process.env.GOOGLE_API_KEY;
|
|
374
|
+
const defaultKeyFilename = process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
|
375
|
+
const defaultSpeaker = DEFAULT_VOICE;
|
|
376
|
+
const resolvedProject = project || process.env.GOOGLE_CLOUD_PROJECT;
|
|
377
|
+
const resolvedLocation = location || process.env.GOOGLE_CLOUD_LOCATION || "us-central1";
|
|
378
|
+
if (vertexAI && !resolvedProject) throw new Error("Google Cloud project ID is required when using Vertex AI. Set GOOGLE_CLOUD_PROJECT environment variable or pass project to constructor.");
|
|
379
|
+
const vertexConfig = {
|
|
380
|
+
vertexAI,
|
|
381
|
+
project: resolvedProject
|
|
382
|
+
};
|
|
383
|
+
const sharedFallback = {
|
|
384
|
+
apiKey: defaultApiKey ?? speechModel?.apiKey ?? listeningModel?.apiKey,
|
|
385
|
+
keyFilename: defaultKeyFilename ?? speechModel?.keyFilename ?? listeningModel?.keyFilename,
|
|
386
|
+
credentials: speechModel?.credentials ?? listeningModel?.credentials,
|
|
387
|
+
projectId: resolvedProject
|
|
388
|
+
};
|
|
389
|
+
const speechAuthConfig = resolveAuthConfig(speechModel, sharedFallback, vertexConfig);
|
|
390
|
+
const listeningAuthConfig = resolveAuthConfig(listeningModel, sharedFallback, vertexConfig);
|
|
391
|
+
super({
|
|
392
|
+
speechModel: {
|
|
393
|
+
name: "",
|
|
394
|
+
apiKey: speechAuthConfig.apiKey ?? defaultApiKey
|
|
395
|
+
},
|
|
396
|
+
listeningModel: {
|
|
397
|
+
name: "",
|
|
398
|
+
apiKey: listeningAuthConfig.apiKey ?? defaultApiKey
|
|
399
|
+
},
|
|
400
|
+
speaker: speaker ?? defaultSpeaker
|
|
401
|
+
});
|
|
402
|
+
this.vertexAI = vertexAI;
|
|
403
|
+
this.project = resolvedProject;
|
|
404
|
+
this.location = resolvedLocation;
|
|
405
|
+
const ttsOptions = buildAuthOptions(speechAuthConfig, {
|
|
406
|
+
vertexAI,
|
|
407
|
+
location: resolvedLocation
|
|
408
|
+
});
|
|
409
|
+
const speechOptions = buildAuthOptions(listeningAuthConfig, {
|
|
410
|
+
vertexAI,
|
|
411
|
+
location: resolvedLocation
|
|
412
|
+
});
|
|
413
|
+
this.ttsClient = new TextToSpeechClient(ttsOptions);
|
|
414
|
+
this.speechClient = new SpeechClient(speechOptions);
|
|
415
|
+
this.speechOptionsV2 = speechOptions;
|
|
416
|
+
}
|
|
417
|
+
getV2SpeechClient() {
|
|
418
|
+
if (!this.speechClientV2) this.speechClientV2 = new v2.SpeechClient(this.speechOptionsV2);
|
|
419
|
+
return this.speechClientV2;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Check if Vertex AI mode is enabled
|
|
423
|
+
* @returns {boolean} True if using Vertex AI
|
|
424
|
+
*/
|
|
425
|
+
isUsingVertexAI() {
|
|
426
|
+
return this.vertexAI;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Get the configured Google Cloud project ID
|
|
430
|
+
* @returns {string | undefined} The project ID or undefined if not set
|
|
431
|
+
*/
|
|
432
|
+
getProject() {
|
|
433
|
+
return this.project;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Get the configured Google Cloud location/region
|
|
437
|
+
* @returns {string} The location (default: 'us-central1')
|
|
438
|
+
*/
|
|
439
|
+
getLocation() {
|
|
440
|
+
return this.location;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Gets a list of available voices
|
|
444
|
+
* @returns {Promise<Array<{voiceId: string, languageCodes: string[]}>>} List of available voices and their supported languages. Default language is en-US.
|
|
445
|
+
*/
|
|
446
|
+
async getSpeakers({ languageCode = "en-US" } = {}) {
|
|
447
|
+
const [response] = await this.ttsClient.listVoices({ languageCode });
|
|
448
|
+
return (response?.voices || []).filter((voice) => voice.name && voice.languageCodes).map((voice) => ({
|
|
449
|
+
voiceId: voice.name,
|
|
450
|
+
languageCodes: voice.languageCodes
|
|
451
|
+
}));
|
|
452
|
+
}
|
|
453
|
+
async streamToString(stream) {
|
|
454
|
+
const chunks = [];
|
|
455
|
+
for await (const chunk of stream) if (typeof chunk === "string") chunks.push(Buffer.from(chunk));
|
|
456
|
+
else chunks.push(chunk);
|
|
457
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Converts text to speech.
|
|
461
|
+
*
|
|
462
|
+
* When `input` is a string or stream, builds a text-only request (existing behaviour).
|
|
463
|
+
* Pass `options.input` to send richer proto fields (ssml, markup, prompt,
|
|
464
|
+
* customPronunciations, multiSpeakerMarkup) and `options.voice` for fields
|
|
465
|
+
* like modelName or multiSpeakerVoiceConfig.
|
|
466
|
+
*
|
|
467
|
+
* @param {string | NodeJS.ReadableStream} input - Text or stream to convert to speech
|
|
468
|
+
* @param {GoogleSpeakOptions} [options] - Speech synthesis options
|
|
469
|
+
* @returns {Promise<NodeJS.ReadableStream>} Stream of synthesised audio. Default encoding is LINEAR16.
|
|
470
|
+
*/
|
|
471
|
+
async speak(input, options) {
|
|
472
|
+
const defaultVoiceName = options?.speaker || this.speaker;
|
|
473
|
+
const defaultLanguageCode = options?.languageCode || defaultVoiceName?.split("-").slice(0, 2).join("-") || "en-US";
|
|
474
|
+
const requestInput = options?.input ? { ...options.input } : { text: typeof input === "string" ? input : await this.streamToString(input) };
|
|
475
|
+
if (options?.input && !options.input.text && !options.input.ssml && !options.input.markup && !options.input.multiSpeakerMarkup) requestInput.text = typeof input === "string" ? input : await this.streamToString(input);
|
|
476
|
+
const request = {
|
|
477
|
+
input: requestInput,
|
|
478
|
+
voice: {
|
|
479
|
+
name: defaultVoiceName,
|
|
480
|
+
languageCode: defaultLanguageCode,
|
|
481
|
+
...options?.voice
|
|
482
|
+
},
|
|
483
|
+
audioConfig: options?.audioConfig || { audioEncoding: "LINEAR16" }
|
|
484
|
+
};
|
|
485
|
+
const [response] = await this.ttsClient.synthesizeSpeech(request);
|
|
486
|
+
if (!response.audioContent) throw new Error("No audio content returned.");
|
|
487
|
+
if (typeof response.audioContent === "string") throw new Error("Audio content is a string.");
|
|
488
|
+
const stream = new PassThrough();
|
|
489
|
+
stream.end(Buffer.from(response.audioContent));
|
|
490
|
+
return stream;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Checks if listening capabilities are enabled.
|
|
494
|
+
*
|
|
495
|
+
* @returns {Promise<{ enabled: boolean }>}
|
|
496
|
+
*/
|
|
497
|
+
async getListener() {
|
|
498
|
+
return { enabled: true };
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Converts speech to text using Cloud Speech-to-Text v1 or v2.
|
|
502
|
+
*
|
|
503
|
+
* Pass `{ v2: true }` in options to use the v2 API, which supports additional
|
|
504
|
+
* audio formats like AAC-in-MP4 (iOS Safari) via `autoDecodingConfig` or
|
|
505
|
+
* `explicitDecodingConfig`. The v1 path remains the default.
|
|
506
|
+
*
|
|
507
|
+
* @param {NodeJS.ReadableStream} audioStream - Audio stream to transcribe. Default encoding is LINEAR16.
|
|
508
|
+
* @param {GoogleListenOptions} [options] - Recognition options
|
|
509
|
+
* @returns {Promise<string>} Transcribed text
|
|
510
|
+
*/
|
|
511
|
+
async listen(audioStream, options) {
|
|
512
|
+
const chunks = [];
|
|
513
|
+
for await (const chunk of audioStream) if (typeof chunk === "string") chunks.push(Buffer.from(chunk));
|
|
514
|
+
else chunks.push(chunk);
|
|
515
|
+
const buffer = Buffer.concat(chunks);
|
|
516
|
+
if (options && "v2" in options && options.v2) return this.recognizeV2(buffer, options);
|
|
517
|
+
return this.recognizeV1(buffer, options);
|
|
518
|
+
}
|
|
519
|
+
async recognizeV1(buffer, options) {
|
|
520
|
+
const request = {
|
|
521
|
+
config: {
|
|
522
|
+
encoding: "LINEAR16",
|
|
523
|
+
languageCode: "en-US",
|
|
524
|
+
...options?.config
|
|
525
|
+
},
|
|
526
|
+
audio: { content: buffer.toString("base64") }
|
|
527
|
+
};
|
|
528
|
+
const [response] = await this.speechClient.recognize(request);
|
|
529
|
+
return this.extractTranscription(response?.results);
|
|
530
|
+
}
|
|
531
|
+
async recognizeV2(buffer, options) {
|
|
532
|
+
const config = { ...options.config };
|
|
533
|
+
if (!config.autoDecodingConfig && !config.explicitDecodingConfig) config.autoDecodingConfig = {};
|
|
534
|
+
if (!config.languageCodes || config.languageCodes.length === 0) config.languageCodes = ["en-US"];
|
|
535
|
+
if (!config.model) config.model = "long";
|
|
536
|
+
let recognizer = options.recognizer;
|
|
537
|
+
if (!recognizer) recognizer = `projects/${this.project || await this.getV2SpeechClient().getProjectId()}/locations/global/recognizers/_`;
|
|
538
|
+
const request = {
|
|
539
|
+
recognizer,
|
|
540
|
+
config,
|
|
541
|
+
content: buffer
|
|
542
|
+
};
|
|
543
|
+
const [response] = await this.getV2SpeechClient().recognize(request);
|
|
544
|
+
return this.extractTranscription(response?.results);
|
|
545
|
+
}
|
|
546
|
+
extractTranscription(results) {
|
|
547
|
+
if (!results || results.length === 0) throw new Error("No transcription results returned");
|
|
548
|
+
const transcription = results.map((result) => {
|
|
549
|
+
if (!result.alternatives || result.alternatives.length === 0) return "";
|
|
550
|
+
return result.alternatives[0].transcript || "";
|
|
551
|
+
}).filter((text) => text.length > 0).join(" ");
|
|
552
|
+
if (!transcription) throw new Error("No valid transcription found in results");
|
|
553
|
+
return transcription;
|
|
554
|
+
}
|
|
538
555
|
};
|
|
539
|
-
|
|
556
|
+
//#endregion
|
|
540
557
|
export { GoogleVoice };
|
|
541
|
-
|
|
558
|
+
|
|
542
559
|
//# sourceMappingURL=index.js.map
|