@mastra/voice-google 0.13.0-alpha.0 → 0.14.0-alpha.0

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 CHANGED
@@ -1,5 +1,38 @@
1
1
  # @mastra/voice-google
2
2
 
3
+ ## 0.14.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added support for SSML, custom pronunciations, multi-speaker markup, and Gemini-TTS model selection in `GoogleVoice.speak()`. Existing text-only usage is unchanged. ([#19425](https://github.com/mastra-ai/mastra/pull/19425))
8
+
9
+ **New `options.input` fields:** pass `ssml`, `markup`, `customPronunciations`, `multiSpeakerMarkup`, or `prompt` (for Gemini-TTS style steering) directly to the Google Cloud TTS API.
10
+
11
+ **New `options.voice` fields:** set `modelName` (e.g. `gemini-2.5-flash-preview-tts`) or `multiSpeakerVoiceConfig` alongside the default `name` and `languageCode`.
12
+
13
+ ```ts
14
+ // SSML with custom pronunciations
15
+ await voice.speak('Give Metacam to the patient.', {
16
+ input: {
17
+ ssml: '<speak>Give <phoneme alphabet="ipa" ph="mɛtəˈkæm">Metacam</phoneme>.</speak>',
18
+ },
19
+ });
20
+
21
+ // Gemini-TTS with prompt-driven styling
22
+ await voice.speak('Hello!', {
23
+ voice: { name: 'Kore', modelName: 'gemini-2.5-flash-preview-tts' },
24
+ input: { prompt: 'Warm, calm tone.' },
25
+ });
26
+ ```
27
+
28
+ - Added Cloud Speech-to-Text v2 support to `GoogleVoice.listen()`. Pass `{ v2: true }` in options to use the v2 API, which supports additional audio formats like AAC-in-MP4 (iOS Safari) via `autoDecodingConfig` or `explicitDecodingConfig`. The v1 path remains the default — no breaking changes. ([#19422](https://github.com/mastra-ai/mastra/pull/19422))
29
+
30
+ ## 0.13.0
31
+
32
+ ### Minor Changes
33
+
34
+ - Random bump ([#18178](https://github.com/mastra-ai/mastra/pull/18178))
35
+
3
36
  ## 0.13.0-alpha.0
4
37
 
5
38
  ### Minor Changes
@@ -231,6 +231,7 @@ declare namespace _ai_sdk_provider_utils {
231
231
  Validator,
232
232
  asSchema,
233
233
  asValidator,
234
+ cancelResponseBody,
234
235
  combineHeaders,
235
236
  convertAsyncIteratorToReadableStream,
236
237
  convertBase64ToUint8Array,
@@ -249,13 +250,16 @@ declare namespace _ai_sdk_provider_utils {
249
250
  dynamicTool,
250
251
  executeTool,
251
252
  extractResponseHeaders,
253
+ fetchWithValidatedRedirects,
252
254
  generateId,
253
255
  getErrorMessage,
254
256
  getFromApi,
255
257
  getRuntimeEnvironmentUserAgent,
256
258
  injectJsonInstructionIntoMessages,
257
259
  isAbortError,
260
+ isBrowserRuntime,
258
261
  isParsableJson,
262
+ isSameOrigin,
259
263
  isUrlSupported,
260
264
  isValidator,
261
265
  jsonSchema,
@@ -494,6 +498,20 @@ export declare type CallSettings = {
494
498
  */
495
499
  export declare type CallWarning = LanguageModelV2CallWarning;
496
500
 
501
+ /**
502
+ * Cancels a response body to release the underlying connection.
503
+ *
504
+ * When a fetch Response is rejected without consuming its body (e.g. a failed
505
+ * status code, an open-redirect rejection, or a Content-Length that exceeds the
506
+ * size limit), the underlying TCP socket is not returned to the connection pool
507
+ * and may stay open until the process runs out of file descriptors. Cancelling
508
+ * the body avoids this leak.
509
+ *
510
+ * Errors thrown while cancelling are ignored: the body may already be locked,
511
+ * disturbed, or absent, none of which should mask the original rejection.
512
+ */
513
+ declare function cancelResponseBody(response: Response): Promise<void>;
514
+
497
515
  export declare interface ChatInit<UI_MESSAGE extends UIMessage> {
498
516
  /**
499
517
  * A unique identifier for the chat. If not provided, a random one will be
@@ -956,7 +974,21 @@ export declare type CreateUIMessage<UI_MESSAGE extends UIMessage> = Omit<UI_MESS
956
974
  role?: UI_MESSAGE['role'];
957
975
  };
958
976
 
959
- export declare function createUIMessageStream<UI_MESSAGE extends UIMessage>({ execute, onError, originalMessages, onFinish, generateId, }: {
977
+ /**
978
+ * Creates a UI message stream that can be used to send messages to the client.
979
+ *
980
+ * @param options.execute - A function that is called with a writer to write UI message chunks to the stream.
981
+ * @param options.onError - A function that extracts an error message from an error. Defaults to `() => 'An error occurred.'` so server-side error details are not leaked to the client; supply your own to surface richer messages.
982
+ * @param options.originalMessages - The original messages. If provided, persistence mode is assumed
983
+ * and a message ID is provided for the response message.
984
+ * @param options.onStepFinish - A callback that is called when each step finishes. Useful for persisting intermediate messages.
985
+ * @param options.onFinish - A callback that is called when the stream finishes.
986
+ * @param options.generateId - A function that generates a unique ID. Defaults to the built-in ID generator.
987
+ *
988
+ * @returns A `ReadableStream` of UI message chunks.
989
+ */
990
+ export declare function createUIMessageStream<UI_MESSAGE extends UIMessage>({ execute, onError, // prevent leaking server error details to the client by default
991
+ originalMessages, onFinish, generateId, }: {
960
992
  execute: (options: {
961
993
  writer: UIMessageStreamWriter<UI_MESSAGE>;
962
994
  }) => Promise<void> | void;
@@ -2129,6 +2161,36 @@ declare function extractResponseHeaders(response: Response): {
2129
2161
  */
2130
2162
  declare type FetchFunction = typeof globalThis.fetch;
2131
2163
 
2164
+ /**
2165
+ * Fetches a URL while enforcing the SSRF download guard on every hop.
2166
+ *
2167
+ * Redirects are followed manually (`redirect: 'manual'`) so each hop is
2168
+ * validated with {@link validateDownloadUrl} *before* it is requested. Relying
2169
+ * on the default `redirect: 'follow'` would issue the request to a redirect
2170
+ * target (e.g. an internal address) before we ever see its URL, defeating the
2171
+ * SSRF guard.
2172
+ *
2173
+ * A `redirect: 'manual'` request yields an unreadable opaque response in the
2174
+ * browser (and in other spec-compliant fetch implementations), so the redirect
2175
+ * target cannot be validated here. In a real browser this is safe to follow
2176
+ * natively because SSRF is not reachable (fetch is constrained by CORS and
2177
+ * cannot reach a server's internal network or cloud-metadata). On any other
2178
+ * runtime we cannot validate the hop, so we fail closed rather than follow it
2179
+ * blindly and bypass the SSRF guard.
2180
+ *
2181
+ * The returned response is the final (non-redirect) response. The caller is
2182
+ * responsible for checking `response.ok` and reading the body.
2183
+ *
2184
+ * @throws DownloadError if a hop is unsafe, the redirect limit is exceeded, or
2185
+ * a redirect cannot be validated on a non-browser runtime.
2186
+ */
2187
+ declare function fetchWithValidatedRedirects({ url, headers, abortSignal, maxRedirects, }: {
2188
+ url: string;
2189
+ headers?: HeadersInit;
2190
+ abortSignal?: AbortSignal;
2191
+ maxRedirects?: number;
2192
+ }): Promise<Response>;
2193
+
2132
2194
  /**
2133
2195
  File content part of a prompt. It contains a file.
2134
2196
  */
@@ -2261,7 +2323,7 @@ declare interface GatewayGenerationInfoParams {
2261
2323
  id: string;
2262
2324
  }
2263
2325
 
2264
- declare type GatewayImageModelId = 'bfl/flux-2-flex' | 'bfl/flux-2-klein-4b' | 'bfl/flux-2-klein-9b' | 'bfl/flux-2-max' | 'bfl/flux-2-pro' | 'bfl/flux-kontext-max' | 'bfl/flux-kontext-pro' | 'bfl/flux-pro-1.0-fill' | 'bfl/flux-pro-1.1' | 'bfl/flux-pro-1.1-ultra' | 'bytedance/seedream-4.0' | 'bytedance/seedream-4.5' | 'bytedance/seedream-5.0-lite' | 'google/imagen-4.0-fast-generate-001' | 'google/imagen-4.0-generate-001' | 'google/imagen-4.0-ultra-generate-001' | 'openai/gpt-image-1' | 'openai/gpt-image-1-mini' | 'openai/gpt-image-1.5' | 'openai/gpt-image-2' | 'prodia/flux-fast-schnell' | 'recraft/recraft-v2' | 'recraft/recraft-v3' | 'recraft/recraft-v4' | 'recraft/recraft-v4-pro' | 'xai/grok-imagine-image' | 'xai/grok-imagine-image-pro' | (string & {});
2326
+ declare type GatewayImageModelId = 'bfl/flux-2-flex' | 'bfl/flux-2-klein-4b' | 'bfl/flux-2-klein-9b' | 'bfl/flux-2-max' | 'bfl/flux-2-pro' | 'bfl/flux-kontext-max' | 'bfl/flux-kontext-pro' | 'bfl/flux-pro-1.0-fill' | 'bfl/flux-pro-1.1' | 'bfl/flux-pro-1.1-ultra' | 'bytedance/seedream-4.0' | 'bytedance/seedream-4.5' | 'bytedance/seedream-5.0-lite' | 'google/imagen-4.0-fast-generate-001' | 'google/imagen-4.0-generate-001' | 'google/imagen-4.0-ultra-generate-001' | 'openai/gpt-image-1' | 'openai/gpt-image-1-mini' | 'openai/gpt-image-1.5' | 'openai/gpt-image-2' | 'prodia/flux-fast-schnell' | 'quiverai/arrow-1.1' | 'recraft/recraft-v2' | 'recraft/recraft-v3' | 'recraft/recraft-v4' | 'recraft/recraft-v4-pro' | 'recraft/recraft-v4.1' | 'recraft/recraft-v4.1-pro' | 'recraft/recraft-v4.1-utility' | 'recraft/recraft-v4.1-utility-pro' | 'xai/grok-imagine-image' | (string & {});
2265
2327
 
2266
2328
  declare interface GatewayLanguageModelEntry {
2267
2329
  /**
@@ -2312,7 +2374,7 @@ declare interface GatewayLanguageModelEntry {
2312
2374
 
2313
2375
  declare type GatewayLanguageModelSpecification = Pick<LanguageModelV2, 'specificationVersion' | 'provider' | 'modelId'>;
2314
2376
 
2315
- export declare type GatewayModelId = 'alibaba/qwen-3-14b' | 'alibaba/qwen-3-235b' | 'alibaba/qwen-3-30b' | 'alibaba/qwen-3-32b' | 'alibaba/qwen-3.6-max-preview' | 'alibaba/qwen3-235b-a22b-thinking' | 'alibaba/qwen3-coder' | 'alibaba/qwen3-coder-30b-a3b' | 'alibaba/qwen3-coder-next' | 'alibaba/qwen3-coder-plus' | 'alibaba/qwen3-max' | 'alibaba/qwen3-max-preview' | 'alibaba/qwen3-max-thinking' | 'alibaba/qwen3-next-80b-a3b-instruct' | 'alibaba/qwen3-next-80b-a3b-thinking' | 'alibaba/qwen3-vl-235b-a22b-instruct' | 'alibaba/qwen3-vl-instruct' | 'alibaba/qwen3-vl-thinking' | 'alibaba/qwen3.5-flash' | 'alibaba/qwen3.5-plus' | 'alibaba/qwen3.6-27b' | 'alibaba/qwen3.6-plus' | 'amazon/nova-2-lite' | 'amazon/nova-lite' | 'amazon/nova-micro' | 'amazon/nova-pro' | 'anthropic/claude-3-haiku' | 'anthropic/claude-3.5-haiku' | 'anthropic/claude-3.7-sonnet' | 'anthropic/claude-haiku-4.5' | 'anthropic/claude-opus-4' | 'anthropic/claude-opus-4.1' | 'anthropic/claude-opus-4.5' | 'anthropic/claude-opus-4.6' | 'anthropic/claude-opus-4.7' | 'anthropic/claude-sonnet-4' | 'anthropic/claude-sonnet-4.5' | 'anthropic/claude-sonnet-4.6' | 'arcee-ai/trinity-large-preview' | 'arcee-ai/trinity-large-thinking' | 'arcee-ai/trinity-mini' | 'bytedance/seed-1.6' | 'bytedance/seed-1.8' | 'cohere/command-a' | 'deepseek/deepseek-r1' | 'deepseek/deepseek-v3' | 'deepseek/deepseek-v3.1' | 'deepseek/deepseek-v3.1-terminus' | 'deepseek/deepseek-v3.2' | 'deepseek/deepseek-v3.2-thinking' | 'deepseek/deepseek-v4-flash' | 'deepseek/deepseek-v4-pro' | 'google/gemini-2.0-flash' | 'google/gemini-2.0-flash-lite' | 'google/gemini-2.5-flash' | 'google/gemini-2.5-flash-image' | 'google/gemini-2.5-flash-lite' | 'google/gemini-2.5-pro' | 'google/gemini-3-flash' | 'google/gemini-3-pro-image' | 'google/gemini-3-pro-preview' | 'google/gemini-3.1-flash-image-preview' | 'google/gemini-3.1-flash-lite-preview' | 'google/gemini-3.1-pro-preview' | 'google/gemma-4-26b-a4b-it' | 'google/gemma-4-31b-it' | 'inception/mercury-2' | 'inception/mercury-coder-small' | 'interfaze/interfaze-beta' | 'kwaipilot/kat-coder-pro-v1' | 'kwaipilot/kat-coder-pro-v2' | 'meituan/longcat-flash-chat' | 'meituan/longcat-flash-thinking-2601' | 'meta/llama-3.1-70b' | 'meta/llama-3.1-8b' | 'meta/llama-3.2-11b' | 'meta/llama-3.2-1b' | 'meta/llama-3.2-3b' | 'meta/llama-3.2-90b' | 'meta/llama-3.3-70b' | 'meta/llama-4-maverick' | 'meta/llama-4-scout' | 'minimax/minimax-m2' | 'minimax/minimax-m2.1' | 'minimax/minimax-m2.1-lightning' | 'minimax/minimax-m2.5' | 'minimax/minimax-m2.5-highspeed' | 'minimax/minimax-m2.7' | 'minimax/minimax-m2.7-highspeed' | 'mistral/codestral' | 'mistral/devstral-2' | 'mistral/devstral-small' | 'mistral/devstral-small-2' | 'mistral/magistral-medium' | 'mistral/magistral-small' | 'mistral/ministral-14b' | 'mistral/ministral-3b' | 'mistral/ministral-8b' | 'mistral/mistral-large-3' | 'mistral/mistral-medium' | 'mistral/mistral-nemo' | 'mistral/mistral-small' | 'mistral/pixtral-12b' | 'mistral/pixtral-large' | 'moonshotai/kimi-k2' | 'moonshotai/kimi-k2-thinking' | 'moonshotai/kimi-k2-thinking-turbo' | 'moonshotai/kimi-k2-turbo' | 'moonshotai/kimi-k2.5' | 'moonshotai/kimi-k2.6' | 'morph/morph-v3-fast' | 'morph/morph-v3-large' | 'nvidia/nemotron-3-nano-30b-a3b' | 'nvidia/nemotron-3-super-120b-a12b' | 'nvidia/nemotron-nano-12b-v2-vl' | 'nvidia/nemotron-nano-9b-v2' | 'openai/gpt-3.5-turbo' | 'openai/gpt-3.5-turbo-instruct' | 'openai/gpt-4-turbo' | 'openai/gpt-4.1' | 'openai/gpt-4.1-mini' | 'openai/gpt-4.1-nano' | 'openai/gpt-4o' | 'openai/gpt-4o-mini' | 'openai/gpt-4o-mini-search-preview' | 'openai/gpt-5' | 'openai/gpt-5-chat' | 'openai/gpt-5-codex' | 'openai/gpt-5-mini' | 'openai/gpt-5-nano' | 'openai/gpt-5-pro' | 'openai/gpt-5.1-codex' | 'openai/gpt-5.1-codex-max' | 'openai/gpt-5.1-codex-mini' | 'openai/gpt-5.1-instant' | 'openai/gpt-5.1-thinking' | 'openai/gpt-5.2' | 'openai/gpt-5.2-chat' | 'openai/gpt-5.2-codex' | 'openai/gpt-5.2-pro' | 'openai/gpt-5.3-chat' | 'openai/gpt-5.3-codex' | 'openai/gpt-5.4' | 'openai/gpt-5.4-mini' | 'openai/gpt-5.4-nano' | 'openai/gpt-5.4-pro' | 'openai/gpt-5.5' | 'openai/gpt-5.5-pro' | 'openai/gpt-oss-120b' | 'openai/gpt-oss-20b' | 'openai/gpt-oss-safeguard-20b' | 'openai/o1' | 'openai/o3' | 'openai/o3-deep-research' | 'openai/o3-mini' | 'openai/o3-pro' | 'openai/o4-mini' | 'perplexity/sonar' | 'perplexity/sonar-pro' | 'perplexity/sonar-reasoning-pro' | 'xai/grok-3' | 'xai/grok-3-fast' | 'xai/grok-3-mini' | 'xai/grok-3-mini-fast' | 'xai/grok-4' | 'xai/grok-4-fast-non-reasoning' | 'xai/grok-4-fast-reasoning' | 'xai/grok-4.1-fast-non-reasoning' | 'xai/grok-4.1-fast-reasoning' | 'xai/grok-4.20-multi-agent' | 'xai/grok-4.20-multi-agent-beta' | 'xai/grok-4.20-non-reasoning' | 'xai/grok-4.20-non-reasoning-beta' | 'xai/grok-4.20-reasoning' | 'xai/grok-4.20-reasoning-beta' | 'xai/grok-4.3' | 'xai/grok-code-fast-1' | 'xiaomi/mimo-v2-flash' | 'xiaomi/mimo-v2-pro' | 'xiaomi/mimo-v2.5' | 'xiaomi/mimo-v2.5-pro' | 'zai/glm-4.5' | 'zai/glm-4.5-air' | 'zai/glm-4.5v' | 'zai/glm-4.6' | 'zai/glm-4.6v' | 'zai/glm-4.6v-flash' | 'zai/glm-4.7' | 'zai/glm-4.7-flash' | 'zai/glm-4.7-flashx' | 'zai/glm-5' | 'zai/glm-5-turbo' | 'zai/glm-5.1' | 'zai/glm-5v-turbo' | (string & {});
2377
+ export declare type GatewayModelId = 'alibaba/qwen-3-14b' | 'alibaba/qwen-3-235b' | 'alibaba/qwen-3-30b' | 'alibaba/qwen-3-32b' | 'alibaba/qwen-3.6-max-preview' | 'alibaba/qwen3-235b-a22b-thinking' | 'alibaba/qwen3-coder' | 'alibaba/qwen3-coder-30b-a3b' | 'alibaba/qwen3-coder-next' | 'alibaba/qwen3-coder-plus' | 'alibaba/qwen3-max' | 'alibaba/qwen3-max-preview' | 'alibaba/qwen3-max-thinking' | 'alibaba/qwen3-next-80b-a3b-instruct' | 'alibaba/qwen3-next-80b-a3b-thinking' | 'alibaba/qwen3-vl-235b-a22b-instruct' | 'alibaba/qwen3-vl-instruct' | 'alibaba/qwen3-vl-thinking' | 'alibaba/qwen3.5-flash' | 'alibaba/qwen3.5-plus' | 'alibaba/qwen3.6-27b' | 'alibaba/qwen3.6-plus' | 'alibaba/qwen3.7-max' | 'alibaba/qwen3.7-plus' | 'amazon/nova-2-lite' | 'amazon/nova-lite' | 'amazon/nova-micro' | 'amazon/nova-pro' | 'anthropic/claude-3-haiku' | 'anthropic/claude-3.5-haiku' | 'anthropic/claude-fable-5' | 'anthropic/claude-haiku-4.5' | 'anthropic/claude-opus-4' | 'anthropic/claude-opus-4.1' | 'anthropic/claude-opus-4.5' | 'anthropic/claude-opus-4.6' | 'anthropic/claude-opus-4.7' | 'anthropic/claude-opus-4.8' | 'anthropic/claude-sonnet-4' | 'anthropic/claude-sonnet-4.5' | 'anthropic/claude-sonnet-4.6' | 'anthropic/claude-sonnet-5' | 'arcee-ai/trinity-large-preview' | 'arcee-ai/trinity-large-thinking' | 'arcee-ai/trinity-mini' | 'bytedance/seed-1.6' | 'bytedance/seed-1.8' | 'cohere/command-a' | 'deepseek/deepseek-r1' | 'deepseek/deepseek-v3' | 'deepseek/deepseek-v3.1' | 'deepseek/deepseek-v3.1-terminus' | 'deepseek/deepseek-v3.2' | 'deepseek/deepseek-v3.2-thinking' | 'deepseek/deepseek-v4-flash' | 'deepseek/deepseek-v4-pro' | 'google/gemini-2.5-flash' | 'google/gemini-2.5-flash-image' | 'google/gemini-2.5-flash-lite' | 'google/gemini-2.5-pro' | 'google/gemini-3-flash' | 'google/gemini-3-pro-image' | 'google/gemini-3-pro-preview' | 'google/gemini-3.1-flash-image' | 'google/gemini-3.1-flash-image-preview' | 'google/gemini-3.1-flash-lite' | 'google/gemini-3.1-flash-lite-image' | 'google/gemini-3.1-flash-lite-preview' | 'google/gemini-3.1-pro-preview' | 'google/gemini-3.5-flash' | 'google/gemma-4-26b-a4b-it' | 'google/gemma-4-31b-it' | 'inception/mercury-2' | 'inception/mercury-coder-small' | 'interfaze/interfaze-beta' | 'kwaipilot/kat-coder-pro-v1' | 'kwaipilot/kat-coder-pro-v2' | 'meituan/longcat-flash-chat' | 'meituan/longcat-flash-thinking-2601' | 'meta/llama-3.1-70b' | 'meta/llama-3.1-8b' | 'meta/llama-3.2-11b' | 'meta/llama-3.2-1b' | 'meta/llama-3.2-3b' | 'meta/llama-3.2-90b' | 'meta/llama-3.3-70b' | 'meta/llama-4-maverick' | 'meta/llama-4-scout' | 'minimax/minimax-m2' | 'minimax/minimax-m2.1' | 'minimax/minimax-m2.1-lightning' | 'minimax/minimax-m2.5' | 'minimax/minimax-m2.5-highspeed' | 'minimax/minimax-m2.7' | 'minimax/minimax-m2.7-highspeed' | 'minimax/minimax-m3' | 'mistral/codestral' | 'mistral/devstral-2' | 'mistral/devstral-small' | 'mistral/devstral-small-2' | 'mistral/magistral-medium' | 'mistral/magistral-small' | 'mistral/ministral-14b' | 'mistral/ministral-3b' | 'mistral/ministral-8b' | 'mistral/mistral-large-3' | 'mistral/mistral-medium' | 'mistral/mistral-medium-3.5' | 'mistral/mistral-nemo' | 'mistral/mistral-small' | 'mistral/pixtral-12b' | 'mistral/pixtral-large' | 'moonshotai/kimi-k2' | 'moonshotai/kimi-k2-thinking' | 'moonshotai/kimi-k2.5' | 'moonshotai/kimi-k2.6' | 'moonshotai/kimi-k2.7-code' | 'moonshotai/kimi-k2.7-code-highspeed' | 'morph/morph-v3-fast' | 'morph/morph-v3-large' | 'nvidia/nemotron-3-nano-30b-a3b' | 'nvidia/nemotron-3-super-120b-a12b' | 'nvidia/nemotron-3-ultra-550b-a55b' | 'nvidia/nemotron-nano-12b-v2-vl' | 'nvidia/nemotron-nano-9b-v2' | 'openai/gpt-3.5-turbo' | 'openai/gpt-3.5-turbo-instruct' | 'openai/gpt-4-turbo' | 'openai/gpt-4.1' | 'openai/gpt-4.1-mini' | 'openai/gpt-4.1-nano' | 'openai/gpt-4o' | 'openai/gpt-4o-mini' | 'openai/gpt-4o-mini-search-preview' | 'openai/gpt-5' | 'openai/gpt-5-chat' | 'openai/gpt-5-codex' | 'openai/gpt-5-mini' | 'openai/gpt-5-nano' | 'openai/gpt-5-pro' | 'openai/gpt-5.1-codex' | 'openai/gpt-5.1-codex-max' | 'openai/gpt-5.1-codex-mini' | 'openai/gpt-5.1-instant' | 'openai/gpt-5.1-thinking' | 'openai/gpt-5.2' | 'openai/gpt-5.2-chat' | 'openai/gpt-5.2-codex' | 'openai/gpt-5.2-pro' | 'openai/gpt-5.3-chat' | 'openai/gpt-5.3-codex' | 'openai/gpt-5.4' | 'openai/gpt-5.4-mini' | 'openai/gpt-5.4-nano' | 'openai/gpt-5.4-pro' | 'openai/gpt-5.5' | 'openai/gpt-5.5-pro' | 'openai/gpt-oss-120b' | 'openai/gpt-oss-20b' | 'openai/gpt-oss-safeguard-20b' | 'openai/o1' | 'openai/o3' | 'openai/o3-deep-research' | 'openai/o3-mini' | 'openai/o3-pro' | 'openai/o4-mini' | 'perplexity/sonar' | 'perplexity/sonar-pro' | 'perplexity/sonar-reasoning-pro' | 'sakana/fugu-ultra' | 'stepfun/step-3.5-flash' | 'stepfun/step-3.7-flash' | 'xai/grok-4.1-fast-non-reasoning' | 'xai/grok-4.1-fast-reasoning' | 'xai/grok-4.20-multi-agent' | 'xai/grok-4.20-multi-agent-beta' | 'xai/grok-4.20-non-reasoning' | 'xai/grok-4.20-non-reasoning-beta' | 'xai/grok-4.20-reasoning' | 'xai/grok-4.20-reasoning-beta' | 'xai/grok-4.3' | 'xai/grok-build-0.1' | 'xiaomi/mimo-v2-flash' | 'xiaomi/mimo-v2-pro' | 'xiaomi/mimo-v2.5' | 'xiaomi/mimo-v2.5-pro' | 'zai/glm-4.5' | 'zai/glm-4.5-air' | 'zai/glm-4.5v' | 'zai/glm-4.6' | 'zai/glm-4.6v' | 'zai/glm-4.6v-flash' | 'zai/glm-4.7' | 'zai/glm-4.7-flash' | 'zai/glm-4.7-flashx' | 'zai/glm-5' | 'zai/glm-5-turbo' | 'zai/glm-5.1' | 'zai/glm-5.2' | 'zai/glm-5.2-fast' | 'zai/glm-5v-turbo' | (string & {});
2316
2378
 
2317
2379
  declare interface GatewayProvider extends ProviderV2 {
2318
2380
  (modelId: GatewayModelId): LanguageModelV2;
@@ -3424,6 +3486,16 @@ export declare class InvalidToolInputError extends AISDKError {
3424
3486
 
3425
3487
  declare function isAbortError(error: unknown): error is Error;
3426
3488
 
3489
+ /**
3490
+ * Returns `true` when running in a browser.
3491
+ *
3492
+ * Detection keys on the presence of a global `window`, matching the browser
3493
+ * check used elsewhere in this package (see `getRuntimeEnvironmentUserAgent`)
3494
+ * so the SDK has a single, consistent definition of "browser". Server runtimes
3495
+ * (Node.js, Deno, Bun, edge/workers) do not define `window`.
3496
+ */
3497
+ declare function isBrowserRuntime(globalThisAny?: any): boolean;
3498
+
3427
3499
  /**
3428
3500
  * Check if a message part is a data part.
3429
3501
  */
@@ -3456,6 +3528,20 @@ declare function isParsableJson(input: string): boolean;
3456
3528
  */
3457
3529
  export declare function isReasoningUIPart(part: UIMessagePart<UIDataTypes, UITools>): part is ReasoningUIPart;
3458
3530
 
3531
+ /**
3532
+ * Returns true when `url` has the same origin (scheme + host + port) as
3533
+ * `baseUrl`.
3534
+ *
3535
+ * Used to decide whether provider credentials may be attached to a request to a
3536
+ * URL taken from a provider response (e.g. a polling or media-download URL).
3537
+ * Credentials must only be sent to the provider's own origin; a response that
3538
+ * names a foreign host (a CDN, or an attacker-controlled host if the response
3539
+ * is tampered with) must not receive the API key.
3540
+ *
3541
+ * Returns false if either value is not a valid absolute URL (fail-closed).
3542
+ */
3543
+ declare function isSameOrigin(url: string, baseUrl: string): boolean;
3544
+
3459
3545
  /**
3460
3546
  * Type guard to check if a message part is a text part.
3461
3547
  */
@@ -8714,6 +8800,10 @@ export declare const userModelMessageSchema: z.ZodType<UserModelMessage>;
8714
8800
  * Validates that a URL is safe to download from, blocking private/internal addresses
8715
8801
  * to prevent SSRF attacks.
8716
8802
  *
8803
+ * Note: this performs string/literal-IP checks only. It does not resolve DNS, so a
8804
+ * hostname that resolves to a private address is not blocked here (see callers, which
8805
+ * should additionally constrain egress at the network layer when handling untrusted URLs).
8806
+ *
8717
8807
  * @param url - The URL string to validate.
8718
8808
  * @throws DownloadError if the URL is unsafe.
8719
8809
  */
@@ -129,6 +129,16 @@ declare class RequestContext<Values extends Record<string, any> | unknown = unkn
129
129
  * a clean JSON-safe dict for cross-context cycles.
130
130
  */
131
131
  private isSerializable;
132
+ /**
133
+ * Custom span serialization to prevent leaking internal state (like auth
134
+ * tokens stored in the private `registry` Map) into observability spans.
135
+ *
136
+ * `deepClean` in `@mastra/observability` calls this method before falling
137
+ * back to `Object.keys()` — which would walk the runtime-enumerable
138
+ * `registry` field and serialize its raw Map entries (including any
139
+ * bearer tokens) into exported spans.
140
+ */
141
+ serializeForSpan(): Record<string, unknown>;
132
142
  /**
133
143
  * Get all values as a typed object for destructuring.
134
144
  * Returns Record<string, any> when untyped, or the Values type when typed.
@@ -3,7 +3,7 @@ name: mastra-voice-google
3
3
  description: Documentation for @mastra/voice-google. Use when working with @mastra/voice-google APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/voice-google"
6
- version: "0.13.0-alpha.0"
6
+ version: "0.14.0-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -16,7 +16,6 @@ Read the individual reference documents for detailed explanations and code examp
16
16
 
17
17
  ### Docs
18
18
 
19
- - [Voice](references/docs-agents-adding-voice.md) - Learn how to add voice capabilities to your Mastra agents for text-to-speech and speech-to-text interactions.
20
19
  - [Voice in Mastra](references/docs-voice-overview.md) - Overview of voice capabilities in Mastra, including text-to-speech, speech-to-text, and real-time speech-to-speech interactions.
21
20
 
22
21
  ### Reference
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.13.0-alpha.0",
2
+ "version": "0.14.0-alpha.0",
3
3
  "package": "@mastra/voice-google",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -1,10 +1,12 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Voice in Mastra
2
4
 
3
5
  Mastra's Voice system provides a unified interface for voice interactions, enabling text-to-speech (TTS), speech-to-text (STT), and real-time speech-to-speech (STS) capabilities in your applications.
4
6
 
5
- ## Adding voice to agents
7
+ ## Add voice to agents
6
8
 
7
- To learn how to integrate voice capabilities into your agents, check out the [Adding Voice to Agents](https://mastra.ai/docs/agents/adding-voice) documentation. This section covers how to use both single and multiple voice providers, as well as real-time interactions.
9
+ Pass a voice provider to an agent with the `voice` property. The same property supports text-to-speech (TTS), speech-to-text (STT), and real-time speech-to-speech (STS), depending on the provider you configure.
8
10
 
9
11
  ```typescript
10
12
  import { Agent } from '@mastra/core/agent'
@@ -1137,7 +1139,7 @@ const voiceAgent = new Agent({
1137
1139
  })
1138
1140
  ```
1139
1141
 
1140
- ### Using Multiple Voice Providers
1142
+ ### Using multiple voice providers
1141
1143
 
1142
1144
  This example demonstrates how to create and use two different voice providers in Mastra: OpenAI for speech-to-text (STT) and PlayAI for text-to-speech (TTS).
1143
1145
 
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Google
2
4
 
3
5
  The Google Voice implementation in Mastra provides both text-to-speech (TTS) and speech-to-text (STT) capabilities using Google Cloud services. It supports multiple voices, languages, advanced audio configuration options, and both standard API key authentication and Vertex AI mode for enterprise deployments.
@@ -10,7 +12,7 @@ import { GoogleVoice } from '@mastra/voice-google'
10
12
  // Initialize with default configuration (uses GOOGLE_API_KEY environment variable)
11
13
  const voice = new GoogleVoice()
12
14
 
13
- // Text-to-Speech
15
+ // Text-to-Speech (plain text)
14
16
  const audioStream = await voice.speak('Hello, world!', {
15
17
  languageCode: 'en-US',
16
18
  audioConfig: {
@@ -18,6 +20,19 @@ const audioStream = await voice.speak('Hello, world!', {
18
20
  },
19
21
  })
20
22
 
23
+ // Text-to-Speech with SSML
24
+ const ssmlStream = await voice.speak('ignored', {
25
+ input: {
26
+ ssml: '<speak>Take <say-as interpret-as="unit">5 mg</say-as> daily.</speak>',
27
+ },
28
+ })
29
+
30
+ // Text-to-Speech with Gemini-TTS model
31
+ const geminiStream = await voice.speak('Hello from Gemini TTS!', {
32
+ voice: { name: 'Kore', modelName: 'gemini-2.5-flash-preview-tts' },
33
+ input: { prompt: 'Warm, calm tone.' },
34
+ })
35
+
21
36
  // Speech-to-Text
22
37
  const transcript = await voice.listen(audioStream, {
23
38
  config: {
@@ -66,25 +81,52 @@ Converts text to speech using Google Cloud Text-to-Speech service.
66
81
 
67
82
  **options** (`object`): Speech synthesis options
68
83
 
69
- **options.speaker** (`string`): Voice ID to use for this request
84
+ **options.speaker** (`string`): Voice ID to use for this request.
70
85
 
71
- **options.languageCode** (`string`): Language code for the voice (e.g., 'en-US'). Defaults to the language code from the speaker ID or 'en-US'
86
+ **options.languageCode** (`string`): Language code for the voice (e.g., 'en-US'). Defaults to the language code derived from the speaker ID, or 'en-US'.
72
87
 
73
- **options.audioConfig** (`ISynthesizeSpeechRequest['audioConfig']`): Audio configuration options from Google Cloud Text-to-Speech API
88
+ **options.input** (`ISynthesizeSpeechRequest['input']`): Rich input object passed through to the Google Cloud TTS API. Supports ssml, markup, prompt (Gemini-TTS style steering), customPronunciations, and multiSpeakerMarkup. When provided without text, ssml, markup, or multiSpeakerMarkup, the positional input argument is used as the text field automatically.
89
+
90
+ **options.voice** (`ISynthesizeSpeechRequest['voice']`): Voice configuration merged on top of defaults (name and languageCode). Supports modelName (e.g., 'gemini-2.5-flash-preview-tts') and multiSpeakerVoiceConfig.
91
+
92
+ **options.audioConfig** (`ISynthesizeSpeechRequest['audioConfig']`): Audio configuration options from Google Cloud Text-to-Speech API.
74
93
 
75
94
  Returns: `Promise<NodeJS.ReadableStream>`
76
95
 
77
96
  ### `listen()`
78
97
 
79
- Converts speech to text using Google Cloud Speech-to-Text service.
98
+ Converts speech to text using Google Cloud Speech-to-Text service. Supports both v1 (default) and v2 APIs. The v2 API adds support for AAC-in-MP4 audio (iOS Safari) via auto-decoding.
99
+
100
+ #### v1 (default)
101
+
102
+ **audioStream** (`NodeJS.ReadableStream`): Audio stream to transcribe
103
+
104
+ **options** (`GoogleListenOptionsV1`): v1 recognition options
105
+
106
+ **options.config** (`IRecognitionConfig`): v1 recognition configuration from Google Cloud Speech-to-Text API
107
+
108
+ #### v2
109
+
110
+ Pass `v2: true` to use the Cloud Speech-to-Text v2 API, which supports additional audio formats like AAC-in-MP4 (iOS Safari).
111
+
112
+ ```typescript
113
+ const transcript = await voice.listen(iosSafariAacStream, {
114
+ v2: true,
115
+ config: {
116
+ autoDecodingConfig: {},
117
+ },
118
+ })
119
+ ```
80
120
 
81
121
  **audioStream** (`NodeJS.ReadableStream`): Audio stream to transcribe
82
122
 
83
- **options** (`object`): Recognition options
123
+ **options** (`GoogleListenOptionsV2`): v2 recognition options
124
+
125
+ **options.v2** (`true`): Enables the v2 API path
84
126
 
85
- **options.stream** (`boolean`): Whether to use streaming recognition
127
+ **options.config** (`v2.IRecognitionConfig`): v2 recognition configuration. Defaults to auto-decoding with languageCodes: \['en-US'] and model: 'long'. Set autoDecodingConfig: {} to auto-detect the audio format, or use explicitDecodingConfig to specify an encoding like MP4\_AAC, M4A\_AAC, or MOV\_AAC.
86
128
 
87
- **options.config** (`IRecognitionConfig`): Recognition configuration from Google Cloud Speech-to-Text API
129
+ **options.recognizer** (`string`): v2 recognizer resource path. Defaults to projects/{project}/locations/global/recognizers/\_ where {project} is resolved from the constructor project option, GOOGLE\_CLOUD\_PROJECT, or the client's default project.
88
130
 
89
131
  Returns: `Promise<string>`
90
132
 
package/dist/index.cjs CHANGED
@@ -308,6 +308,8 @@ var DEFAULT_VOICE = "en-US-Casual-K";
308
308
  var GoogleVoice = class extends MastraVoice {
309
309
  ttsClient;
310
310
  speechClient;
311
+ speechClientV2;
312
+ speechOptionsV2;
311
313
  vertexAI;
312
314
  project;
313
315
  location;
@@ -357,6 +359,13 @@ var GoogleVoice = class extends MastraVoice {
357
359
  const speechOptions = buildAuthOptions(listeningAuthConfig, { vertexAI});
358
360
  this.ttsClient = new textToSpeech.TextToSpeechClient(ttsOptions);
359
361
  this.speechClient = new speech.SpeechClient(speechOptions);
362
+ this.speechOptionsV2 = speechOptions;
363
+ }
364
+ getV2SpeechClient() {
365
+ if (!this.speechClientV2) {
366
+ this.speechClientV2 = new speech.v2.SpeechClient(this.speechOptionsV2);
367
+ }
368
+ return this.speechClientV2;
360
369
  }
361
370
  /**
362
371
  * Check if Vertex AI mode is enabled
@@ -402,21 +411,30 @@ var GoogleVoice = class extends MastraVoice {
402
411
  return Buffer.concat(chunks).toString("utf-8");
403
412
  }
404
413
  /**
405
- * Converts text to speech
414
+ * Converts text to speech.
415
+ *
416
+ * When `input` is a string or stream, builds a text-only request (existing behaviour).
417
+ * Pass `options.input` to send richer proto fields (ssml, markup, prompt,
418
+ * customPronunciations, multiSpeakerMarkup) and `options.voice` for fields
419
+ * like modelName or multiSpeakerVoiceConfig.
420
+ *
406
421
  * @param {string | NodeJS.ReadableStream} input - Text or stream to convert to speech
407
- * @param {Object} [options] - Speech synthesis options
408
- * @param {string} [options.speaker] - Voice ID to use
409
- * @param {string} [options.languageCode] - Language code for the voice
410
- * @param {TextToSpeechTypes.cloud.texttospeech.v1.ISynthesizeSpeechRequest['audioConfig']} [options.audioConfig] - Audio configuration options
411
- * @returns {Promise<NodeJS.ReadableStream>} Stream of synthesized audio. Default encoding is LINEAR16.
422
+ * @param {GoogleSpeakOptions} [options] - Speech synthesis options
423
+ * @returns {Promise<NodeJS.ReadableStream>} Stream of synthesised audio. Default encoding is LINEAR16.
412
424
  */
413
425
  async speak(input, options) {
414
- const text = typeof input === "string" ? input : await this.streamToString(input);
426
+ const defaultVoiceName = options?.speaker || this.speaker;
427
+ const defaultLanguageCode = options?.languageCode || defaultVoiceName?.split("-").slice(0, 2).join("-") || "en-US";
428
+ const requestInput = options?.input ? { ...options.input } : { text: typeof input === "string" ? input : await this.streamToString(input) };
429
+ if (options?.input && !options.input.text && !options.input.ssml && !options.input.markup && !options.input.multiSpeakerMarkup) {
430
+ requestInput.text = typeof input === "string" ? input : await this.streamToString(input);
431
+ }
415
432
  const request = {
416
- input: { text },
433
+ input: requestInput,
417
434
  voice: {
418
- name: options?.speaker || this.speaker,
419
- languageCode: options?.languageCode || options?.speaker?.split("-").slice(0, 2).join("-") || "en-US"
435
+ name: defaultVoiceName,
436
+ languageCode: defaultLanguageCode,
437
+ ...options?.voice
420
438
  },
421
439
  audioConfig: options?.audioConfig || { audioEncoding: "LINEAR16" }
422
440
  };
@@ -440,10 +458,14 @@ var GoogleVoice = class extends MastraVoice {
440
458
  return { enabled: true };
441
459
  }
442
460
  /**
443
- * Converts speech to text
461
+ * Converts speech to text using Cloud Speech-to-Text v1 or v2.
462
+ *
463
+ * Pass `{ v2: true }` in options to use the v2 API, which supports additional
464
+ * audio formats like AAC-in-MP4 (iOS Safari) via `autoDecodingConfig` or
465
+ * `explicitDecodingConfig`. The v1 path remains the default.
466
+ *
444
467
  * @param {NodeJS.ReadableStream} audioStream - Audio stream to transcribe. Default encoding is LINEAR16.
445
- * @param {Object} [options] - Recognition options
446
- * @param {SpeechTypes.cloud.speech.v1.IRecognitionConfig} [options.config] - Recognition configuration
468
+ * @param {GoogleListenOptions} [options] - Recognition options
447
469
  * @returns {Promise<string>} Transcribed text
448
470
  */
449
471
  async listen(audioStream, options) {
@@ -456,7 +478,13 @@ var GoogleVoice = class extends MastraVoice {
456
478
  }
457
479
  }
458
480
  const buffer = Buffer.concat(chunks);
459
- let request = {
481
+ if (options && "v2" in options && options.v2) {
482
+ return this.recognizeV2(buffer, options);
483
+ }
484
+ return this.recognizeV1(buffer, options);
485
+ }
486
+ async recognizeV1(buffer, options) {
487
+ const request = {
460
488
  config: {
461
489
  encoding: "LINEAR16",
462
490
  languageCode: "en-US",
@@ -467,10 +495,38 @@ var GoogleVoice = class extends MastraVoice {
467
495
  }
468
496
  };
469
497
  const [response] = await this.speechClient.recognize(request);
470
- if (!response.results || response.results.length === 0) {
498
+ return this.extractTranscription(response?.results);
499
+ }
500
+ async recognizeV2(buffer, options) {
501
+ const config = { ...options.config };
502
+ if (!config.autoDecodingConfig && !config.explicitDecodingConfig) {
503
+ config.autoDecodingConfig = {};
504
+ }
505
+ if (!config.languageCodes || config.languageCodes.length === 0) {
506
+ config.languageCodes = ["en-US"];
507
+ }
508
+ if (!config.model) {
509
+ config.model = "long";
510
+ }
511
+ let recognizer = options.recognizer;
512
+ if (!recognizer) {
513
+ const project = this.project || await this.getV2SpeechClient().getProjectId();
514
+ recognizer = `projects/${project}/locations/global/recognizers/_`;
515
+ }
516
+ const request = {
517
+ recognizer,
518
+ config,
519
+ content: buffer
520
+ };
521
+ const client = this.getV2SpeechClient();
522
+ const [response] = await client.recognize(request);
523
+ return this.extractTranscription(response?.results);
524
+ }
525
+ extractTranscription(results) {
526
+ if (!results || results.length === 0) {
471
527
  throw new Error("No transcription results returned");
472
528
  }
473
- const transcription = response.results.map((result) => {
529
+ const transcription = results.map((result) => {
474
530
  if (!result.alternatives || result.alternatives.length === 0) {
475
531
  return "";
476
532
  }