@livekit/agents-plugin-google 1.0.47 → 1.0.49
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/beta/gemini_tts.cjs
CHANGED
|
@@ -147,12 +147,12 @@ class ChunkedStream extends import_agents.tts.ChunkedStream {
|
|
|
147
147
|
parts: [{ text: inputText }]
|
|
148
148
|
}
|
|
149
149
|
];
|
|
150
|
-
const responseStream = await this.#tts.client.models.generateContentStream({
|
|
151
|
-
model: this.#tts.opts.model,
|
|
152
|
-
contents,
|
|
153
|
-
config
|
|
154
|
-
});
|
|
155
150
|
try {
|
|
151
|
+
const responseStream = await this.#tts.client.models.generateContentStream({
|
|
152
|
+
model: this.#tts.opts.model,
|
|
153
|
+
contents,
|
|
154
|
+
config
|
|
155
|
+
});
|
|
156
156
|
for await (const response of responseStream) {
|
|
157
157
|
await this.#processResponse(response, bstream, requestId);
|
|
158
158
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/beta/gemini_tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as types from '@google/genai';\nimport { GoogleGenAI } from '@google/genai';\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APIStatusError,\n AudioByteStream,\n isAPIError,\n shortuuid,\n tts,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\n\nexport type GeminiTTSModels = 'gemini-2.5-flash-preview-tts' | 'gemini-2.5-pro-preview-tts';\n\nexport type GeminiVoices =\n | 'Zephyr'\n | 'Puck'\n | 'Charon'\n | 'Kore'\n | 'Fenrir'\n | 'Leda'\n | 'Orus'\n | 'Aoede'\n | 'Callirrhoe'\n | 'Autonoe'\n | 'Enceladus'\n | 'Iapetus'\n | 'Umbriel'\n | 'Algieba'\n | 'Despina'\n | 'Erinome'\n | 'Algenib'\n | 'Rasalgethi'\n | 'Laomedeia'\n | 'Achernar'\n | 'Alnilam'\n | 'Schedar'\n | 'Gacrux'\n | 'Pulcherrima'\n | 'Achird'\n | 'Zubenelgenubi'\n | 'Vindemiatrix'\n | 'Sadachbia'\n | 'Sadaltager'\n | 'Sulafat';\n\nconst DEFAULT_MODEL: GeminiTTSModels = 'gemini-2.5-flash-preview-tts';\nconst DEFAULT_VOICE: GeminiVoices = 'Kore';\nconst DEFAULT_SAMPLE_RATE = 24000; // not configurable\nconst NUM_CHANNELS = 1;\nconst DEFAULT_INSTRUCTIONS = \"Say the text with a proper tone, don't omit or add any words\";\n\nexport interface TTSOptions {\n model: GeminiTTSModels | string;\n voiceName: GeminiVoices | string;\n vertexai: boolean;\n project?: string;\n location?: string;\n instructions?: string;\n}\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n #client: GoogleGenAI;\n label = 'google.gemini.TTS';\n\n /**\n * Create a new instance of Gemini TTS.\n *\n * Environment Requirements:\n * - For VertexAI: Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the path of the service account key file.\n * - For Google Gemini API: Set the `apiKey` argument or the `GOOGLE_API_KEY` environment variable.\n *\n * @param opts - Configuration options for Gemini TTS\n */\n constructor({\n model = DEFAULT_MODEL,\n voiceName = DEFAULT_VOICE,\n apiKey,\n vertexai,\n project,\n location,\n instructions,\n }: Partial<TTSOptions & { apiKey: string }> = {}) {\n super(DEFAULT_SAMPLE_RATE, NUM_CHANNELS, { streaming: false });\n\n const gcpProject: string | undefined = project || process.env.GOOGLE_CLOUD_PROJECT;\n const gcpLocation: string | undefined =\n location || process.env.GOOGLE_CLOUD_LOCATION || 'us-central1';\n const useVertexai = vertexai ?? process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true';\n const geminiApiKey = apiKey || process.env.GOOGLE_API_KEY;\n\n let finalProject: string | undefined = gcpProject;\n let finalLocation: string | undefined = gcpLocation;\n let finalApiKey: string | undefined = geminiApiKey;\n\n if (useVertexai) {\n if (!finalProject) {\n throw new APIConnectionError({\n message:\n 'Project ID is required for Vertex AI. Set via project option or GOOGLE_CLOUD_PROJECT environment variable',\n });\n }\n finalApiKey = undefined;\n } else {\n finalProject = undefined;\n finalLocation = undefined;\n if (!finalApiKey) {\n throw new APIConnectionError({\n message:\n 'API key is required for Google API either via apiKey or GOOGLE_API_KEY environment variable',\n });\n }\n }\n\n this.#opts = {\n model,\n voiceName,\n vertexai: useVertexai,\n project: finalProject,\n location: finalLocation,\n instructions: instructions ?? DEFAULT_INSTRUCTIONS,\n };\n\n const clientOptions: types.GoogleGenAIOptions = useVertexai\n ? {\n vertexai: true,\n project: finalProject,\n location: finalLocation,\n }\n : {\n apiKey: finalApiKey,\n };\n\n this.#client = new GoogleGenAI(clientOptions);\n }\n\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): ChunkedStream {\n return new ChunkedStream(text, this, connOptions, abortSignal);\n }\n\n /**\n * Update the TTS options.\n *\n * @param opts - Options to update\n */\n updateOptions(opts: { voiceName?: GeminiVoices | string }) {\n if (opts.voiceName !== undefined) {\n this.#opts.voiceName = opts.voiceName;\n }\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on Gemini TTS');\n }\n\n get opts(): TTSOptions {\n return this.#opts;\n }\n\n get client(): GoogleGenAI {\n return this.#client;\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n label = 'google.gemini.ChunkedStream';\n\n constructor(\n inputText: string,\n tts: TTS,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(inputText, tts, connOptions, abortSignal);\n this.#tts = tts;\n }\n\n protected async run() {\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#tts.sampleRate, this.#tts.numChannels);\n\n const config: types.GenerateContentConfig = {\n responseModalities: ['AUDIO'],\n speechConfig: {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: this.#tts.opts.voiceName,\n },\n },\n },\n abortSignal: this.abortSignal,\n };\n\n let inputText = this.inputText;\n if (this.#tts.opts.instructions) {\n inputText = `${this.#tts.opts.instructions}:\\n\"${inputText}\"`;\n }\n\n const contents: types.Content[] = [\n {\n role: 'user',\n parts: [{ text: inputText }],\n },\n ];\n\n const responseStream = await this.#tts.client.models.generateContentStream({\n model: this.#tts.opts.model,\n contents,\n config,\n });\n\n try {\n for await (const response of responseStream) {\n await this.#processResponse(response, bstream, requestId);\n }\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n return;\n }\n if (isAPIError(error)) throw error;\n\n const err = error as {\n code?: number;\n message?: string;\n status?: string;\n type?: string;\n };\n\n if (err.code && err.code >= 400 && err.code < 500) {\n if (err.code === 429) {\n throw new APIStatusError({\n message: `Gemini TTS: Rate limit error - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: 429,\n retryable: true,\n },\n });\n } else {\n throw new APIStatusError({\n message: `Gemini TTS: Client error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable: false,\n },\n });\n }\n }\n\n if (err.code && err.code >= 500) {\n throw new APIStatusError({\n message: `Gemini TTS: Server error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable: true,\n },\n });\n }\n\n throw new APIConnectionError({\n message: `Gemini TTS: Connection error - ${err.message || 'Unknown error'}`,\n options: { retryable: true },\n });\n } finally {\n this.queue.close();\n }\n }\n\n async #processResponse(\n response: types.GenerateContentResponse,\n bstream: AudioByteStream,\n requestId: string,\n ) {\n if (!response.candidates || response.candidates.length === 0) {\n return;\n }\n\n const candidate = response.candidates[0];\n if (!candidate || !candidate.content?.parts) {\n return;\n }\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (final: boolean) => {\n if (lastFrame) {\n this.queue.put({\n requestId,\n frame: lastFrame,\n segmentId: requestId,\n final,\n });\n lastFrame = undefined;\n }\n };\n\n for (const part of candidate.content.parts) {\n if (part.inlineData?.data && part.inlineData.mimeType?.startsWith('audio/')) {\n const audioBuffer = Buffer.from(part.inlineData.data, 'base64');\n\n for (const frame of bstream.write(audioBuffer)) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n }\n }\n\n for (const frame of bstream.flush()) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n\n sendLastFrame(true);\n }\n}\n"],"mappings":"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","names":["tts"]}
|
|
1
|
+
{"version":3,"sources":["../../src/beta/gemini_tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as types from '@google/genai';\nimport { GoogleGenAI } from '@google/genai';\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APIStatusError,\n AudioByteStream,\n isAPIError,\n shortuuid,\n tts,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\n\nexport type GeminiTTSModels = 'gemini-2.5-flash-preview-tts' | 'gemini-2.5-pro-preview-tts';\n\nexport type GeminiVoices =\n | 'Zephyr'\n | 'Puck'\n | 'Charon'\n | 'Kore'\n | 'Fenrir'\n | 'Leda'\n | 'Orus'\n | 'Aoede'\n | 'Callirrhoe'\n | 'Autonoe'\n | 'Enceladus'\n | 'Iapetus'\n | 'Umbriel'\n | 'Algieba'\n | 'Despina'\n | 'Erinome'\n | 'Algenib'\n | 'Rasalgethi'\n | 'Laomedeia'\n | 'Achernar'\n | 'Alnilam'\n | 'Schedar'\n | 'Gacrux'\n | 'Pulcherrima'\n | 'Achird'\n | 'Zubenelgenubi'\n | 'Vindemiatrix'\n | 'Sadachbia'\n | 'Sadaltager'\n | 'Sulafat';\n\nconst DEFAULT_MODEL: GeminiTTSModels = 'gemini-2.5-flash-preview-tts';\nconst DEFAULT_VOICE: GeminiVoices = 'Kore';\nconst DEFAULT_SAMPLE_RATE = 24000; // not configurable\nconst NUM_CHANNELS = 1;\nconst DEFAULT_INSTRUCTIONS = \"Say the text with a proper tone, don't omit or add any words\";\n\nexport interface TTSOptions {\n model: GeminiTTSModels | string;\n voiceName: GeminiVoices | string;\n vertexai: boolean;\n project?: string;\n location?: string;\n instructions?: string;\n}\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n #client: GoogleGenAI;\n label = 'google.gemini.TTS';\n\n /**\n * Create a new instance of Gemini TTS.\n *\n * Environment Requirements:\n * - For VertexAI: Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the path of the service account key file.\n * - For Google Gemini API: Set the `apiKey` argument or the `GOOGLE_API_KEY` environment variable.\n *\n * @param opts - Configuration options for Gemini TTS\n */\n constructor({\n model = DEFAULT_MODEL,\n voiceName = DEFAULT_VOICE,\n apiKey,\n vertexai,\n project,\n location,\n instructions,\n }: Partial<TTSOptions & { apiKey: string }> = {}) {\n super(DEFAULT_SAMPLE_RATE, NUM_CHANNELS, { streaming: false });\n\n const gcpProject: string | undefined = project || process.env.GOOGLE_CLOUD_PROJECT;\n const gcpLocation: string | undefined =\n location || process.env.GOOGLE_CLOUD_LOCATION || 'us-central1';\n const useVertexai = vertexai ?? process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true';\n const geminiApiKey = apiKey || process.env.GOOGLE_API_KEY;\n\n let finalProject: string | undefined = gcpProject;\n let finalLocation: string | undefined = gcpLocation;\n let finalApiKey: string | undefined = geminiApiKey;\n\n if (useVertexai) {\n if (!finalProject) {\n throw new APIConnectionError({\n message:\n 'Project ID is required for Vertex AI. Set via project option or GOOGLE_CLOUD_PROJECT environment variable',\n });\n }\n finalApiKey = undefined;\n } else {\n finalProject = undefined;\n finalLocation = undefined;\n if (!finalApiKey) {\n throw new APIConnectionError({\n message:\n 'API key is required for Google API either via apiKey or GOOGLE_API_KEY environment variable',\n });\n }\n }\n\n this.#opts = {\n model,\n voiceName,\n vertexai: useVertexai,\n project: finalProject,\n location: finalLocation,\n instructions: instructions ?? DEFAULT_INSTRUCTIONS,\n };\n\n const clientOptions: types.GoogleGenAIOptions = useVertexai\n ? {\n vertexai: true,\n project: finalProject,\n location: finalLocation,\n }\n : {\n apiKey: finalApiKey,\n };\n\n this.#client = new GoogleGenAI(clientOptions);\n }\n\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): ChunkedStream {\n return new ChunkedStream(text, this, connOptions, abortSignal);\n }\n\n /**\n * Update the TTS options.\n *\n * @param opts - Options to update\n */\n updateOptions(opts: { voiceName?: GeminiVoices | string }) {\n if (opts.voiceName !== undefined) {\n this.#opts.voiceName = opts.voiceName;\n }\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on Gemini TTS');\n }\n\n get opts(): TTSOptions {\n return this.#opts;\n }\n\n get client(): GoogleGenAI {\n return this.#client;\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n label = 'google.gemini.ChunkedStream';\n\n constructor(\n inputText: string,\n tts: TTS,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(inputText, tts, connOptions, abortSignal);\n this.#tts = tts;\n }\n\n protected async run() {\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#tts.sampleRate, this.#tts.numChannels);\n\n const config: types.GenerateContentConfig = {\n responseModalities: ['AUDIO'],\n speechConfig: {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: this.#tts.opts.voiceName,\n },\n },\n },\n abortSignal: this.abortSignal,\n };\n\n let inputText = this.inputText;\n if (this.#tts.opts.instructions) {\n inputText = `${this.#tts.opts.instructions}:\\n\"${inputText}\"`;\n }\n\n const contents: types.Content[] = [\n {\n role: 'user',\n parts: [{ text: inputText }],\n },\n ];\n\n try {\n const responseStream = await this.#tts.client.models.generateContentStream({\n model: this.#tts.opts.model,\n contents,\n config,\n });\n\n for await (const response of responseStream) {\n await this.#processResponse(response, bstream, requestId);\n }\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n return;\n }\n if (isAPIError(error)) throw error;\n\n const err = error as {\n code?: number;\n message?: string;\n status?: string;\n type?: string;\n };\n\n if (err.code && err.code >= 400 && err.code < 500) {\n if (err.code === 429) {\n throw new APIStatusError({\n message: `Gemini TTS: Rate limit error - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: 429,\n retryable: true,\n },\n });\n } else {\n throw new APIStatusError({\n message: `Gemini TTS: Client error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable: false,\n },\n });\n }\n }\n\n if (err.code && err.code >= 500) {\n throw new APIStatusError({\n message: `Gemini TTS: Server error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable: true,\n },\n });\n }\n\n throw new APIConnectionError({\n message: `Gemini TTS: Connection error - ${err.message || 'Unknown error'}`,\n options: { retryable: true },\n });\n } finally {\n this.queue.close();\n }\n }\n\n async #processResponse(\n response: types.GenerateContentResponse,\n bstream: AudioByteStream,\n requestId: string,\n ) {\n if (!response.candidates || response.candidates.length === 0) {\n return;\n }\n\n const candidate = response.candidates[0];\n if (!candidate || !candidate.content?.parts) {\n return;\n }\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (final: boolean) => {\n if (lastFrame) {\n this.queue.put({\n requestId,\n frame: lastFrame,\n segmentId: requestId,\n final,\n });\n lastFrame = undefined;\n }\n };\n\n for (const part of candidate.content.parts) {\n if (part.inlineData?.data && part.inlineData.mimeType?.startsWith('audio/')) {\n const audioBuffer = Buffer.from(part.inlineData.data, 'base64');\n\n for (const frame of bstream.write(audioBuffer)) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n }\n }\n\n for (const frame of bstream.flush()) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n\n sendLastFrame(true);\n }\n}\n"],"mappings":"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","names":["tts"]}
|
package/dist/beta/gemini_tts.js
CHANGED
|
@@ -130,12 +130,12 @@ class ChunkedStream extends tts.ChunkedStream {
|
|
|
130
130
|
parts: [{ text: inputText }]
|
|
131
131
|
}
|
|
132
132
|
];
|
|
133
|
-
const responseStream = await this.#tts.client.models.generateContentStream({
|
|
134
|
-
model: this.#tts.opts.model,
|
|
135
|
-
contents,
|
|
136
|
-
config
|
|
137
|
-
});
|
|
138
133
|
try {
|
|
134
|
+
const responseStream = await this.#tts.client.models.generateContentStream({
|
|
135
|
+
model: this.#tts.opts.model,
|
|
136
|
+
contents,
|
|
137
|
+
config
|
|
138
|
+
});
|
|
139
139
|
for await (const response of responseStream) {
|
|
140
140
|
await this.#processResponse(response, bstream, requestId);
|
|
141
141
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/beta/gemini_tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as types from '@google/genai';\nimport { GoogleGenAI } from '@google/genai';\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APIStatusError,\n AudioByteStream,\n isAPIError,\n shortuuid,\n tts,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\n\nexport type GeminiTTSModels = 'gemini-2.5-flash-preview-tts' | 'gemini-2.5-pro-preview-tts';\n\nexport type GeminiVoices =\n | 'Zephyr'\n | 'Puck'\n | 'Charon'\n | 'Kore'\n | 'Fenrir'\n | 'Leda'\n | 'Orus'\n | 'Aoede'\n | 'Callirrhoe'\n | 'Autonoe'\n | 'Enceladus'\n | 'Iapetus'\n | 'Umbriel'\n | 'Algieba'\n | 'Despina'\n | 'Erinome'\n | 'Algenib'\n | 'Rasalgethi'\n | 'Laomedeia'\n | 'Achernar'\n | 'Alnilam'\n | 'Schedar'\n | 'Gacrux'\n | 'Pulcherrima'\n | 'Achird'\n | 'Zubenelgenubi'\n | 'Vindemiatrix'\n | 'Sadachbia'\n | 'Sadaltager'\n | 'Sulafat';\n\nconst DEFAULT_MODEL: GeminiTTSModels = 'gemini-2.5-flash-preview-tts';\nconst DEFAULT_VOICE: GeminiVoices = 'Kore';\nconst DEFAULT_SAMPLE_RATE = 24000; // not configurable\nconst NUM_CHANNELS = 1;\nconst DEFAULT_INSTRUCTIONS = \"Say the text with a proper tone, don't omit or add any words\";\n\nexport interface TTSOptions {\n model: GeminiTTSModels | string;\n voiceName: GeminiVoices | string;\n vertexai: boolean;\n project?: string;\n location?: string;\n instructions?: string;\n}\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n #client: GoogleGenAI;\n label = 'google.gemini.TTS';\n\n /**\n * Create a new instance of Gemini TTS.\n *\n * Environment Requirements:\n * - For VertexAI: Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the path of the service account key file.\n * - For Google Gemini API: Set the `apiKey` argument or the `GOOGLE_API_KEY` environment variable.\n *\n * @param opts - Configuration options for Gemini TTS\n */\n constructor({\n model = DEFAULT_MODEL,\n voiceName = DEFAULT_VOICE,\n apiKey,\n vertexai,\n project,\n location,\n instructions,\n }: Partial<TTSOptions & { apiKey: string }> = {}) {\n super(DEFAULT_SAMPLE_RATE, NUM_CHANNELS, { streaming: false });\n\n const gcpProject: string | undefined = project || process.env.GOOGLE_CLOUD_PROJECT;\n const gcpLocation: string | undefined =\n location || process.env.GOOGLE_CLOUD_LOCATION || 'us-central1';\n const useVertexai = vertexai ?? process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true';\n const geminiApiKey = apiKey || process.env.GOOGLE_API_KEY;\n\n let finalProject: string | undefined = gcpProject;\n let finalLocation: string | undefined = gcpLocation;\n let finalApiKey: string | undefined = geminiApiKey;\n\n if (useVertexai) {\n if (!finalProject) {\n throw new APIConnectionError({\n message:\n 'Project ID is required for Vertex AI. Set via project option or GOOGLE_CLOUD_PROJECT environment variable',\n });\n }\n finalApiKey = undefined;\n } else {\n finalProject = undefined;\n finalLocation = undefined;\n if (!finalApiKey) {\n throw new APIConnectionError({\n message:\n 'API key is required for Google API either via apiKey or GOOGLE_API_KEY environment variable',\n });\n }\n }\n\n this.#opts = {\n model,\n voiceName,\n vertexai: useVertexai,\n project: finalProject,\n location: finalLocation,\n instructions: instructions ?? DEFAULT_INSTRUCTIONS,\n };\n\n const clientOptions: types.GoogleGenAIOptions = useVertexai\n ? {\n vertexai: true,\n project: finalProject,\n location: finalLocation,\n }\n : {\n apiKey: finalApiKey,\n };\n\n this.#client = new GoogleGenAI(clientOptions);\n }\n\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): ChunkedStream {\n return new ChunkedStream(text, this, connOptions, abortSignal);\n }\n\n /**\n * Update the TTS options.\n *\n * @param opts - Options to update\n */\n updateOptions(opts: { voiceName?: GeminiVoices | string }) {\n if (opts.voiceName !== undefined) {\n this.#opts.voiceName = opts.voiceName;\n }\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on Gemini TTS');\n }\n\n get opts(): TTSOptions {\n return this.#opts;\n }\n\n get client(): GoogleGenAI {\n return this.#client;\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n label = 'google.gemini.ChunkedStream';\n\n constructor(\n inputText: string,\n tts: TTS,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(inputText, tts, connOptions, abortSignal);\n this.#tts = tts;\n }\n\n protected async run() {\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#tts.sampleRate, this.#tts.numChannels);\n\n const config: types.GenerateContentConfig = {\n responseModalities: ['AUDIO'],\n speechConfig: {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: this.#tts.opts.voiceName,\n },\n },\n },\n abortSignal: this.abortSignal,\n };\n\n let inputText = this.inputText;\n if (this.#tts.opts.instructions) {\n inputText = `${this.#tts.opts.instructions}:\\n\"${inputText}\"`;\n }\n\n const contents: types.Content[] = [\n {\n role: 'user',\n parts: [{ text: inputText }],\n },\n ];\n\n const responseStream = await this.#tts.client.models.generateContentStream({\n model: this.#tts.opts.model,\n contents,\n config,\n });\n\n try {\n for await (const response of responseStream) {\n await this.#processResponse(response, bstream, requestId);\n }\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n return;\n }\n if (isAPIError(error)) throw error;\n\n const err = error as {\n code?: number;\n message?: string;\n status?: string;\n type?: string;\n };\n\n if (err.code && err.code >= 400 && err.code < 500) {\n if (err.code === 429) {\n throw new APIStatusError({\n message: `Gemini TTS: Rate limit error - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: 429,\n retryable: true,\n },\n });\n } else {\n throw new APIStatusError({\n message: `Gemini TTS: Client error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable: false,\n },\n });\n }\n }\n\n if (err.code && err.code >= 500) {\n throw new APIStatusError({\n message: `Gemini TTS: Server error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable: true,\n },\n });\n }\n\n throw new APIConnectionError({\n message: `Gemini TTS: Connection error - ${err.message || 'Unknown error'}`,\n options: { retryable: true },\n });\n } finally {\n this.queue.close();\n }\n }\n\n async #processResponse(\n response: types.GenerateContentResponse,\n bstream: AudioByteStream,\n requestId: string,\n ) {\n if (!response.candidates || response.candidates.length === 0) {\n return;\n }\n\n const candidate = response.candidates[0];\n if (!candidate || !candidate.content?.parts) {\n return;\n }\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (final: boolean) => {\n if (lastFrame) {\n this.queue.put({\n requestId,\n frame: lastFrame,\n segmentId: requestId,\n final,\n });\n lastFrame = undefined;\n }\n };\n\n for (const part of candidate.content.parts) {\n if (part.inlineData?.data && part.inlineData.mimeType?.startsWith('audio/')) {\n const audioBuffer = Buffer.from(part.inlineData.data, 'base64');\n\n for (const frame of bstream.write(audioBuffer)) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n }\n }\n\n for (const frame of bstream.flush()) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n\n sendLastFrame(true);\n }\n}\n"],"mappings":"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","names":["tts"]}
|
|
1
|
+
{"version":3,"sources":["../../src/beta/gemini_tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as types from '@google/genai';\nimport { GoogleGenAI } from '@google/genai';\nimport {\n type APIConnectOptions,\n APIConnectionError,\n APIStatusError,\n AudioByteStream,\n isAPIError,\n shortuuid,\n tts,\n} from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\n\nexport type GeminiTTSModels = 'gemini-2.5-flash-preview-tts' | 'gemini-2.5-pro-preview-tts';\n\nexport type GeminiVoices =\n | 'Zephyr'\n | 'Puck'\n | 'Charon'\n | 'Kore'\n | 'Fenrir'\n | 'Leda'\n | 'Orus'\n | 'Aoede'\n | 'Callirrhoe'\n | 'Autonoe'\n | 'Enceladus'\n | 'Iapetus'\n | 'Umbriel'\n | 'Algieba'\n | 'Despina'\n | 'Erinome'\n | 'Algenib'\n | 'Rasalgethi'\n | 'Laomedeia'\n | 'Achernar'\n | 'Alnilam'\n | 'Schedar'\n | 'Gacrux'\n | 'Pulcherrima'\n | 'Achird'\n | 'Zubenelgenubi'\n | 'Vindemiatrix'\n | 'Sadachbia'\n | 'Sadaltager'\n | 'Sulafat';\n\nconst DEFAULT_MODEL: GeminiTTSModels = 'gemini-2.5-flash-preview-tts';\nconst DEFAULT_VOICE: GeminiVoices = 'Kore';\nconst DEFAULT_SAMPLE_RATE = 24000; // not configurable\nconst NUM_CHANNELS = 1;\nconst DEFAULT_INSTRUCTIONS = \"Say the text with a proper tone, don't omit or add any words\";\n\nexport interface TTSOptions {\n model: GeminiTTSModels | string;\n voiceName: GeminiVoices | string;\n vertexai: boolean;\n project?: string;\n location?: string;\n instructions?: string;\n}\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n #client: GoogleGenAI;\n label = 'google.gemini.TTS';\n\n /**\n * Create a new instance of Gemini TTS.\n *\n * Environment Requirements:\n * - For VertexAI: Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the path of the service account key file.\n * - For Google Gemini API: Set the `apiKey` argument or the `GOOGLE_API_KEY` environment variable.\n *\n * @param opts - Configuration options for Gemini TTS\n */\n constructor({\n model = DEFAULT_MODEL,\n voiceName = DEFAULT_VOICE,\n apiKey,\n vertexai,\n project,\n location,\n instructions,\n }: Partial<TTSOptions & { apiKey: string }> = {}) {\n super(DEFAULT_SAMPLE_RATE, NUM_CHANNELS, { streaming: false });\n\n const gcpProject: string | undefined = project || process.env.GOOGLE_CLOUD_PROJECT;\n const gcpLocation: string | undefined =\n location || process.env.GOOGLE_CLOUD_LOCATION || 'us-central1';\n const useVertexai = vertexai ?? process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true';\n const geminiApiKey = apiKey || process.env.GOOGLE_API_KEY;\n\n let finalProject: string | undefined = gcpProject;\n let finalLocation: string | undefined = gcpLocation;\n let finalApiKey: string | undefined = geminiApiKey;\n\n if (useVertexai) {\n if (!finalProject) {\n throw new APIConnectionError({\n message:\n 'Project ID is required for Vertex AI. Set via project option or GOOGLE_CLOUD_PROJECT environment variable',\n });\n }\n finalApiKey = undefined;\n } else {\n finalProject = undefined;\n finalLocation = undefined;\n if (!finalApiKey) {\n throw new APIConnectionError({\n message:\n 'API key is required for Google API either via apiKey or GOOGLE_API_KEY environment variable',\n });\n }\n }\n\n this.#opts = {\n model,\n voiceName,\n vertexai: useVertexai,\n project: finalProject,\n location: finalLocation,\n instructions: instructions ?? DEFAULT_INSTRUCTIONS,\n };\n\n const clientOptions: types.GoogleGenAIOptions = useVertexai\n ? {\n vertexai: true,\n project: finalProject,\n location: finalLocation,\n }\n : {\n apiKey: finalApiKey,\n };\n\n this.#client = new GoogleGenAI(clientOptions);\n }\n\n synthesize(\n text: string,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ): ChunkedStream {\n return new ChunkedStream(text, this, connOptions, abortSignal);\n }\n\n /**\n * Update the TTS options.\n *\n * @param opts - Options to update\n */\n updateOptions(opts: { voiceName?: GeminiVoices | string }) {\n if (opts.voiceName !== undefined) {\n this.#opts.voiceName = opts.voiceName;\n }\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on Gemini TTS');\n }\n\n get opts(): TTSOptions {\n return this.#opts;\n }\n\n get client(): GoogleGenAI {\n return this.#client;\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n #tts: TTS;\n label = 'google.gemini.ChunkedStream';\n\n constructor(\n inputText: string,\n tts: TTS,\n connOptions?: APIConnectOptions,\n abortSignal?: AbortSignal,\n ) {\n super(inputText, tts, connOptions, abortSignal);\n this.#tts = tts;\n }\n\n protected async run() {\n const requestId = shortuuid();\n const bstream = new AudioByteStream(this.#tts.sampleRate, this.#tts.numChannels);\n\n const config: types.GenerateContentConfig = {\n responseModalities: ['AUDIO'],\n speechConfig: {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: this.#tts.opts.voiceName,\n },\n },\n },\n abortSignal: this.abortSignal,\n };\n\n let inputText = this.inputText;\n if (this.#tts.opts.instructions) {\n inputText = `${this.#tts.opts.instructions}:\\n\"${inputText}\"`;\n }\n\n const contents: types.Content[] = [\n {\n role: 'user',\n parts: [{ text: inputText }],\n },\n ];\n\n try {\n const responseStream = await this.#tts.client.models.generateContentStream({\n model: this.#tts.opts.model,\n contents,\n config,\n });\n\n for await (const response of responseStream) {\n await this.#processResponse(response, bstream, requestId);\n }\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n return;\n }\n if (isAPIError(error)) throw error;\n\n const err = error as {\n code?: number;\n message?: string;\n status?: string;\n type?: string;\n };\n\n if (err.code && err.code >= 400 && err.code < 500) {\n if (err.code === 429) {\n throw new APIStatusError({\n message: `Gemini TTS: Rate limit error - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: 429,\n retryable: true,\n },\n });\n } else {\n throw new APIStatusError({\n message: `Gemini TTS: Client error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable: false,\n },\n });\n }\n }\n\n if (err.code && err.code >= 500) {\n throw new APIStatusError({\n message: `Gemini TTS: Server error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable: true,\n },\n });\n }\n\n throw new APIConnectionError({\n message: `Gemini TTS: Connection error - ${err.message || 'Unknown error'}`,\n options: { retryable: true },\n });\n } finally {\n this.queue.close();\n }\n }\n\n async #processResponse(\n response: types.GenerateContentResponse,\n bstream: AudioByteStream,\n requestId: string,\n ) {\n if (!response.candidates || response.candidates.length === 0) {\n return;\n }\n\n const candidate = response.candidates[0];\n if (!candidate || !candidate.content?.parts) {\n return;\n }\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (final: boolean) => {\n if (lastFrame) {\n this.queue.put({\n requestId,\n frame: lastFrame,\n segmentId: requestId,\n final,\n });\n lastFrame = undefined;\n }\n };\n\n for (const part of candidate.content.parts) {\n if (part.inlineData?.data && part.inlineData.mimeType?.startsWith('audio/')) {\n const audioBuffer = Buffer.from(part.inlineData.data, 'base64');\n\n for (const frame of bstream.write(audioBuffer)) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n }\n }\n\n for (const frame of bstream.flush()) {\n sendLastFrame(false);\n lastFrame = frame;\n }\n\n sendLastFrame(true);\n }\n}\n"],"mappings":"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","names":["tts"]}
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livekit/agents-plugin-google",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.49",
|
|
4
4
|
"description": "Google Gemini plugin for LiveKit Node Agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"require": "dist/index.cjs",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"@microsoft/api-extractor": "^7.35.0",
|
|
30
30
|
"tsup": "^8.3.5",
|
|
31
31
|
"typescript": "^5.0.0",
|
|
32
|
-
"@livekit/agents": "1.0.
|
|
33
|
-
"@livekit/agents-plugin-openai": "1.0.
|
|
34
|
-
"@livekit/agents-plugins-test": "1.0.
|
|
32
|
+
"@livekit/agents": "1.0.49",
|
|
33
|
+
"@livekit/agents-plugin-openai": "1.0.49",
|
|
34
|
+
"@livekit/agents-plugins-test": "1.0.49"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@google/genai": "^1.34.0",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@livekit/rtc-node": "^0.13.24",
|
|
44
|
-
"@livekit/agents": "1.0.
|
|
44
|
+
"@livekit/agents": "1.0.49"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsup --onSuccess \"pnpm build:types\"",
|
package/src/beta/gemini_tts.ts
CHANGED
|
@@ -213,13 +213,13 @@ export class ChunkedStream extends tts.ChunkedStream {
|
|
|
213
213
|
},
|
|
214
214
|
];
|
|
215
215
|
|
|
216
|
-
const responseStream = await this.#tts.client.models.generateContentStream({
|
|
217
|
-
model: this.#tts.opts.model,
|
|
218
|
-
contents,
|
|
219
|
-
config,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
216
|
try {
|
|
217
|
+
const responseStream = await this.#tts.client.models.generateContentStream({
|
|
218
|
+
model: this.#tts.opts.model,
|
|
219
|
+
contents,
|
|
220
|
+
config,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
223
|
for await (const response of responseStream) {
|
|
224
224
|
await this.#processResponse(response, bstream, requestId);
|
|
225
225
|
}
|