@livekit/agents-plugin-google 1.0.50 → 1.1.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/dist/llm.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as types from '@google/genai';\nimport { FunctionCallingConfigMode, type GenerateContentConfig, GoogleGenAI } from '@google/genai';\nimport type { APIConnectOptions } from '@livekit/agents';\nimport {\n APIConnectionError,\n APIStatusError,\n DEFAULT_API_CONNECT_OPTIONS,\n llm,\n shortuuid,\n} from '@livekit/agents';\nimport type { ChatModels } from './models.js';\nimport type { LLMTools } from './tools.js';\nimport { toFunctionDeclarations } from './utils.js';\n\ninterface GoogleFormatData {\n systemMessages: string[] | null;\n}\n\nexport interface LLMOptions {\n model: string | ChatModels;\n apiKey?: string;\n temperature?: number;\n toolChoice?: llm.ToolChoice;\n vertexai?: boolean;\n project?: string;\n location?: string;\n maxOutputTokens?: number;\n topP?: number;\n topK?: number;\n presencePenalty?: number;\n frequencyPenalty?: number;\n thinkingConfig?: types.ThinkingConfig;\n automaticFunctionCallingConfig?: types.AutomaticFunctionCallingConfig;\n geminiTools?: LLMTools;\n httpOptions?: types.HttpOptions;\n seed?: number;\n}\n\nexport class LLM extends llm.LLM {\n #opts: LLMOptions;\n #client: GoogleGenAI;\n\n label(): string {\n return 'google.LLM';\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n /**\n * Create a new instance of Google GenAI LLM.\n *\n * Environment Requirements:\n * - For VertexAI: Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the path of the service account key file or use any of the other Google Cloud auth methods.\n * The Google Cloud project and location can be set via `project` and `location` arguments or the environment variables\n * `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION`. By default, the project is inferred from the service account key file,\n * and the location defaults to \"us-central1\".\n * - For Google Gemini API: Set the `apiKey` argument or the `GOOGLE_API_KEY` environment variable.\n *\n * @param model - The model name to use. Defaults to \"gemini-2.0-flash-001\".\n * @param apiKey - The API key for Google Gemini. If not provided, it attempts to read from the `GOOGLE_API_KEY` environment variable.\n * @param vertexai - Whether to use VertexAI. If not provided, it attempts to read from the `GOOGLE_GENAI_USE_VERTEXAI` environment variable. Defaults to false.\n * @param project - The Google Cloud project to use (only for VertexAI). Defaults to undefined.\n * @param location - The location to use for VertexAI API requests. Default value is \"us-central1\".\n * @param temperature - Sampling temperature for response generation. Defaults to undefined.\n * @param maxOutputTokens - Maximum number of tokens to generate in the output. Defaults to undefined.\n * @param topP - The nucleus sampling probability for response generation. Defaults to undefined.\n * @param topK - The top-k sampling value for response generation. Defaults to undefined.\n * @param presencePenalty - Penalizes the model for generating previously mentioned concepts. Defaults to undefined.\n * @param frequencyPenalty - Penalizes the model for repeating words. Defaults to undefined.\n * @param toolChoice - Specifies whether to use tools during response generation. Defaults to \"auto\".\n * @param thinkingConfig - The thinking configuration for response generation. Defaults to undefined.\n * @param automaticFunctionCallingConfig - The automatic function calling configuration for response generation. Defaults to undefined.\n * @param geminiTools - The Gemini-specific tools to use for the session.\n * @param httpOptions - The HTTP options to use for the session.\n * @param seed - Random seed for reproducible results. Defaults to undefined.\n */\n constructor(\n {\n model,\n apiKey,\n vertexai,\n project,\n location,\n temperature,\n maxOutputTokens,\n topP,\n topK,\n presencePenalty,\n frequencyPenalty,\n toolChoice,\n thinkingConfig,\n automaticFunctionCallingConfig,\n geminiTools,\n httpOptions,\n seed,\n }: LLMOptions = {\n model: 'gemini-2.0-flash-001',\n },\n ) {\n super();\n\n const useVertexAI =\n vertexai ??\n (process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true' ||\n process.env.GOOGLE_GENAI_USE_VERTEXAI === '1');\n\n let gcpProject: string | undefined = project ?? process.env.GOOGLE_CLOUD_PROJECT;\n let gcpLocation: string | undefined = location ?? process.env.GOOGLE_CLOUD_LOCATION;\n let geminiApiKey: string | undefined = apiKey ?? process.env.GOOGLE_API_KEY;\n\n if (useVertexAI) {\n if (!gcpProject) {\n // TODO(brian): use default_async to get the project ID\n throw new Error(\n 'Project ID is required for Vertex AI. Set via project option or GOOGLE_CLOUD_PROJECT environment variable',\n );\n }\n geminiApiKey = undefined;\n } else {\n gcpProject = undefined;\n gcpLocation = undefined;\n if (!geminiApiKey) {\n throw new Error(\n 'API key is required for Google API either via apiKey or GOOGLE_API_KEY environment variable',\n );\n }\n }\n\n // Validate thinkingConfig\n if (thinkingConfig?.thinkingBudget !== undefined) {\n const budget = thinkingConfig.thinkingBudget;\n if (budget < 0 || budget > 24576) {\n throw new Error('thinkingBudget inside thinkingConfig must be between 0 and 24576');\n }\n }\n\n const clientOptions: types.GoogleGenAIOptions = useVertexAI\n ? {\n vertexai: true,\n project: gcpProject,\n location: gcpLocation,\n }\n : {\n apiKey: geminiApiKey,\n };\n\n this.#client = new GoogleGenAI(clientOptions);\n\n this.#opts = {\n model,\n vertexai: useVertexAI,\n project: gcpProject,\n location: gcpLocation,\n temperature,\n maxOutputTokens,\n topP,\n topK,\n presencePenalty,\n frequencyPenalty,\n toolChoice,\n thinkingConfig,\n automaticFunctionCallingConfig,\n geminiTools,\n httpOptions,\n seed,\n apiKey,\n };\n }\n\n chat({\n chatCtx,\n toolCtx,\n connOptions = DEFAULT_API_CONNECT_OPTIONS,\n toolChoice,\n extraKwargs,\n geminiTools,\n }: {\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions?: APIConnectOptions;\n parallelToolCalls?: boolean;\n toolChoice?: llm.ToolChoice;\n extraKwargs?: Record<string, unknown>;\n geminiTools?: LLMTools;\n }): LLMStream {\n const extras: GenerateContentConfig = { ...extraKwargs } as GenerateContentConfig;\n\n toolChoice = toolChoice !== undefined ? toolChoice : this.#opts.toolChoice;\n\n if (toolChoice) {\n let geminiToolConfig: types.ToolConfig;\n\n if (typeof toolChoice === 'object' && toolChoice.type === 'function') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.ANY,\n allowedFunctionNames: [toolChoice.function.name],\n },\n };\n } else if (toolChoice === 'required') {\n const toolNames = Object.entries(toolCtx || {}).map(([name]) => name);\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.ANY,\n allowedFunctionNames: toolNames.length > 0 ? toolNames : undefined,\n },\n };\n } else if (toolChoice === 'auto') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.AUTO,\n },\n };\n } else if (toolChoice === 'none') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.NONE,\n },\n };\n } else {\n throw new Error(`Invalid tool choice: ${toolChoice}`);\n }\n\n extras.toolConfig = geminiToolConfig;\n }\n\n if (this.#opts.temperature !== undefined) {\n extras.temperature = this.#opts.temperature;\n }\n if (this.#opts.maxOutputTokens !== undefined) {\n extras.maxOutputTokens = this.#opts.maxOutputTokens;\n }\n if (this.#opts.topP !== undefined) {\n extras.topP = this.#opts.topP;\n }\n if (this.#opts.topK !== undefined) {\n extras.topK = this.#opts.topK;\n }\n if (this.#opts.presencePenalty !== undefined) {\n extras.presencePenalty = this.#opts.presencePenalty;\n }\n if (this.#opts.frequencyPenalty !== undefined) {\n extras.frequencyPenalty = this.#opts.frequencyPenalty;\n }\n if (this.#opts.seed !== undefined) {\n extras.seed = this.#opts.seed;\n }\n\n if (this.#opts.thinkingConfig !== undefined) {\n extras.thinkingConfig = this.#opts.thinkingConfig;\n }\n\n if (this.#opts.automaticFunctionCallingConfig !== undefined) {\n extras.automaticFunctionCalling = this.#opts.automaticFunctionCallingConfig;\n }\n\n geminiTools = geminiTools !== undefined ? geminiTools : this.#opts.geminiTools;\n\n return new LLMStream(this, {\n client: this.#client,\n model: this.#opts.model,\n chatCtx,\n toolCtx,\n connOptions,\n geminiTools,\n extraKwargs: extras,\n });\n }\n}\n\nconst BLOCKED_REASONS = [\n 'SAFETY',\n 'SPII',\n 'PROHIBITED_CONTENT',\n 'BLOCKLIST',\n 'LANGUAGE',\n 'RECITATION',\n];\n\nexport class LLMStream extends llm.LLMStream {\n #client: GoogleGenAI;\n #model: string;\n #geminiTools?: LLMTools;\n #extraKwargs: GenerateContentConfig;\n\n constructor(\n llm: LLM,\n {\n client,\n model,\n chatCtx,\n toolCtx,\n connOptions,\n geminiTools,\n extraKwargs,\n }: {\n client: GoogleGenAI;\n model: string;\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions: APIConnectOptions;\n geminiTools?: LLMTools;\n extraKwargs: GenerateContentConfig;\n },\n ) {\n // Call base constructor with dev 1.0 object parameter pattern\n super(llm, { chatCtx, toolCtx, connOptions });\n this.#client = client;\n this.#model = model;\n this.#geminiTools = geminiTools;\n this.#extraKwargs = extraKwargs;\n }\n\n protected async run(): Promise<void> {\n let retryable = true;\n const requestId = `google_${Date.now()}`;\n\n try {\n const [turns, extraData] = (await this.chatCtx.toProviderFormat('google')) as [\n Record<string, unknown>[],\n GoogleFormatData,\n ];\n\n const contents: types.Content[] = turns.map((turn: Record<string, unknown>) => ({\n role: turn.role as types.Content['role'],\n parts: turn.parts as types.Part[],\n }));\n\n const functionDeclarations = this.toolCtx ? toFunctionDeclarations(this.toolCtx) : undefined;\n const tools =\n functionDeclarations && functionDeclarations.length > 0\n ? [{ functionDeclarations }]\n : undefined;\n\n let systemInstruction: types.Content | undefined = undefined;\n if (extraData.systemMessages && extraData.systemMessages.length > 0) {\n systemInstruction = {\n parts: extraData.systemMessages.map((content: string) => ({ text: content })),\n };\n }\n\n const response = await this.#client.models.generateContentStream({\n model: this.#model,\n contents,\n config: {\n ...this.#extraKwargs,\n systemInstruction,\n httpOptions: this.#extraKwargs.httpOptions ?? {\n timeout: Math.floor(this.connOptions.timeoutMs),\n },\n tools,\n },\n });\n\n for await (const chunk of response) {\n if (chunk.promptFeedback) {\n throw new APIStatusError({\n message: `Prompt feedback error: ${JSON.stringify(chunk.promptFeedback)}`,\n options: {\n retryable: false,\n requestId,\n },\n });\n }\n\n // Check for blocked reasons first — safety-blocked responses often lack content.parts,\n // so this must run before the no-content guard to avoid wasting retries.\n if (\n chunk.candidates?.[0]?.finishReason &&\n BLOCKED_REASONS.includes(chunk.candidates[0].finishReason)\n ) {\n throw new APIStatusError({\n message: `Google LLM: generation blocked - ${chunk.candidates[0].finishReason}`,\n options: {\n retryable: false,\n requestId,\n },\n });\n }\n\n if (!chunk.candidates || !chunk.candidates[0]?.content?.parts) {\n this.logger.warn(`No content in the response: ${JSON.stringify(chunk)}`);\n if (retryable) {\n throw new APIStatusError({\n message: 'Google LLM: no content in the response',\n options: {\n retryable: true,\n requestId,\n },\n });\n }\n continue;\n }\n\n if (chunk.candidates.length > 1) {\n this.logger.warn(\n 'Google LLM: there are multiple candidates in the response, returning response from the first one.',\n );\n }\n\n const candidate = chunk.candidates[0];\n const finishReason = candidate.finishReason;\n\n let chunksYielded = false;\n for (const part of candidate.content!.parts!) {\n const chatChunk = this.#parsePart(requestId, part);\n if (chatChunk) {\n chunksYielded = true;\n retryable = false;\n this.queue.put(chatChunk);\n }\n }\n\n if (finishReason === 'STOP' && !chunksYielded && retryable) {\n throw new APIStatusError({\n message: 'Google LLM: no response generated',\n options: {\n retryable,\n requestId,\n },\n });\n }\n\n if (chunk.usageMetadata) {\n const usage = chunk.usageMetadata;\n this.queue.put({\n id: requestId,\n usage: {\n completionTokens: usage.candidatesTokenCount || 0,\n promptTokens: usage.promptTokenCount || 0,\n promptCachedTokens: usage.cachedContentTokenCount || 0,\n totalTokens: usage.totalTokenCount || 0,\n },\n });\n }\n }\n } catch (error: unknown) {\n if (error instanceof APIStatusError || error instanceof APIConnectionError) {\n throw error;\n }\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: `Google LLM: 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: `Google LLM: 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: `Google LLM: Server error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable,\n },\n });\n }\n\n throw new APIConnectionError({\n message: `Google LLM: API error - ${err.message || 'Unknown error'}`,\n options: {\n retryable,\n },\n });\n }\n }\n\n #parsePart(id: string, part: types.Part): llm.ChatChunk | null {\n if (part.functionCall) {\n return {\n id,\n delta: {\n role: 'assistant',\n toolCalls: [\n llm.FunctionCall.create({\n callId: part.functionCall.id || shortuuid('function_call_'),\n name: part.functionCall.name!,\n args: JSON.stringify(part.functionCall.args!),\n // Preserve thought signature for Gemini 3+ thinking mode\n thoughtSignature: part.thoughtSignature,\n }),\n ],\n },\n };\n }\n\n if (!part.text) {\n return null;\n }\n\n return {\n id,\n delta: {\n content: part.text,\n role: 'assistant',\n },\n };\n }\n}\n"],"mappings":"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","names":["llm"]}
1
+ {"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as types from '@google/genai';\nimport { FunctionCallingConfigMode, type GenerateContentConfig, GoogleGenAI } from '@google/genai';\nimport type { APIConnectOptions } from '@livekit/agents';\nimport {\n APIConnectionError,\n APIStatusError,\n DEFAULT_API_CONNECT_OPTIONS,\n llm,\n shortuuid,\n} from '@livekit/agents';\nimport type { ChatModels } from './models.js';\nimport type { LLMTools } from './tools.js';\nimport { toFunctionDeclarations } from './utils.js';\n\ninterface GoogleFormatData {\n systemMessages: string[] | null;\n}\n\nexport interface LLMOptions {\n model: string | ChatModels;\n apiKey?: string;\n temperature?: number;\n toolChoice?: llm.ToolChoice;\n vertexai?: boolean;\n project?: string;\n location?: string;\n maxOutputTokens?: number;\n topP?: number;\n topK?: number;\n presencePenalty?: number;\n frequencyPenalty?: number;\n thinkingConfig?: types.ThinkingConfig;\n automaticFunctionCallingConfig?: types.AutomaticFunctionCallingConfig;\n geminiTools?: LLMTools;\n httpOptions?: types.HttpOptions;\n seed?: number;\n}\n\nexport class LLM extends llm.LLM {\n #opts: LLMOptions;\n #client: GoogleGenAI;\n\n label(): string {\n return 'google.LLM';\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n if (this.#opts.vertexai) {\n return 'Vertex AI';\n }\n return 'Gemini';\n }\n\n /**\n * Create a new instance of Google GenAI LLM.\n *\n * Environment Requirements:\n * - For VertexAI: Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the path of the service account key file or use any of the other Google Cloud auth methods.\n * The Google Cloud project and location can be set via `project` and `location` arguments or the environment variables\n * `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION`. By default, the project is inferred from the service account key file,\n * and the location defaults to \"us-central1\".\n * - For Google Gemini API: Set the `apiKey` argument or the `GOOGLE_API_KEY` environment variable.\n *\n * @param model - The model name to use. Defaults to \"gemini-2.0-flash-001\".\n * @param apiKey - The API key for Google Gemini. If not provided, it attempts to read from the `GOOGLE_API_KEY` environment variable.\n * @param vertexai - Whether to use VertexAI. If not provided, it attempts to read from the `GOOGLE_GENAI_USE_VERTEXAI` environment variable. Defaults to false.\n * @param project - The Google Cloud project to use (only for VertexAI). Defaults to undefined.\n * @param location - The location to use for VertexAI API requests. Default value is \"us-central1\".\n * @param temperature - Sampling temperature for response generation. Defaults to undefined.\n * @param maxOutputTokens - Maximum number of tokens to generate in the output. Defaults to undefined.\n * @param topP - The nucleus sampling probability for response generation. Defaults to undefined.\n * @param topK - The top-k sampling value for response generation. Defaults to undefined.\n * @param presencePenalty - Penalizes the model for generating previously mentioned concepts. Defaults to undefined.\n * @param frequencyPenalty - Penalizes the model for repeating words. Defaults to undefined.\n * @param toolChoice - Specifies whether to use tools during response generation. Defaults to \"auto\".\n * @param thinkingConfig - The thinking configuration for response generation. Defaults to undefined.\n * @param automaticFunctionCallingConfig - The automatic function calling configuration for response generation. Defaults to undefined.\n * @param geminiTools - The Gemini-specific tools to use for the session.\n * @param httpOptions - The HTTP options to use for the session.\n * @param seed - Random seed for reproducible results. Defaults to undefined.\n */\n constructor(\n {\n model,\n apiKey,\n vertexai,\n project,\n location,\n temperature,\n maxOutputTokens,\n topP,\n topK,\n presencePenalty,\n frequencyPenalty,\n toolChoice,\n thinkingConfig,\n automaticFunctionCallingConfig,\n geminiTools,\n httpOptions,\n seed,\n }: LLMOptions = {\n model: 'gemini-2.0-flash-001',\n },\n ) {\n super();\n\n const useVertexAI =\n vertexai ??\n (process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true' ||\n process.env.GOOGLE_GENAI_USE_VERTEXAI === '1');\n\n let gcpProject: string | undefined = project ?? process.env.GOOGLE_CLOUD_PROJECT;\n let gcpLocation: string | undefined = location ?? process.env.GOOGLE_CLOUD_LOCATION;\n let geminiApiKey: string | undefined = apiKey ?? process.env.GOOGLE_API_KEY;\n\n if (useVertexAI) {\n if (!gcpProject) {\n // TODO(brian): use default_async to get the project ID\n throw new Error(\n 'Project ID is required for Vertex AI. Set via project option or GOOGLE_CLOUD_PROJECT environment variable',\n );\n }\n geminiApiKey = undefined;\n } else {\n gcpProject = undefined;\n gcpLocation = undefined;\n if (!geminiApiKey) {\n throw new Error(\n 'API key is required for Google API either via apiKey or GOOGLE_API_KEY environment variable',\n );\n }\n }\n\n // Validate thinkingConfig\n if (thinkingConfig?.thinkingBudget !== undefined) {\n const budget = thinkingConfig.thinkingBudget;\n if (budget < 0 || budget > 24576) {\n throw new Error('thinkingBudget inside thinkingConfig must be between 0 and 24576');\n }\n }\n\n const clientOptions: types.GoogleGenAIOptions = useVertexAI\n ? {\n vertexai: true,\n project: gcpProject,\n location: gcpLocation,\n }\n : {\n apiKey: geminiApiKey,\n };\n\n this.#client = new GoogleGenAI(clientOptions);\n\n this.#opts = {\n model,\n vertexai: useVertexAI,\n project: gcpProject,\n location: gcpLocation,\n temperature,\n maxOutputTokens,\n topP,\n topK,\n presencePenalty,\n frequencyPenalty,\n toolChoice,\n thinkingConfig,\n automaticFunctionCallingConfig,\n geminiTools,\n httpOptions,\n seed,\n apiKey,\n };\n }\n\n chat({\n chatCtx,\n toolCtx,\n connOptions = DEFAULT_API_CONNECT_OPTIONS,\n toolChoice,\n extraKwargs,\n geminiTools,\n }: {\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions?: APIConnectOptions;\n parallelToolCalls?: boolean;\n toolChoice?: llm.ToolChoice;\n extraKwargs?: Record<string, unknown>;\n geminiTools?: LLMTools;\n }): LLMStream {\n const extras: GenerateContentConfig = { ...extraKwargs } as GenerateContentConfig;\n\n toolChoice = toolChoice !== undefined ? toolChoice : this.#opts.toolChoice;\n\n if (toolChoice) {\n let geminiToolConfig: types.ToolConfig;\n\n if (typeof toolChoice === 'object' && toolChoice.type === 'function') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.ANY,\n allowedFunctionNames: [toolChoice.function.name],\n },\n };\n } else if (toolChoice === 'required') {\n const toolNames = Object.entries(toolCtx || {}).map(([name]) => name);\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.ANY,\n allowedFunctionNames: toolNames.length > 0 ? toolNames : undefined,\n },\n };\n } else if (toolChoice === 'auto') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.AUTO,\n },\n };\n } else if (toolChoice === 'none') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.NONE,\n },\n };\n } else {\n throw new Error(`Invalid tool choice: ${toolChoice}`);\n }\n\n extras.toolConfig = geminiToolConfig;\n }\n\n if (this.#opts.temperature !== undefined) {\n extras.temperature = this.#opts.temperature;\n }\n if (this.#opts.maxOutputTokens !== undefined) {\n extras.maxOutputTokens = this.#opts.maxOutputTokens;\n }\n if (this.#opts.topP !== undefined) {\n extras.topP = this.#opts.topP;\n }\n if (this.#opts.topK !== undefined) {\n extras.topK = this.#opts.topK;\n }\n if (this.#opts.presencePenalty !== undefined) {\n extras.presencePenalty = this.#opts.presencePenalty;\n }\n if (this.#opts.frequencyPenalty !== undefined) {\n extras.frequencyPenalty = this.#opts.frequencyPenalty;\n }\n if (this.#opts.seed !== undefined) {\n extras.seed = this.#opts.seed;\n }\n\n if (this.#opts.thinkingConfig !== undefined) {\n extras.thinkingConfig = this.#opts.thinkingConfig;\n }\n\n if (this.#opts.automaticFunctionCallingConfig !== undefined) {\n extras.automaticFunctionCalling = this.#opts.automaticFunctionCallingConfig;\n }\n\n geminiTools = geminiTools !== undefined ? geminiTools : this.#opts.geminiTools;\n\n return new LLMStream(this, {\n client: this.#client,\n model: this.#opts.model,\n chatCtx,\n toolCtx,\n connOptions,\n geminiTools,\n extraKwargs: extras,\n });\n }\n}\n\nconst BLOCKED_REASONS = [\n 'SAFETY',\n 'SPII',\n 'PROHIBITED_CONTENT',\n 'BLOCKLIST',\n 'LANGUAGE',\n 'RECITATION',\n];\n\nexport class LLMStream extends llm.LLMStream {\n #client: GoogleGenAI;\n #model: string;\n #geminiTools?: LLMTools;\n #extraKwargs: GenerateContentConfig;\n\n constructor(\n llm: LLM,\n {\n client,\n model,\n chatCtx,\n toolCtx,\n connOptions,\n geminiTools,\n extraKwargs,\n }: {\n client: GoogleGenAI;\n model: string;\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions: APIConnectOptions;\n geminiTools?: LLMTools;\n extraKwargs: GenerateContentConfig;\n },\n ) {\n // Call base constructor with dev 1.0 object parameter pattern\n super(llm, { chatCtx, toolCtx, connOptions });\n this.#client = client;\n this.#model = model;\n this.#geminiTools = geminiTools;\n this.#extraKwargs = extraKwargs;\n }\n\n protected async run(): Promise<void> {\n let retryable = true;\n const requestId = `google_${Date.now()}`;\n\n try {\n const [turns, extraData] = (await this.chatCtx.toProviderFormat('google')) as [\n Record<string, unknown>[],\n GoogleFormatData,\n ];\n\n const contents: types.Content[] = turns.map((turn: Record<string, unknown>) => ({\n role: turn.role as types.Content['role'],\n parts: turn.parts as types.Part[],\n }));\n\n const functionDeclarations = this.toolCtx ? toFunctionDeclarations(this.toolCtx) : undefined;\n const tools =\n functionDeclarations && functionDeclarations.length > 0\n ? [{ functionDeclarations }]\n : undefined;\n\n let systemInstruction: types.Content | undefined = undefined;\n if (extraData.systemMessages && extraData.systemMessages.length > 0) {\n systemInstruction = {\n parts: extraData.systemMessages.map((content: string) => ({ text: content })),\n };\n }\n\n const response = await this.#client.models.generateContentStream({\n model: this.#model,\n contents,\n config: {\n ...this.#extraKwargs,\n systemInstruction,\n httpOptions: this.#extraKwargs.httpOptions ?? {\n timeout: Math.floor(this.connOptions.timeoutMs),\n },\n tools,\n },\n });\n\n for await (const chunk of response) {\n if (chunk.promptFeedback) {\n throw new APIStatusError({\n message: `Prompt feedback error: ${JSON.stringify(chunk.promptFeedback)}`,\n options: {\n retryable: false,\n requestId,\n },\n });\n }\n\n // Check for blocked reasons first — safety-blocked responses often lack content.parts,\n // so this must run before the no-content guard to avoid wasting retries.\n if (\n chunk.candidates?.[0]?.finishReason &&\n BLOCKED_REASONS.includes(chunk.candidates[0].finishReason)\n ) {\n throw new APIStatusError({\n message: `Google LLM: generation blocked - ${chunk.candidates[0].finishReason}`,\n options: {\n retryable: false,\n requestId,\n },\n });\n }\n\n if (!chunk.candidates || !chunk.candidates[0]?.content?.parts) {\n this.logger.warn(`No content in the response: ${JSON.stringify(chunk)}`);\n if (retryable) {\n throw new APIStatusError({\n message: 'Google LLM: no content in the response',\n options: {\n retryable: true,\n requestId,\n },\n });\n }\n continue;\n }\n\n if (chunk.candidates.length > 1) {\n this.logger.warn(\n 'Google LLM: there are multiple candidates in the response, returning response from the first one.',\n );\n }\n\n const candidate = chunk.candidates[0];\n const finishReason = candidate.finishReason;\n\n let chunksYielded = false;\n for (const part of candidate.content!.parts!) {\n const chatChunk = this.#parsePart(requestId, part);\n if (chatChunk) {\n chunksYielded = true;\n retryable = false;\n this.queue.put(chatChunk);\n }\n }\n\n if (finishReason === 'STOP' && !chunksYielded && retryable) {\n throw new APIStatusError({\n message: 'Google LLM: no response generated',\n options: {\n retryable,\n requestId,\n },\n });\n }\n\n if (chunk.usageMetadata) {\n const usage = chunk.usageMetadata;\n this.queue.put({\n id: requestId,\n usage: {\n completionTokens: usage.candidatesTokenCount || 0,\n promptTokens: usage.promptTokenCount || 0,\n promptCachedTokens: usage.cachedContentTokenCount || 0,\n totalTokens: usage.totalTokenCount || 0,\n },\n });\n }\n }\n } catch (error: unknown) {\n if (error instanceof APIStatusError || error instanceof APIConnectionError) {\n throw error;\n }\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: `Google LLM: 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: `Google LLM: 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: `Google LLM: Server error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable,\n },\n });\n }\n\n throw new APIConnectionError({\n message: `Google LLM: API error - ${err.message || 'Unknown error'}`,\n options: {\n retryable,\n },\n });\n }\n }\n\n #parsePart(id: string, part: types.Part): llm.ChatChunk | null {\n if (part.functionCall) {\n return {\n id,\n delta: {\n role: 'assistant',\n toolCalls: [\n llm.FunctionCall.create({\n callId: part.functionCall.id || shortuuid('function_call_'),\n name: part.functionCall.name!,\n args: JSON.stringify(part.functionCall.args!),\n // Preserve thought signature for Gemini 3+ thinking mode\n thoughtSignature: part.thoughtSignature,\n }),\n ],\n },\n };\n }\n\n if (!part.text) {\n return null;\n }\n\n return {\n id,\n delta: {\n content: part.text,\n role: 'assistant',\n },\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,mBAAmF;AAEnF,oBAMO;AAGP,mBAAuC;AA0BhC,MAAM,YAAY,kBAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EAEA,QAAgB;AACd,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,IAAI,WAAmB;AACrB,QAAI,KAAK,MAAM,UAAU;AACvB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,YACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAgB;AAAA,IACd,OAAO;AAAA,EACT,GACA;AACA,UAAM;AAEN,UAAM,cACJ,aACC,QAAQ,IAAI,8BAA8B,UACzC,QAAQ,IAAI,8BAA8B;AAE9C,QAAI,aAAiC,WAAW,QAAQ,IAAI;AAC5D,QAAI,cAAkC,YAAY,QAAQ,IAAI;AAC9D,QAAI,eAAmC,UAAU,QAAQ,IAAI;AAE7D,QAAI,aAAa;AACf,UAAI,CAAC,YAAY;AAEf,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,qBAAe;AAAA,IACjB,OAAO;AACL,mBAAa;AACb,oBAAc;AACd,UAAI,CAAC,cAAc;AACjB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAI,iDAAgB,oBAAmB,QAAW;AAChD,YAAM,SAAS,eAAe;AAC9B,UAAI,SAAS,KAAK,SAAS,OAAO;AAChC,cAAM,IAAI,MAAM,kEAAkE;AAAA,MACpF;AAAA,IACF;AAEA,UAAM,gBAA0C,cAC5C;AAAA,MACE,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,IACZ,IACA;AAAA,MACE,QAAQ;AAAA,IACV;AAEJ,SAAK,UAAU,IAAI,yBAAY,aAAa;AAE5C,SAAK,QAAQ;AAAA,MACX;AAAA,MACA,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,KAAK;AAAA,IACH;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAQc;AACZ,UAAM,SAAgC,EAAE,GAAG,YAAY;AAEvD,iBAAa,eAAe,SAAY,aAAa,KAAK,MAAM;AAEhE,QAAI,YAAY;AACd,UAAI;AAEJ,UAAI,OAAO,eAAe,YAAY,WAAW,SAAS,YAAY;AACpE,2BAAmB;AAAA,UACjB,uBAAuB;AAAA,YACrB,MAAM,uCAA0B;AAAA,YAChC,sBAAsB,CAAC,WAAW,SAAS,IAAI;AAAA,UACjD;AAAA,QACF;AAAA,MACF,WAAW,eAAe,YAAY;AACpC,cAAM,YAAY,OAAO,QAAQ,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AACpE,2BAAmB;AAAA,UACjB,uBAAuB;AAAA,YACrB,MAAM,uCAA0B;AAAA,YAChC,sBAAsB,UAAU,SAAS,IAAI,YAAY;AAAA,UAC3D;AAAA,QACF;AAAA,MACF,WAAW,eAAe,QAAQ;AAChC,2BAAmB;AAAA,UACjB,uBAAuB;AAAA,YACrB,MAAM,uCAA0B;AAAA,UAClC;AAAA,QACF;AAAA,MACF,WAAW,eAAe,QAAQ;AAChC,2BAAmB;AAAA,UACjB,uBAAuB;AAAA,YACrB,MAAM,uCAA0B;AAAA,UAClC;AAAA,QACF;AAAA,MACF,OAAO;AACL,cAAM,IAAI,MAAM,wBAAwB,UAAU,EAAE;AAAA,MACtD;AAEA,aAAO,aAAa;AAAA,IACtB;AAEA,QAAI,KAAK,MAAM,gBAAgB,QAAW;AACxC,aAAO,cAAc,KAAK,MAAM;AAAA,IAClC;AACA,QAAI,KAAK,MAAM,oBAAoB,QAAW;AAC5C,aAAO,kBAAkB,KAAK,MAAM;AAAA,IACtC;AACA,QAAI,KAAK,MAAM,SAAS,QAAW;AACjC,aAAO,OAAO,KAAK,MAAM;AAAA,IAC3B;AACA,QAAI,KAAK,MAAM,SAAS,QAAW;AACjC,aAAO,OAAO,KAAK,MAAM;AAAA,IAC3B;AACA,QAAI,KAAK,MAAM,oBAAoB,QAAW;AAC5C,aAAO,kBAAkB,KAAK,MAAM;AAAA,IACtC;AACA,QAAI,KAAK,MAAM,qBAAqB,QAAW;AAC7C,aAAO,mBAAmB,KAAK,MAAM;AAAA,IACvC;AACA,QAAI,KAAK,MAAM,SAAS,QAAW;AACjC,aAAO,OAAO,KAAK,MAAM;AAAA,IAC3B;AAEA,QAAI,KAAK,MAAM,mBAAmB,QAAW;AAC3C,aAAO,iBAAiB,KAAK,MAAM;AAAA,IACrC;AAEA,QAAI,KAAK,MAAM,mCAAmC,QAAW;AAC3D,aAAO,2BAA2B,KAAK,MAAM;AAAA,IAC/C;AAEA,kBAAc,gBAAgB,SAAY,cAAc,KAAK,MAAM;AAEnE,WAAO,IAAI,UAAU,MAAM;AAAA,MACzB,QAAQ,KAAK;AAAA,MACb,OAAO,KAAK,MAAM;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AACF;AAEA,MAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,kBAAkB,kBAAI,UAAU;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YACEA,MACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GASA;AAEA,UAAMA,MAAK,EAAE,SAAS,SAAS,YAAY,CAAC;AAC5C,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,eAAe;AACpB,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,MAAgB,MAAqB;AArUvC;AAsUI,QAAI,YAAY;AAChB,UAAM,YAAY,UAAU,KAAK,IAAI,CAAC;AAEtC,QAAI;AACF,YAAM,CAAC,OAAO,SAAS,IAAK,MAAM,KAAK,QAAQ,iBAAiB,QAAQ;AAKxE,YAAM,WAA4B,MAAM,IAAI,CAAC,UAAmC;AAAA,QAC9E,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,MACd,EAAE;AAEF,YAAM,uBAAuB,KAAK,cAAU,qCAAuB,KAAK,OAAO,IAAI;AACnF,YAAM,QACJ,wBAAwB,qBAAqB,SAAS,IAClD,CAAC,EAAE,qBAAqB,CAAC,IACzB;AAEN,UAAI,oBAA+C;AACnD,UAAI,UAAU,kBAAkB,UAAU,eAAe,SAAS,GAAG;AACnE,4BAAoB;AAAA,UAClB,OAAO,UAAU,eAAe,IAAI,CAAC,aAAqB,EAAE,MAAM,QAAQ,EAAE;AAAA,QAC9E;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,KAAK,QAAQ,OAAO,sBAAsB;AAAA,QAC/D,OAAO,KAAK;AAAA,QACZ;AAAA,QACA,QAAQ;AAAA,UACN,GAAG,KAAK;AAAA,UACR;AAAA,UACA,aAAa,KAAK,aAAa,eAAe;AAAA,YAC5C,SAAS,KAAK,MAAM,KAAK,YAAY,SAAS;AAAA,UAChD;AAAA,UACA;AAAA,QACF;AAAA,MACF,CAAC;AAED,uBAAiB,SAAS,UAAU;AAClC,YAAI,MAAM,gBAAgB;AACxB,gBAAM,IAAI,6BAAe;AAAA,YACvB,SAAS,0BAA0B,KAAK,UAAU,MAAM,cAAc,CAAC;AAAA,YACvE,SAAS;AAAA,cACP,WAAW;AAAA,cACX;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AAIA,cACE,iBAAM,eAAN,mBAAmB,OAAnB,mBAAuB,iBACvB,gBAAgB,SAAS,MAAM,WAAW,CAAC,EAAE,YAAY,GACzD;AACA,gBAAM,IAAI,6BAAe;AAAA,YACvB,SAAS,oCAAoC,MAAM,WAAW,CAAC,EAAE,YAAY;AAAA,YAC7E,SAAS;AAAA,cACP,WAAW;AAAA,cACX;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AAEA,YAAI,CAAC,MAAM,cAAc,GAAC,iBAAM,WAAW,CAAC,MAAlB,mBAAqB,YAArB,mBAA8B,QAAO;AAC7D,eAAK,OAAO,KAAK,+BAA+B,KAAK,UAAU,KAAK,CAAC,EAAE;AACvE,cAAI,WAAW;AACb,kBAAM,IAAI,6BAAe;AAAA,cACvB,SAAS;AAAA,cACT,SAAS;AAAA,gBACP,WAAW;AAAA,gBACX;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AACA;AAAA,QACF;AAEA,YAAI,MAAM,WAAW,SAAS,GAAG;AAC/B,eAAK,OAAO;AAAA,YACV;AAAA,UACF;AAAA,QACF;AAEA,cAAM,YAAY,MAAM,WAAW,CAAC;AACpC,cAAM,eAAe,UAAU;AAE/B,YAAI,gBAAgB;AACpB,mBAAW,QAAQ,UAAU,QAAS,OAAQ;AAC5C,gBAAM,YAAY,KAAK,WAAW,WAAW,IAAI;AACjD,cAAI,WAAW;AACb,4BAAgB;AAChB,wBAAY;AACZ,iBAAK,MAAM,IAAI,SAAS;AAAA,UAC1B;AAAA,QACF;AAEA,YAAI,iBAAiB,UAAU,CAAC,iBAAiB,WAAW;AAC1D,gBAAM,IAAI,6BAAe;AAAA,YACvB,SAAS;AAAA,YACT,SAAS;AAAA,cACP;AAAA,cACA;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AAEA,YAAI,MAAM,eAAe;AACvB,gBAAM,QAAQ,MAAM;AACpB,eAAK,MAAM,IAAI;AAAA,YACb,IAAI;AAAA,YACJ,OAAO;AAAA,cACL,kBAAkB,MAAM,wBAAwB;AAAA,cAChD,cAAc,MAAM,oBAAoB;AAAA,cACxC,oBAAoB,MAAM,2BAA2B;AAAA,cACrD,aAAa,MAAM,mBAAmB;AAAA,YACxC;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,SAAS,OAAgB;AACvB,UAAI,iBAAiB,gCAAkB,iBAAiB,kCAAoB;AAC1E,cAAM;AAAA,MACR;AAEA,YAAM,MAAM;AAOZ,UAAI,IAAI,QAAQ,IAAI,QAAQ,OAAO,IAAI,OAAO,KAAK;AACjD,YAAI,IAAI,SAAS,KAAK;AACpB,gBAAM,IAAI,6BAAe;AAAA,YACvB,SAAS,kCAAkC,IAAI,WAAW,eAAe;AAAA,YACzE,SAAS;AAAA,cACP,YAAY;AAAA,cACZ,WAAW;AAAA,YACb;AAAA,UACF,CAAC;AAAA,QACH,OAAO;AACL,gBAAM,IAAI,6BAAe;AAAA,YACvB,SAAS,6BAA6B,IAAI,IAAI,OAAO,IAAI,WAAW,eAAe;AAAA,YACnF,SAAS;AAAA,cACP,YAAY,IAAI;AAAA,cAChB,WAAW;AAAA,YACb;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAEA,UAAI,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAC/B,cAAM,IAAI,6BAAe;AAAA,UACvB,SAAS,6BAA6B,IAAI,IAAI,OAAO,IAAI,WAAW,eAAe;AAAA,UACnF,SAAS;AAAA,YACP,YAAY,IAAI;AAAA,YAChB;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,IAAI,iCAAmB;AAAA,QAC3B,SAAS,2BAA2B,IAAI,WAAW,eAAe;AAAA,QAClE,SAAS;AAAA,UACP;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,WAAW,IAAY,MAAwC;AAC7D,QAAI,KAAK,cAAc;AACrB,aAAO;AAAA,QACL;AAAA,QACA,OAAO;AAAA,UACL,MAAM;AAAA,UACN,WAAW;AAAA,YACT,kBAAI,aAAa,OAAO;AAAA,cACtB,QAAQ,KAAK,aAAa,UAAM,yBAAU,gBAAgB;AAAA,cAC1D,MAAM,KAAK,aAAa;AAAA,cACxB,MAAM,KAAK,UAAU,KAAK,aAAa,IAAK;AAAA;AAAA,cAE5C,kBAAkB,KAAK;AAAA,YACzB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,MAAM;AACd,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,QACL,SAAS,KAAK;AAAA,QACd,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;","names":["llm"]}
package/dist/llm.d.cts CHANGED
@@ -27,6 +27,7 @@ export declare class LLM extends llm.LLM {
27
27
  #private;
28
28
  label(): string;
29
29
  get model(): string;
30
+ get provider(): string;
30
31
  /**
31
32
  * Create a new instance of Google GenAI LLM.
32
33
  *
package/dist/llm.d.ts CHANGED
@@ -27,6 +27,7 @@ export declare class LLM extends llm.LLM {
27
27
  #private;
28
28
  label(): string;
29
29
  get model(): string;
30
+ get provider(): string;
30
31
  /**
31
32
  * Create a new instance of Google GenAI LLM.
32
33
  *
package/dist/llm.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,KAAK,MAAM,eAAe,CAAC;AAC5C,OAAO,EAA6B,KAAK,qBAAqB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAIL,GAAG,EAEJ,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAO3C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC;IACtC,8BAA8B,CAAC,EAAE,KAAK,CAAC,8BAA8B,CAAC;IACtE,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,GAAI,SAAQ,GAAG,CAAC,GAAG;;IAI9B,KAAK,IAAI,MAAM;IAIf,IAAI,KAAK,IAAI,MAAM,CAElB;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;gBAED,EACE,KAAK,EACL,MAAM,EACN,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,WAAW,EACX,eAAe,EACf,IAAI,EACJ,IAAI,EACJ,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,8BAA8B,EAC9B,WAAW,EACX,WAAW,EACX,IAAI,GACL,GAAE,UAEF;IAwEH,IAAI,CAAC,EACH,OAAO,EACP,OAAO,EACP,WAAyC,EACzC,UAAU,EACV,WAAW,EACX,WAAW,GACZ,EAAE;QACD,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC;QACzB,OAAO,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC;QAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAC;QAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,UAAU,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtC,WAAW,CAAC,EAAE,QAAQ,CAAC;KACxB,GAAG,SAAS;CAoFd;AAWD,qBAAa,SAAU,SAAQ,GAAG,CAAC,SAAS;;gBAOxC,GAAG,EAAE,GAAG,EACR,EACE,MAAM,EACN,KAAK,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,WAAW,EACX,WAAW,GACZ,EAAE;QACD,MAAM,EAAE,WAAW,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC;QACzB,OAAO,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC;QAC1B,WAAW,EAAE,iBAAiB,CAAC;QAC/B,WAAW,CAAC,EAAE,QAAQ,CAAC;QACvB,WAAW,EAAE,qBAAqB,CAAC;KACpC;cAUa,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA6MrC"}
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"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"}
package/dist/llm.js CHANGED
@@ -16,6 +16,12 @@ class LLM extends llm.LLM {
16
16
  get model() {
17
17
  return this.#opts.model;
18
18
  }
19
+ get provider() {
20
+ if (this.#opts.vertexai) {
21
+ return "Vertex AI";
22
+ }
23
+ return "Gemini";
24
+ }
19
25
  /**
20
26
  * Create a new instance of Google GenAI LLM.
21
27
  *
package/dist/llm.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as types from '@google/genai';\nimport { FunctionCallingConfigMode, type GenerateContentConfig, GoogleGenAI } from '@google/genai';\nimport type { APIConnectOptions } from '@livekit/agents';\nimport {\n APIConnectionError,\n APIStatusError,\n DEFAULT_API_CONNECT_OPTIONS,\n llm,\n shortuuid,\n} from '@livekit/agents';\nimport type { ChatModels } from './models.js';\nimport type { LLMTools } from './tools.js';\nimport { toFunctionDeclarations } from './utils.js';\n\ninterface GoogleFormatData {\n systemMessages: string[] | null;\n}\n\nexport interface LLMOptions {\n model: string | ChatModels;\n apiKey?: string;\n temperature?: number;\n toolChoice?: llm.ToolChoice;\n vertexai?: boolean;\n project?: string;\n location?: string;\n maxOutputTokens?: number;\n topP?: number;\n topK?: number;\n presencePenalty?: number;\n frequencyPenalty?: number;\n thinkingConfig?: types.ThinkingConfig;\n automaticFunctionCallingConfig?: types.AutomaticFunctionCallingConfig;\n geminiTools?: LLMTools;\n httpOptions?: types.HttpOptions;\n seed?: number;\n}\n\nexport class LLM extends llm.LLM {\n #opts: LLMOptions;\n #client: GoogleGenAI;\n\n label(): string {\n return 'google.LLM';\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n /**\n * Create a new instance of Google GenAI LLM.\n *\n * Environment Requirements:\n * - For VertexAI: Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the path of the service account key file or use any of the other Google Cloud auth methods.\n * The Google Cloud project and location can be set via `project` and `location` arguments or the environment variables\n * `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION`. By default, the project is inferred from the service account key file,\n * and the location defaults to \"us-central1\".\n * - For Google Gemini API: Set the `apiKey` argument or the `GOOGLE_API_KEY` environment variable.\n *\n * @param model - The model name to use. Defaults to \"gemini-2.0-flash-001\".\n * @param apiKey - The API key for Google Gemini. If not provided, it attempts to read from the `GOOGLE_API_KEY` environment variable.\n * @param vertexai - Whether to use VertexAI. If not provided, it attempts to read from the `GOOGLE_GENAI_USE_VERTEXAI` environment variable. Defaults to false.\n * @param project - The Google Cloud project to use (only for VertexAI). Defaults to undefined.\n * @param location - The location to use for VertexAI API requests. Default value is \"us-central1\".\n * @param temperature - Sampling temperature for response generation. Defaults to undefined.\n * @param maxOutputTokens - Maximum number of tokens to generate in the output. Defaults to undefined.\n * @param topP - The nucleus sampling probability for response generation. Defaults to undefined.\n * @param topK - The top-k sampling value for response generation. Defaults to undefined.\n * @param presencePenalty - Penalizes the model for generating previously mentioned concepts. Defaults to undefined.\n * @param frequencyPenalty - Penalizes the model for repeating words. Defaults to undefined.\n * @param toolChoice - Specifies whether to use tools during response generation. Defaults to \"auto\".\n * @param thinkingConfig - The thinking configuration for response generation. Defaults to undefined.\n * @param automaticFunctionCallingConfig - The automatic function calling configuration for response generation. Defaults to undefined.\n * @param geminiTools - The Gemini-specific tools to use for the session.\n * @param httpOptions - The HTTP options to use for the session.\n * @param seed - Random seed for reproducible results. Defaults to undefined.\n */\n constructor(\n {\n model,\n apiKey,\n vertexai,\n project,\n location,\n temperature,\n maxOutputTokens,\n topP,\n topK,\n presencePenalty,\n frequencyPenalty,\n toolChoice,\n thinkingConfig,\n automaticFunctionCallingConfig,\n geminiTools,\n httpOptions,\n seed,\n }: LLMOptions = {\n model: 'gemini-2.0-flash-001',\n },\n ) {\n super();\n\n const useVertexAI =\n vertexai ??\n (process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true' ||\n process.env.GOOGLE_GENAI_USE_VERTEXAI === '1');\n\n let gcpProject: string | undefined = project ?? process.env.GOOGLE_CLOUD_PROJECT;\n let gcpLocation: string | undefined = location ?? process.env.GOOGLE_CLOUD_LOCATION;\n let geminiApiKey: string | undefined = apiKey ?? process.env.GOOGLE_API_KEY;\n\n if (useVertexAI) {\n if (!gcpProject) {\n // TODO(brian): use default_async to get the project ID\n throw new Error(\n 'Project ID is required for Vertex AI. Set via project option or GOOGLE_CLOUD_PROJECT environment variable',\n );\n }\n geminiApiKey = undefined;\n } else {\n gcpProject = undefined;\n gcpLocation = undefined;\n if (!geminiApiKey) {\n throw new Error(\n 'API key is required for Google API either via apiKey or GOOGLE_API_KEY environment variable',\n );\n }\n }\n\n // Validate thinkingConfig\n if (thinkingConfig?.thinkingBudget !== undefined) {\n const budget = thinkingConfig.thinkingBudget;\n if (budget < 0 || budget > 24576) {\n throw new Error('thinkingBudget inside thinkingConfig must be between 0 and 24576');\n }\n }\n\n const clientOptions: types.GoogleGenAIOptions = useVertexAI\n ? {\n vertexai: true,\n project: gcpProject,\n location: gcpLocation,\n }\n : {\n apiKey: geminiApiKey,\n };\n\n this.#client = new GoogleGenAI(clientOptions);\n\n this.#opts = {\n model,\n vertexai: useVertexAI,\n project: gcpProject,\n location: gcpLocation,\n temperature,\n maxOutputTokens,\n topP,\n topK,\n presencePenalty,\n frequencyPenalty,\n toolChoice,\n thinkingConfig,\n automaticFunctionCallingConfig,\n geminiTools,\n httpOptions,\n seed,\n apiKey,\n };\n }\n\n chat({\n chatCtx,\n toolCtx,\n connOptions = DEFAULT_API_CONNECT_OPTIONS,\n toolChoice,\n extraKwargs,\n geminiTools,\n }: {\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions?: APIConnectOptions;\n parallelToolCalls?: boolean;\n toolChoice?: llm.ToolChoice;\n extraKwargs?: Record<string, unknown>;\n geminiTools?: LLMTools;\n }): LLMStream {\n const extras: GenerateContentConfig = { ...extraKwargs } as GenerateContentConfig;\n\n toolChoice = toolChoice !== undefined ? toolChoice : this.#opts.toolChoice;\n\n if (toolChoice) {\n let geminiToolConfig: types.ToolConfig;\n\n if (typeof toolChoice === 'object' && toolChoice.type === 'function') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.ANY,\n allowedFunctionNames: [toolChoice.function.name],\n },\n };\n } else if (toolChoice === 'required') {\n const toolNames = Object.entries(toolCtx || {}).map(([name]) => name);\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.ANY,\n allowedFunctionNames: toolNames.length > 0 ? toolNames : undefined,\n },\n };\n } else if (toolChoice === 'auto') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.AUTO,\n },\n };\n } else if (toolChoice === 'none') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.NONE,\n },\n };\n } else {\n throw new Error(`Invalid tool choice: ${toolChoice}`);\n }\n\n extras.toolConfig = geminiToolConfig;\n }\n\n if (this.#opts.temperature !== undefined) {\n extras.temperature = this.#opts.temperature;\n }\n if (this.#opts.maxOutputTokens !== undefined) {\n extras.maxOutputTokens = this.#opts.maxOutputTokens;\n }\n if (this.#opts.topP !== undefined) {\n extras.topP = this.#opts.topP;\n }\n if (this.#opts.topK !== undefined) {\n extras.topK = this.#opts.topK;\n }\n if (this.#opts.presencePenalty !== undefined) {\n extras.presencePenalty = this.#opts.presencePenalty;\n }\n if (this.#opts.frequencyPenalty !== undefined) {\n extras.frequencyPenalty = this.#opts.frequencyPenalty;\n }\n if (this.#opts.seed !== undefined) {\n extras.seed = this.#opts.seed;\n }\n\n if (this.#opts.thinkingConfig !== undefined) {\n extras.thinkingConfig = this.#opts.thinkingConfig;\n }\n\n if (this.#opts.automaticFunctionCallingConfig !== undefined) {\n extras.automaticFunctionCalling = this.#opts.automaticFunctionCallingConfig;\n }\n\n geminiTools = geminiTools !== undefined ? geminiTools : this.#opts.geminiTools;\n\n return new LLMStream(this, {\n client: this.#client,\n model: this.#opts.model,\n chatCtx,\n toolCtx,\n connOptions,\n geminiTools,\n extraKwargs: extras,\n });\n }\n}\n\nconst BLOCKED_REASONS = [\n 'SAFETY',\n 'SPII',\n 'PROHIBITED_CONTENT',\n 'BLOCKLIST',\n 'LANGUAGE',\n 'RECITATION',\n];\n\nexport class LLMStream extends llm.LLMStream {\n #client: GoogleGenAI;\n #model: string;\n #geminiTools?: LLMTools;\n #extraKwargs: GenerateContentConfig;\n\n constructor(\n llm: LLM,\n {\n client,\n model,\n chatCtx,\n toolCtx,\n connOptions,\n geminiTools,\n extraKwargs,\n }: {\n client: GoogleGenAI;\n model: string;\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions: APIConnectOptions;\n geminiTools?: LLMTools;\n extraKwargs: GenerateContentConfig;\n },\n ) {\n // Call base constructor with dev 1.0 object parameter pattern\n super(llm, { chatCtx, toolCtx, connOptions });\n this.#client = client;\n this.#model = model;\n this.#geminiTools = geminiTools;\n this.#extraKwargs = extraKwargs;\n }\n\n protected async run(): Promise<void> {\n let retryable = true;\n const requestId = `google_${Date.now()}`;\n\n try {\n const [turns, extraData] = (await this.chatCtx.toProviderFormat('google')) as [\n Record<string, unknown>[],\n GoogleFormatData,\n ];\n\n const contents: types.Content[] = turns.map((turn: Record<string, unknown>) => ({\n role: turn.role as types.Content['role'],\n parts: turn.parts as types.Part[],\n }));\n\n const functionDeclarations = this.toolCtx ? toFunctionDeclarations(this.toolCtx) : undefined;\n const tools =\n functionDeclarations && functionDeclarations.length > 0\n ? [{ functionDeclarations }]\n : undefined;\n\n let systemInstruction: types.Content | undefined = undefined;\n if (extraData.systemMessages && extraData.systemMessages.length > 0) {\n systemInstruction = {\n parts: extraData.systemMessages.map((content: string) => ({ text: content })),\n };\n }\n\n const response = await this.#client.models.generateContentStream({\n model: this.#model,\n contents,\n config: {\n ...this.#extraKwargs,\n systemInstruction,\n httpOptions: this.#extraKwargs.httpOptions ?? {\n timeout: Math.floor(this.connOptions.timeoutMs),\n },\n tools,\n },\n });\n\n for await (const chunk of response) {\n if (chunk.promptFeedback) {\n throw new APIStatusError({\n message: `Prompt feedback error: ${JSON.stringify(chunk.promptFeedback)}`,\n options: {\n retryable: false,\n requestId,\n },\n });\n }\n\n // Check for blocked reasons first — safety-blocked responses often lack content.parts,\n // so this must run before the no-content guard to avoid wasting retries.\n if (\n chunk.candidates?.[0]?.finishReason &&\n BLOCKED_REASONS.includes(chunk.candidates[0].finishReason)\n ) {\n throw new APIStatusError({\n message: `Google LLM: generation blocked - ${chunk.candidates[0].finishReason}`,\n options: {\n retryable: false,\n requestId,\n },\n });\n }\n\n if (!chunk.candidates || !chunk.candidates[0]?.content?.parts) {\n this.logger.warn(`No content in the response: ${JSON.stringify(chunk)}`);\n if (retryable) {\n throw new APIStatusError({\n message: 'Google LLM: no content in the response',\n options: {\n retryable: true,\n requestId,\n },\n });\n }\n continue;\n }\n\n if (chunk.candidates.length > 1) {\n this.logger.warn(\n 'Google LLM: there are multiple candidates in the response, returning response from the first one.',\n );\n }\n\n const candidate = chunk.candidates[0];\n const finishReason = candidate.finishReason;\n\n let chunksYielded = false;\n for (const part of candidate.content!.parts!) {\n const chatChunk = this.#parsePart(requestId, part);\n if (chatChunk) {\n chunksYielded = true;\n retryable = false;\n this.queue.put(chatChunk);\n }\n }\n\n if (finishReason === 'STOP' && !chunksYielded && retryable) {\n throw new APIStatusError({\n message: 'Google LLM: no response generated',\n options: {\n retryable,\n requestId,\n },\n });\n }\n\n if (chunk.usageMetadata) {\n const usage = chunk.usageMetadata;\n this.queue.put({\n id: requestId,\n usage: {\n completionTokens: usage.candidatesTokenCount || 0,\n promptTokens: usage.promptTokenCount || 0,\n promptCachedTokens: usage.cachedContentTokenCount || 0,\n totalTokens: usage.totalTokenCount || 0,\n },\n });\n }\n }\n } catch (error: unknown) {\n if (error instanceof APIStatusError || error instanceof APIConnectionError) {\n throw error;\n }\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: `Google LLM: 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: `Google LLM: 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: `Google LLM: Server error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable,\n },\n });\n }\n\n throw new APIConnectionError({\n message: `Google LLM: API error - ${err.message || 'Unknown error'}`,\n options: {\n retryable,\n },\n });\n }\n }\n\n #parsePart(id: string, part: types.Part): llm.ChatChunk | null {\n if (part.functionCall) {\n return {\n id,\n delta: {\n role: 'assistant',\n toolCalls: [\n llm.FunctionCall.create({\n callId: part.functionCall.id || shortuuid('function_call_'),\n name: part.functionCall.name!,\n args: JSON.stringify(part.functionCall.args!),\n // Preserve thought signature for Gemini 3+ thinking mode\n thoughtSignature: part.thoughtSignature,\n }),\n ],\n },\n };\n }\n\n if (!part.text) {\n return null;\n }\n\n return {\n id,\n delta: {\n content: part.text,\n role: 'assistant',\n },\n };\n }\n}\n"],"mappings":"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","names":["llm"]}
1
+ {"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type * as types from '@google/genai';\nimport { FunctionCallingConfigMode, type GenerateContentConfig, GoogleGenAI } from '@google/genai';\nimport type { APIConnectOptions } from '@livekit/agents';\nimport {\n APIConnectionError,\n APIStatusError,\n DEFAULT_API_CONNECT_OPTIONS,\n llm,\n shortuuid,\n} from '@livekit/agents';\nimport type { ChatModels } from './models.js';\nimport type { LLMTools } from './tools.js';\nimport { toFunctionDeclarations } from './utils.js';\n\ninterface GoogleFormatData {\n systemMessages: string[] | null;\n}\n\nexport interface LLMOptions {\n model: string | ChatModels;\n apiKey?: string;\n temperature?: number;\n toolChoice?: llm.ToolChoice;\n vertexai?: boolean;\n project?: string;\n location?: string;\n maxOutputTokens?: number;\n topP?: number;\n topK?: number;\n presencePenalty?: number;\n frequencyPenalty?: number;\n thinkingConfig?: types.ThinkingConfig;\n automaticFunctionCallingConfig?: types.AutomaticFunctionCallingConfig;\n geminiTools?: LLMTools;\n httpOptions?: types.HttpOptions;\n seed?: number;\n}\n\nexport class LLM extends llm.LLM {\n #opts: LLMOptions;\n #client: GoogleGenAI;\n\n label(): string {\n return 'google.LLM';\n }\n\n get model(): string {\n return this.#opts.model;\n }\n\n get provider(): string {\n if (this.#opts.vertexai) {\n return 'Vertex AI';\n }\n return 'Gemini';\n }\n\n /**\n * Create a new instance of Google GenAI LLM.\n *\n * Environment Requirements:\n * - For VertexAI: Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the path of the service account key file or use any of the other Google Cloud auth methods.\n * The Google Cloud project and location can be set via `project` and `location` arguments or the environment variables\n * `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION`. By default, the project is inferred from the service account key file,\n * and the location defaults to \"us-central1\".\n * - For Google Gemini API: Set the `apiKey` argument or the `GOOGLE_API_KEY` environment variable.\n *\n * @param model - The model name to use. Defaults to \"gemini-2.0-flash-001\".\n * @param apiKey - The API key for Google Gemini. If not provided, it attempts to read from the `GOOGLE_API_KEY` environment variable.\n * @param vertexai - Whether to use VertexAI. If not provided, it attempts to read from the `GOOGLE_GENAI_USE_VERTEXAI` environment variable. Defaults to false.\n * @param project - The Google Cloud project to use (only for VertexAI). Defaults to undefined.\n * @param location - The location to use for VertexAI API requests. Default value is \"us-central1\".\n * @param temperature - Sampling temperature for response generation. Defaults to undefined.\n * @param maxOutputTokens - Maximum number of tokens to generate in the output. Defaults to undefined.\n * @param topP - The nucleus sampling probability for response generation. Defaults to undefined.\n * @param topK - The top-k sampling value for response generation. Defaults to undefined.\n * @param presencePenalty - Penalizes the model for generating previously mentioned concepts. Defaults to undefined.\n * @param frequencyPenalty - Penalizes the model for repeating words. Defaults to undefined.\n * @param toolChoice - Specifies whether to use tools during response generation. Defaults to \"auto\".\n * @param thinkingConfig - The thinking configuration for response generation. Defaults to undefined.\n * @param automaticFunctionCallingConfig - The automatic function calling configuration for response generation. Defaults to undefined.\n * @param geminiTools - The Gemini-specific tools to use for the session.\n * @param httpOptions - The HTTP options to use for the session.\n * @param seed - Random seed for reproducible results. Defaults to undefined.\n */\n constructor(\n {\n model,\n apiKey,\n vertexai,\n project,\n location,\n temperature,\n maxOutputTokens,\n topP,\n topK,\n presencePenalty,\n frequencyPenalty,\n toolChoice,\n thinkingConfig,\n automaticFunctionCallingConfig,\n geminiTools,\n httpOptions,\n seed,\n }: LLMOptions = {\n model: 'gemini-2.0-flash-001',\n },\n ) {\n super();\n\n const useVertexAI =\n vertexai ??\n (process.env.GOOGLE_GENAI_USE_VERTEXAI === 'true' ||\n process.env.GOOGLE_GENAI_USE_VERTEXAI === '1');\n\n let gcpProject: string | undefined = project ?? process.env.GOOGLE_CLOUD_PROJECT;\n let gcpLocation: string | undefined = location ?? process.env.GOOGLE_CLOUD_LOCATION;\n let geminiApiKey: string | undefined = apiKey ?? process.env.GOOGLE_API_KEY;\n\n if (useVertexAI) {\n if (!gcpProject) {\n // TODO(brian): use default_async to get the project ID\n throw new Error(\n 'Project ID is required for Vertex AI. Set via project option or GOOGLE_CLOUD_PROJECT environment variable',\n );\n }\n geminiApiKey = undefined;\n } else {\n gcpProject = undefined;\n gcpLocation = undefined;\n if (!geminiApiKey) {\n throw new Error(\n 'API key is required for Google API either via apiKey or GOOGLE_API_KEY environment variable',\n );\n }\n }\n\n // Validate thinkingConfig\n if (thinkingConfig?.thinkingBudget !== undefined) {\n const budget = thinkingConfig.thinkingBudget;\n if (budget < 0 || budget > 24576) {\n throw new Error('thinkingBudget inside thinkingConfig must be between 0 and 24576');\n }\n }\n\n const clientOptions: types.GoogleGenAIOptions = useVertexAI\n ? {\n vertexai: true,\n project: gcpProject,\n location: gcpLocation,\n }\n : {\n apiKey: geminiApiKey,\n };\n\n this.#client = new GoogleGenAI(clientOptions);\n\n this.#opts = {\n model,\n vertexai: useVertexAI,\n project: gcpProject,\n location: gcpLocation,\n temperature,\n maxOutputTokens,\n topP,\n topK,\n presencePenalty,\n frequencyPenalty,\n toolChoice,\n thinkingConfig,\n automaticFunctionCallingConfig,\n geminiTools,\n httpOptions,\n seed,\n apiKey,\n };\n }\n\n chat({\n chatCtx,\n toolCtx,\n connOptions = DEFAULT_API_CONNECT_OPTIONS,\n toolChoice,\n extraKwargs,\n geminiTools,\n }: {\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions?: APIConnectOptions;\n parallelToolCalls?: boolean;\n toolChoice?: llm.ToolChoice;\n extraKwargs?: Record<string, unknown>;\n geminiTools?: LLMTools;\n }): LLMStream {\n const extras: GenerateContentConfig = { ...extraKwargs } as GenerateContentConfig;\n\n toolChoice = toolChoice !== undefined ? toolChoice : this.#opts.toolChoice;\n\n if (toolChoice) {\n let geminiToolConfig: types.ToolConfig;\n\n if (typeof toolChoice === 'object' && toolChoice.type === 'function') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.ANY,\n allowedFunctionNames: [toolChoice.function.name],\n },\n };\n } else if (toolChoice === 'required') {\n const toolNames = Object.entries(toolCtx || {}).map(([name]) => name);\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.ANY,\n allowedFunctionNames: toolNames.length > 0 ? toolNames : undefined,\n },\n };\n } else if (toolChoice === 'auto') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.AUTO,\n },\n };\n } else if (toolChoice === 'none') {\n geminiToolConfig = {\n functionCallingConfig: {\n mode: FunctionCallingConfigMode.NONE,\n },\n };\n } else {\n throw new Error(`Invalid tool choice: ${toolChoice}`);\n }\n\n extras.toolConfig = geminiToolConfig;\n }\n\n if (this.#opts.temperature !== undefined) {\n extras.temperature = this.#opts.temperature;\n }\n if (this.#opts.maxOutputTokens !== undefined) {\n extras.maxOutputTokens = this.#opts.maxOutputTokens;\n }\n if (this.#opts.topP !== undefined) {\n extras.topP = this.#opts.topP;\n }\n if (this.#opts.topK !== undefined) {\n extras.topK = this.#opts.topK;\n }\n if (this.#opts.presencePenalty !== undefined) {\n extras.presencePenalty = this.#opts.presencePenalty;\n }\n if (this.#opts.frequencyPenalty !== undefined) {\n extras.frequencyPenalty = this.#opts.frequencyPenalty;\n }\n if (this.#opts.seed !== undefined) {\n extras.seed = this.#opts.seed;\n }\n\n if (this.#opts.thinkingConfig !== undefined) {\n extras.thinkingConfig = this.#opts.thinkingConfig;\n }\n\n if (this.#opts.automaticFunctionCallingConfig !== undefined) {\n extras.automaticFunctionCalling = this.#opts.automaticFunctionCallingConfig;\n }\n\n geminiTools = geminiTools !== undefined ? geminiTools : this.#opts.geminiTools;\n\n return new LLMStream(this, {\n client: this.#client,\n model: this.#opts.model,\n chatCtx,\n toolCtx,\n connOptions,\n geminiTools,\n extraKwargs: extras,\n });\n }\n}\n\nconst BLOCKED_REASONS = [\n 'SAFETY',\n 'SPII',\n 'PROHIBITED_CONTENT',\n 'BLOCKLIST',\n 'LANGUAGE',\n 'RECITATION',\n];\n\nexport class LLMStream extends llm.LLMStream {\n #client: GoogleGenAI;\n #model: string;\n #geminiTools?: LLMTools;\n #extraKwargs: GenerateContentConfig;\n\n constructor(\n llm: LLM,\n {\n client,\n model,\n chatCtx,\n toolCtx,\n connOptions,\n geminiTools,\n extraKwargs,\n }: {\n client: GoogleGenAI;\n model: string;\n chatCtx: llm.ChatContext;\n toolCtx?: llm.ToolContext;\n connOptions: APIConnectOptions;\n geminiTools?: LLMTools;\n extraKwargs: GenerateContentConfig;\n },\n ) {\n // Call base constructor with dev 1.0 object parameter pattern\n super(llm, { chatCtx, toolCtx, connOptions });\n this.#client = client;\n this.#model = model;\n this.#geminiTools = geminiTools;\n this.#extraKwargs = extraKwargs;\n }\n\n protected async run(): Promise<void> {\n let retryable = true;\n const requestId = `google_${Date.now()}`;\n\n try {\n const [turns, extraData] = (await this.chatCtx.toProviderFormat('google')) as [\n Record<string, unknown>[],\n GoogleFormatData,\n ];\n\n const contents: types.Content[] = turns.map((turn: Record<string, unknown>) => ({\n role: turn.role as types.Content['role'],\n parts: turn.parts as types.Part[],\n }));\n\n const functionDeclarations = this.toolCtx ? toFunctionDeclarations(this.toolCtx) : undefined;\n const tools =\n functionDeclarations && functionDeclarations.length > 0\n ? [{ functionDeclarations }]\n : undefined;\n\n let systemInstruction: types.Content | undefined = undefined;\n if (extraData.systemMessages && extraData.systemMessages.length > 0) {\n systemInstruction = {\n parts: extraData.systemMessages.map((content: string) => ({ text: content })),\n };\n }\n\n const response = await this.#client.models.generateContentStream({\n model: this.#model,\n contents,\n config: {\n ...this.#extraKwargs,\n systemInstruction,\n httpOptions: this.#extraKwargs.httpOptions ?? {\n timeout: Math.floor(this.connOptions.timeoutMs),\n },\n tools,\n },\n });\n\n for await (const chunk of response) {\n if (chunk.promptFeedback) {\n throw new APIStatusError({\n message: `Prompt feedback error: ${JSON.stringify(chunk.promptFeedback)}`,\n options: {\n retryable: false,\n requestId,\n },\n });\n }\n\n // Check for blocked reasons first — safety-blocked responses often lack content.parts,\n // so this must run before the no-content guard to avoid wasting retries.\n if (\n chunk.candidates?.[0]?.finishReason &&\n BLOCKED_REASONS.includes(chunk.candidates[0].finishReason)\n ) {\n throw new APIStatusError({\n message: `Google LLM: generation blocked - ${chunk.candidates[0].finishReason}`,\n options: {\n retryable: false,\n requestId,\n },\n });\n }\n\n if (!chunk.candidates || !chunk.candidates[0]?.content?.parts) {\n this.logger.warn(`No content in the response: ${JSON.stringify(chunk)}`);\n if (retryable) {\n throw new APIStatusError({\n message: 'Google LLM: no content in the response',\n options: {\n retryable: true,\n requestId,\n },\n });\n }\n continue;\n }\n\n if (chunk.candidates.length > 1) {\n this.logger.warn(\n 'Google LLM: there are multiple candidates in the response, returning response from the first one.',\n );\n }\n\n const candidate = chunk.candidates[0];\n const finishReason = candidate.finishReason;\n\n let chunksYielded = false;\n for (const part of candidate.content!.parts!) {\n const chatChunk = this.#parsePart(requestId, part);\n if (chatChunk) {\n chunksYielded = true;\n retryable = false;\n this.queue.put(chatChunk);\n }\n }\n\n if (finishReason === 'STOP' && !chunksYielded && retryable) {\n throw new APIStatusError({\n message: 'Google LLM: no response generated',\n options: {\n retryable,\n requestId,\n },\n });\n }\n\n if (chunk.usageMetadata) {\n const usage = chunk.usageMetadata;\n this.queue.put({\n id: requestId,\n usage: {\n completionTokens: usage.candidatesTokenCount || 0,\n promptTokens: usage.promptTokenCount || 0,\n promptCachedTokens: usage.cachedContentTokenCount || 0,\n totalTokens: usage.totalTokenCount || 0,\n },\n });\n }\n }\n } catch (error: unknown) {\n if (error instanceof APIStatusError || error instanceof APIConnectionError) {\n throw error;\n }\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: `Google LLM: 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: `Google LLM: 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: `Google LLM: Server error (${err.code}) - ${err.message || 'Unknown error'}`,\n options: {\n statusCode: err.code,\n retryable,\n },\n });\n }\n\n throw new APIConnectionError({\n message: `Google LLM: API error - ${err.message || 'Unknown error'}`,\n options: {\n retryable,\n },\n });\n }\n }\n\n #parsePart(id: string, part: types.Part): llm.ChatChunk | null {\n if (part.functionCall) {\n return {\n id,\n delta: {\n role: 'assistant',\n toolCalls: [\n llm.FunctionCall.create({\n callId: part.functionCall.id || shortuuid('function_call_'),\n name: part.functionCall.name!,\n args: JSON.stringify(part.functionCall.args!),\n // Preserve thought signature for Gemini 3+ thinking mode\n thoughtSignature: part.thoughtSignature,\n }),\n ],\n },\n };\n }\n\n if (!part.text) {\n return null;\n }\n\n return {\n id,\n delta: {\n content: part.text,\n role: 'assistant',\n },\n };\n }\n}\n"],"mappings":"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","names":["llm"]}
package/dist/llm.test.cjs CHANGED
@@ -2,13 +2,21 @@
2
2
  var import_agents_plugins_test = require("@livekit/agents-plugins-test");
3
3
  var import_vitest = require("vitest");
4
4
  var import_llm = require("./llm.cjs");
5
- (0, import_vitest.describe)("Google", async () => {
6
- await (0, import_agents_plugins_test.llm)(
7
- new import_llm.LLM({
8
- model: "gemini-2.5-flash",
9
- temperature: 0
10
- }),
11
- true
12
- );
13
- });
5
+ const hasGoogleApiKey = Boolean(process.env.GOOGLE_API_KEY);
6
+ if (hasGoogleApiKey) {
7
+ (0, import_vitest.describe)("Google", async () => {
8
+ await (0, import_agents_plugins_test.llm)(
9
+ new import_llm.LLM({
10
+ model: "gemini-2.5-flash",
11
+ temperature: 0
12
+ }),
13
+ true
14
+ );
15
+ });
16
+ } else {
17
+ (0, import_vitest.describe)("Google", () => {
18
+ import_vitest.it.skip("requires GOOGLE_API_KEY", () => {
19
+ });
20
+ });
21
+ }
14
22
  //# sourceMappingURL=llm.test.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { LLM } from './llm.js';\n\ndescribe('Google', async () => {\n await llm(\n new LLM({\n model: 'gemini-2.5-flash',\n temperature: 0,\n }),\n true,\n );\n});\n"],"mappings":";AAGA,iCAAoB;AACpB,oBAAyB;AACzB,iBAAoB;AAAA,IAEpB,wBAAS,UAAU,YAAY;AAC7B,YAAM;AAAA,IACJ,IAAI,eAAI;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IACD;AAAA,EACF;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { LLM } from './llm.js';\n\nconst hasGoogleApiKey = Boolean(process.env.GOOGLE_API_KEY);\n\nif (hasGoogleApiKey) {\n describe('Google', async () => {\n await llm(\n new LLM({\n model: 'gemini-2.5-flash',\n temperature: 0,\n }),\n true,\n );\n });\n} else {\n describe('Google', () => {\n it.skip('requires GOOGLE_API_KEY', () => {});\n });\n}\n"],"mappings":";AAGA,iCAAoB;AACpB,oBAA6B;AAC7B,iBAAoB;AAEpB,MAAM,kBAAkB,QAAQ,QAAQ,IAAI,cAAc;AAE1D,IAAI,iBAAiB;AACnB,8BAAS,UAAU,YAAY;AAC7B,cAAM;AAAA,MACJ,IAAI,eAAI;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,MACf,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF,CAAC;AACH,OAAO;AACL,8BAAS,UAAU,MAAM;AACvB,qBAAG,KAAK,2BAA2B,MAAM;AAAA,IAAC,CAAC;AAAA,EAC7C,CAAC;AACH;","names":[]}
package/dist/llm.test.js CHANGED
@@ -1,13 +1,21 @@
1
1
  import { llm } from "@livekit/agents-plugins-test";
2
- import { describe } from "vitest";
2
+ import { describe, it } from "vitest";
3
3
  import { LLM } from "./llm.js";
4
- describe("Google", async () => {
5
- await llm(
6
- new LLM({
7
- model: "gemini-2.5-flash",
8
- temperature: 0
9
- }),
10
- true
11
- );
12
- });
4
+ const hasGoogleApiKey = Boolean(process.env.GOOGLE_API_KEY);
5
+ if (hasGoogleApiKey) {
6
+ describe("Google", async () => {
7
+ await llm(
8
+ new LLM({
9
+ model: "gemini-2.5-flash",
10
+ temperature: 0
11
+ }),
12
+ true
13
+ );
14
+ });
15
+ } else {
16
+ describe("Google", () => {
17
+ it.skip("requires GOOGLE_API_KEY", () => {
18
+ });
19
+ });
20
+ }
13
21
  //# sourceMappingURL=llm.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm } from '@livekit/agents-plugins-test';\nimport { describe } from 'vitest';\nimport { LLM } from './llm.js';\n\ndescribe('Google', async () => {\n await llm(\n new LLM({\n model: 'gemini-2.5-flash',\n temperature: 0,\n }),\n true,\n );\n});\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,gBAAgB;AACzB,SAAS,WAAW;AAEpB,SAAS,UAAU,YAAY;AAC7B,QAAM;AAAA,IACJ,IAAI,IAAI;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IACf,CAAC;AAAA,IACD;AAAA,EACF;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/llm.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { llm } from '@livekit/agents-plugins-test';\nimport { describe, it } from 'vitest';\nimport { LLM } from './llm.js';\n\nconst hasGoogleApiKey = Boolean(process.env.GOOGLE_API_KEY);\n\nif (hasGoogleApiKey) {\n describe('Google', async () => {\n await llm(\n new LLM({\n model: 'gemini-2.5-flash',\n temperature: 0,\n }),\n true,\n );\n });\n} else {\n describe('Google', () => {\n it.skip('requires GOOGLE_API_KEY', () => {});\n });\n}\n"],"mappings":"AAGA,SAAS,WAAW;AACpB,SAAS,UAAU,UAAU;AAC7B,SAAS,WAAW;AAEpB,MAAM,kBAAkB,QAAQ,QAAQ,IAAI,cAAc;AAE1D,IAAI,iBAAiB;AACnB,WAAS,UAAU,YAAY;AAC7B,UAAM;AAAA,MACJ,IAAI,IAAI;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,MACf,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF,CAAC;AACH,OAAO;AACL,WAAS,UAAU,MAAM;AACvB,OAAG,KAAK,2BAA2B,MAAM;AAAA,IAAC,CAAC;AAAA,EAC7C,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents-plugin-google",
3
- "version": "1.0.50",
3
+ "version": "1.1.0",
4
4
  "description": "Google Gemini plugin for LiveKit Node Agents",
5
5
  "main": "dist/index.js",
6
6
  "require": "dist/index.cjs",
@@ -29,19 +29,19 @@
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.50",
33
- "@livekit/agents-plugin-openai": "1.0.50",
34
- "@livekit/agents-plugins-test": "1.0.50"
32
+ "@livekit/agents": "1.1.0",
33
+ "@livekit/agents-plugin-openai": "1.1.0",
34
+ "@livekit/agents-plugins-test": "1.1.0"
35
35
  },
36
36
  "dependencies": {
37
- "@google/genai": "^1.34.0",
37
+ "@google/genai": "^1.44.0",
38
38
  "@livekit/mutex": "^1.1.1",
39
39
  "@types/json-schema": "^7.0.15",
40
40
  "json-schema": "^0.4.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@livekit/rtc-node": "^0.13.24",
44
- "@livekit/agents": "1.0.50"
44
+ "@livekit/agents": "1.1.0"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "tsup --onSuccess \"pnpm build:types\"",
@@ -26,6 +26,7 @@ import {
26
26
  delay,
27
27
  llm,
28
28
  log,
29
+ normalizeLanguage,
29
30
  shortuuid,
30
31
  stream,
31
32
  } from '@livekit/agents';
@@ -327,7 +328,7 @@ export class RealtimeModel extends llm.RealtimeModel {
327
328
  model: options.model || defaultModel,
328
329
  apiKey,
329
330
  voice: options.voice || 'Puck',
330
- language: options.language,
331
+ language: options.language ? normalizeLanguage(options.language) : undefined,
331
332
  responseModalities: options.modalities || [Modality.AUDIO],
332
333
  vertexai,
333
334
  project,
@@ -416,6 +417,8 @@ export class RealtimeSession extends llm.RealtimeSession {
416
417
  private hasReceivedAudioInput = false;
417
418
  private pendingInterruptText = false;
418
419
  private earlyCompletionPending = false;
420
+ private pendingToolCallIds = new Set<string>();
421
+ private generationPendingTurnComplete?: ResponseGeneration;
419
422
 
420
423
  #client: GoogleGenAI;
421
424
  #task: Promise<void>;
@@ -477,6 +480,11 @@ export class RealtimeSession extends llm.RealtimeSession {
477
480
  this.earlyCompletionPending = false;
478
481
  this.pendingInterruptText = false;
479
482
 
483
+ this.pendingToolCallIds.clear();
484
+ if (this.generationPendingTurnComplete) {
485
+ this.markCurrentGenerationDone(false, this.generationPendingTurnComplete);
486
+ this.generationPendingTurnComplete = undefined;
487
+ }
480
488
  unlock();
481
489
  }
482
490
 
@@ -644,6 +652,8 @@ export class RealtimeSession extends llm.RealtimeSession {
644
652
  }
645
653
 
646
654
  pushAudio(frame: AudioFrame): void {
655
+ if (this.pendingToolCallIds.size > 0) return;
656
+
647
657
  // Track that we've received audio input
648
658
  this.hasReceivedAudioInput = true;
649
659
 
@@ -735,6 +745,8 @@ export class RealtimeSession extends llm.RealtimeSession {
735
745
  return;
736
746
  }
737
747
 
748
+ if (this.pendingToolCallIds.size > 0) return;
749
+
738
750
  if (!this.inUserActivity) {
739
751
  this.inUserActivity = true;
740
752
  this.sendClientEvent({
@@ -968,13 +980,20 @@ export class RealtimeSession extends llm.RealtimeSession {
968
980
  if (LK_GOOGLE_DEBUG) {
969
981
  this.#logger.debug(`(client) -> ${JSON.stringify(this.loggableClientEvent(msg))}`);
970
982
  }
971
- await session.sendToolResponse({
972
- functionResponses,
973
- });
983
+ try {
984
+ await session.sendToolResponse({
985
+ functionResponses,
986
+ });
987
+ } finally {
988
+ for (const fr of functionResponses) {
989
+ if (fr?.id) this.pendingToolCallIds.delete(fr.id);
990
+ }
991
+ }
974
992
  }
975
993
  break;
976
994
  case 'realtime_input':
977
995
  const { mediaChunks, activityStart, activityEnd, text } = msg.value;
996
+ if (this.pendingToolCallIds.size > 0) break;
978
997
  if (mediaChunks) {
979
998
  for (const mediaChunk of mediaChunks) {
980
999
  await session.sendRealtimeInput({ media: mediaChunk });
@@ -1164,21 +1183,25 @@ export class RealtimeSession extends llm.RealtimeSession {
1164
1183
  return obj;
1165
1184
  }
1166
1185
 
1167
- private markCurrentGenerationDone(keepFunctionChannelOpen: boolean = false): void {
1168
- if (!this.currentGeneration || this.currentGeneration._done) {
1186
+ private markCurrentGenerationDone(
1187
+ keepFunctionChannelOpen: boolean = false,
1188
+ gen?: ResponseGeneration,
1189
+ ): void {
1190
+ const target = gen ?? this.currentGeneration;
1191
+ if (!target || target._done) {
1169
1192
  return;
1170
1193
  }
1171
1194
 
1172
1195
  this.handleInputSpeechStopped();
1173
1196
 
1174
- const gen = this.currentGeneration;
1197
+ const targetGen = target;
1175
1198
 
1176
1199
  // The only way we'd know that the transcription is complete is by when they are
1177
1200
  // done with generation
1178
- if (gen.inputTranscription) {
1201
+ if (targetGen.inputTranscription) {
1179
1202
  this.emit('input_audio_transcription_completed', {
1180
- itemId: gen.inputId,
1181
- transcript: gen.inputTranscription,
1203
+ itemId: targetGen.inputId,
1204
+ transcript: targetGen.inputTranscription,
1182
1205
  isFinal: true,
1183
1206
  } as llm.InputTranscriptionCompleted);
1184
1207
 
@@ -1186,31 +1209,31 @@ export class RealtimeSession extends llm.RealtimeSession {
1186
1209
  // we would handle it manually here
1187
1210
  this._chatCtx.addMessage({
1188
1211
  role: 'user',
1189
- content: gen.inputTranscription,
1190
- id: gen.inputId,
1212
+ content: targetGen.inputTranscription,
1213
+ id: targetGen.inputId,
1191
1214
  });
1192
1215
  }
1193
1216
 
1194
- if (gen.outputText) {
1217
+ if (targetGen.outputText) {
1195
1218
  this._chatCtx.addMessage({
1196
1219
  role: 'assistant',
1197
- content: gen.outputText,
1198
- id: gen.responseId,
1220
+ content: targetGen.outputText,
1221
+ id: targetGen.responseId,
1199
1222
  });
1200
1223
  }
1201
1224
 
1202
1225
  if (this.options.outputAudioTranscription === undefined) {
1203
1226
  // close the text data of transcription synchronizer
1204
- gen.textChannel.write('');
1227
+ targetGen.textChannel.write('');
1205
1228
  }
1206
1229
 
1207
- gen.textChannel.close();
1208
- gen.audioChannel.close();
1230
+ targetGen.textChannel.close();
1231
+ targetGen.audioChannel.close();
1209
1232
  if (!keepFunctionChannelOpen) {
1210
- gen.functionChannel.close();
1233
+ targetGen.functionChannel.close();
1211
1234
  }
1212
- gen.messageChannel.close();
1213
- gen._done = true;
1235
+ targetGen.messageChannel.close();
1236
+ targetGen._done = true;
1214
1237
  }
1215
1238
 
1216
1239
  private emitError(error: Error, recoverable: boolean): void {
@@ -1289,14 +1312,21 @@ export class RealtimeSession extends llm.RealtimeSession {
1289
1312
  }
1290
1313
 
1291
1314
  private startNewGeneration(): void {
1315
+ const previousGen = this.currentGeneration;
1316
+ const previousHadOpenFunctionChannel = previousGen && !previousGen.functionChannel.closed;
1317
+
1292
1318
  // close functionChannel of previous generation if still open (no toolCall arrived)
1293
- if (this.currentGeneration && !this.currentGeneration.functionChannel.closed) {
1294
- this.currentGeneration.functionChannel.close();
1319
+ if (previousGen && previousHadOpenFunctionChannel) {
1320
+ previousGen.functionChannel.close();
1295
1321
  }
1296
1322
 
1297
- if (this.currentGeneration && !this.currentGeneration._done) {
1298
- this.#logger.warn('Starting new generation while another is active. Finalizing previous.');
1299
- this.markCurrentGenerationDone();
1323
+ if (previousGen && !previousGen._done) {
1324
+ if (previousHadOpenFunctionChannel) {
1325
+ this.generationPendingTurnComplete = previousGen;
1326
+ } else {
1327
+ this.#logger.warn('Starting new generation while another is active. Finalizing previous.');
1328
+ this.markCurrentGenerationDone();
1329
+ }
1300
1330
  }
1301
1331
 
1302
1332
  const responseId = shortuuid('GR_');
@@ -1451,7 +1481,12 @@ export class RealtimeSession extends llm.RealtimeSession {
1451
1481
  }
1452
1482
 
1453
1483
  if (serverContent.turnComplete && !this.earlyCompletionPending) {
1454
- this.markCurrentGenerationDone();
1484
+ if (this.generationPendingTurnComplete) {
1485
+ this.markCurrentGenerationDone(false, this.generationPendingTurnComplete);
1486
+ this.generationPendingTurnComplete = undefined;
1487
+ } else {
1488
+ this.markCurrentGenerationDone();
1489
+ }
1455
1490
  }
1456
1491
 
1457
1492
  // Assume Gemini emits turnComplete/generationComplete before any new generation content.
@@ -1482,17 +1517,16 @@ export class RealtimeSession extends llm.RealtimeSession {
1482
1517
  this.#logger.warn('received function call without name, skipping');
1483
1518
  continue;
1484
1519
  }
1520
+ const callId = fc.id || shortuuid('fnc-call-');
1521
+ this.pendingToolCallIds.add(callId);
1485
1522
  gen.functionChannel.write(
1486
1523
  llm.FunctionCall.create({
1487
- callId: fc.id || shortuuid('fnc-call-'),
1524
+ callId,
1488
1525
  name: fc.name,
1489
1526
  args: fc.args ? JSON.stringify(fc.args) : '',
1490
1527
  }),
1491
1528
  );
1492
1529
  }
1493
-
1494
- gen.functionChannel.close();
1495
- this.markCurrentGenerationDone();
1496
1530
  }
1497
1531
 
1498
1532
  private handleToolCallCancellation(cancellation: types.LiveServerToolCallCancellation): void {
@@ -1502,6 +1536,9 @@ export class RealtimeSession extends llm.RealtimeSession {
1502
1536
  },
1503
1537
  'server cancelled tool calls',
1504
1538
  );
1539
+ for (const id of cancellation.ids || []) {
1540
+ this.pendingToolCallIds.delete(id);
1541
+ }
1505
1542
  }
1506
1543
 
1507
1544
  private handleUsageMetadata(usage: types.UsageMetadata): void {