@livekit/agents-plugin-google 1.2.2 → 1.2.4
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/README.md +15 -1
- package/dist/beta/realtime/api_proto.cjs.map +1 -1
- package/dist/beta/realtime/api_proto.d.cts +1 -1
- package/dist/beta/realtime/api_proto.d.ts +1 -1
- package/dist/beta/realtime/api_proto.d.ts.map +1 -1
- package/dist/beta/realtime/realtime_api.cjs +47 -17
- package/dist/beta/realtime/realtime_api.cjs.map +1 -1
- package/dist/beta/realtime/realtime_api.d.cts +2 -2
- package/dist/beta/realtime/realtime_api.d.ts +2 -2
- package/dist/beta/realtime/realtime_api.d.ts.map +1 -1
- package/dist/beta/realtime/realtime_api.js +47 -17
- package/dist/beta/realtime/realtime_api.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/llm.cjs +1 -1
- package/dist/llm.cjs.map +1 -1
- package/dist/llm.d.cts +1 -1
- package/dist/llm.d.ts +1 -1
- package/dist/llm.js +1 -1
- package/dist/llm.js.map +1 -1
- package/package.json +7 -7
- package/src/beta/realtime/api_proto.ts +1 -0
- package/src/beta/realtime/realtime_api.ts +54 -18
- package/src/llm.ts +1 -1
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 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"]}
|
|
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. Gemini 3.1 models use `thinkingLevel`; Gemini 2.5 models use `thinkingBudget`. 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.d.cts
CHANGED
|
@@ -50,7 +50,7 @@ export declare class LLM extends llm.LLM {
|
|
|
50
50
|
* @param presencePenalty - Penalizes the model for generating previously mentioned concepts. Defaults to undefined.
|
|
51
51
|
* @param frequencyPenalty - Penalizes the model for repeating words. Defaults to undefined.
|
|
52
52
|
* @param toolChoice - Specifies whether to use tools during response generation. Defaults to "auto".
|
|
53
|
-
* @param thinkingConfig - The thinking configuration for response generation. Defaults to undefined.
|
|
53
|
+
* @param thinkingConfig - The thinking configuration for response generation. Gemini 3.1 models use `thinkingLevel`; Gemini 2.5 models use `thinkingBudget`. Defaults to undefined.
|
|
54
54
|
* @param automaticFunctionCallingConfig - The automatic function calling configuration for response generation. Defaults to undefined.
|
|
55
55
|
* @param geminiTools - The Gemini-specific tools to use for the session.
|
|
56
56
|
* @param httpOptions - The HTTP options to use for the session.
|
package/dist/llm.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export declare class LLM extends llm.LLM {
|
|
|
50
50
|
* @param presencePenalty - Penalizes the model for generating previously mentioned concepts. Defaults to undefined.
|
|
51
51
|
* @param frequencyPenalty - Penalizes the model for repeating words. Defaults to undefined.
|
|
52
52
|
* @param toolChoice - Specifies whether to use tools during response generation. Defaults to "auto".
|
|
53
|
-
* @param thinkingConfig - The thinking configuration for response generation. Defaults to undefined.
|
|
53
|
+
* @param thinkingConfig - The thinking configuration for response generation. Gemini 3.1 models use `thinkingLevel`; Gemini 2.5 models use `thinkingBudget`. Defaults to undefined.
|
|
54
54
|
* @param automaticFunctionCallingConfig - The automatic function calling configuration for response generation. Defaults to undefined.
|
|
55
55
|
* @param geminiTools - The Gemini-specific tools to use for the session.
|
|
56
56
|
* @param httpOptions - The HTTP options to use for the session.
|
package/dist/llm.js
CHANGED
|
@@ -44,7 +44,7 @@ class LLM extends llm.LLM {
|
|
|
44
44
|
* @param presencePenalty - Penalizes the model for generating previously mentioned concepts. Defaults to undefined.
|
|
45
45
|
* @param frequencyPenalty - Penalizes the model for repeating words. Defaults to undefined.
|
|
46
46
|
* @param toolChoice - Specifies whether to use tools during response generation. Defaults to "auto".
|
|
47
|
-
* @param thinkingConfig - The thinking configuration for response generation. Defaults to undefined.
|
|
47
|
+
* @param thinkingConfig - The thinking configuration for response generation. Gemini 3.1 models use `thinkingLevel`; Gemini 2.5 models use `thinkingBudget`. Defaults to undefined.
|
|
48
48
|
* @param automaticFunctionCallingConfig - The automatic function calling configuration for response generation. Defaults to undefined.
|
|
49
49
|
* @param geminiTools - The Gemini-specific tools to use for the session.
|
|
50
50
|
* @param httpOptions - The HTTP options to use for the session.
|
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 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"]}
|
|
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. Gemini 3.1 models use `thinkingLevel`; Gemini 2.5 models use `thinkingBudget`. 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":"AAIA,SAAS,2BAAuD,mBAAmB;AAEnF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,8BAA8B;AA0BhC,MAAM,YAAY,IAAI,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,YAAY,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,0BAA0B;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,0BAA0B;AAAA,YAChC,sBAAsB,UAAU,SAAS,IAAI,YAAY;AAAA,UAC3D;AAAA,QACF;AAAA,MACF,WAAW,eAAe,QAAQ;AAChC,2BAAmB;AAAA,UACjB,uBAAuB;AAAA,YACrB,MAAM,0BAA0B;AAAA,UAClC;AAAA,QACF;AAAA,MACF,WAAW,eAAe,QAAQ;AAChC,2BAAmB;AAAA,UACjB,uBAAuB;AAAA,YACrB,MAAM,0BAA0B;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,IAAI,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,UAAU,uBAAuB,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,eAAe;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,eAAe;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,eAAe;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,eAAe;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,kBAAkB,iBAAiB,oBAAoB;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,eAAe;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,eAAe;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,eAAe;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,mBAAmB;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,IAAI,aAAa,OAAO;AAAA,cACtB,QAAQ,KAAK,aAAa,MAAM,UAAU,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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livekit/agents-plugin-google",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Google Gemini plugin for LiveKit Node Agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"require": "dist/index.cjs",
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"README.md"
|
|
26
26
|
],
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@livekit/rtc-node": "^0.13.
|
|
28
|
+
"@livekit/rtc-node": "^0.13.25",
|
|
29
29
|
"@microsoft/api-extractor": "^7.35.0",
|
|
30
30
|
"tsup": "^8.3.5",
|
|
31
31
|
"typescript": "^5.0.0",
|
|
32
|
-
"@livekit/agents": "1.2.
|
|
33
|
-
"@livekit/agents-plugin-openai": "1.2.
|
|
34
|
-
"@livekit/agents-plugins-test": "1.2.
|
|
32
|
+
"@livekit/agents": "1.2.4",
|
|
33
|
+
"@livekit/agents-plugin-openai": "1.2.4",
|
|
34
|
+
"@livekit/agents-plugins-test": "1.2.4"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@google/genai": "^1.44.0",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"json-schema": "^0.4.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@livekit/rtc-node": "^0.13.
|
|
44
|
-
"@livekit/agents": "1.2.
|
|
43
|
+
"@livekit/rtc-node": "^0.13.25",
|
|
44
|
+
"@livekit/agents": "1.2.4"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsup --onSuccess \"pnpm build:types\"",
|
|
@@ -19,6 +19,7 @@ export type LiveAPIModels =
|
|
|
19
19
|
| 'gemini-live-2.5-flash-preview-native-audio-09-2025' // Public preview https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-5-flash-live-api#live-2.5-flash-preview
|
|
20
20
|
| 'gemini-live-2.5-flash-preview-native-audio' // still works, possibly an alias, but not mentioned in any docs or changelog
|
|
21
21
|
// Gemini API models
|
|
22
|
+
| 'gemini-3.1-flash-live-preview' // https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-live-preview
|
|
22
23
|
| 'gemini-2.5-flash-native-audio-preview-12-2025' // https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-live
|
|
23
24
|
| 'gemini-2.5-flash-native-audio-preview-09-2025' // https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-live
|
|
24
25
|
| 'gemini-2.0-flash-exp'; // still works in Gemini API but not VertexAI
|
|
@@ -288,8 +288,8 @@ export class RealtimeModel extends llm.RealtimeModel {
|
|
|
288
288
|
/**
|
|
289
289
|
* Thinking configuration for native audio models.
|
|
290
290
|
* If not set, the model's default thinking behavior is used.
|
|
291
|
-
*
|
|
292
|
-
*
|
|
291
|
+
* Gemini 3.1 live models use `thinkingLevel`.
|
|
292
|
+
* Gemini 2.5 live models use `thinkingBudget`.
|
|
293
293
|
*/
|
|
294
294
|
thinkingConfig?: types.ThinkingConfig;
|
|
295
295
|
} = {},
|
|
@@ -543,6 +543,13 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
543
543
|
}
|
|
544
544
|
|
|
545
545
|
async updateInstructions(instructions: string): Promise<void> {
|
|
546
|
+
if (this.options.model === 'gemini-3.1-flash-live-preview') {
|
|
547
|
+
this.#logger.warn(
|
|
548
|
+
'updateInstructions is not compatible with gemini-3.1-flash-live-preview and will be ignored.',
|
|
549
|
+
);
|
|
550
|
+
this.options.instructions = instructions;
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
546
553
|
if (this.options.instructions === undefined || this.options.instructions !== instructions) {
|
|
547
554
|
this.options.instructions = instructions;
|
|
548
555
|
this.markRestartNeeded();
|
|
@@ -550,6 +557,13 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
550
557
|
}
|
|
551
558
|
|
|
552
559
|
async updateChatCtx(chatCtx: llm.ChatContext): Promise<void> {
|
|
560
|
+
if (this.options.model === 'gemini-3.1-flash-live-preview') {
|
|
561
|
+
this.#logger.warn(
|
|
562
|
+
'updateChatCtx is not compatible with gemini-3.1-flash-live-preview and will be ignored.',
|
|
563
|
+
);
|
|
564
|
+
this._chatCtx = chatCtx.copy();
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
553
567
|
const unlock = await this.sessionLock.lock();
|
|
554
568
|
try {
|
|
555
569
|
if (!this.activeSession) {
|
|
@@ -660,12 +674,10 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
660
674
|
for (const f of this.resampleAudio(frame)) {
|
|
661
675
|
for (const nf of this.bstream.write(f.data.buffer as ArrayBuffer)) {
|
|
662
676
|
const realtimeInput: types.LiveClientRealtimeInput = {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
},
|
|
668
|
-
],
|
|
677
|
+
audio: {
|
|
678
|
+
mimeType: 'audio/pcm',
|
|
679
|
+
data: Buffer.from(nf.data.buffer).toString('base64'),
|
|
680
|
+
},
|
|
669
681
|
};
|
|
670
682
|
this.sendClientEvent({
|
|
671
683
|
type: 'realtime_input',
|
|
@@ -684,6 +696,15 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
684
696
|
}
|
|
685
697
|
|
|
686
698
|
async generateReply(instructions?: string): Promise<llm.GenerationCreatedEvent> {
|
|
699
|
+
if (this.options.model === 'gemini-3.1-flash-live-preview') {
|
|
700
|
+
this.#logger.warn(
|
|
701
|
+
'generateReply is not compatible with gemini-3.1-flash-live-preview. Use a Gemini 2.5 live model for voice-agent flows that require programmatic reply generation.',
|
|
702
|
+
);
|
|
703
|
+
throw new Error(
|
|
704
|
+
"generateReply is not compatible with 'gemini-3.1-flash-live-preview'; use a Gemini 2.5 live model for voice-agent flows that require programmatic reply generation.",
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
|
|
687
708
|
if (this.pendingGenerationFut && !this.pendingGenerationFut.done) {
|
|
688
709
|
this.#logger.warn(
|
|
689
710
|
'generateReply called while another generation is pending, cancelling previous.',
|
|
@@ -789,6 +810,7 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
789
810
|
this.#closed = true;
|
|
790
811
|
|
|
791
812
|
this.sessionShouldClose.set();
|
|
813
|
+
this.inputResampler?.close();
|
|
792
814
|
|
|
793
815
|
await this.closeActiveSession();
|
|
794
816
|
|
|
@@ -992,13 +1014,16 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
992
1014
|
}
|
|
993
1015
|
break;
|
|
994
1016
|
case 'realtime_input':
|
|
995
|
-
const { mediaChunks, activityStart, activityEnd, text } = msg.value;
|
|
1017
|
+
const { mediaChunks, audio, activityStart, activityEnd, text } = msg.value;
|
|
996
1018
|
if (this.pendingToolCallIds.size > 0) break;
|
|
997
1019
|
if (mediaChunks) {
|
|
998
1020
|
for (const mediaChunk of mediaChunks) {
|
|
999
1021
|
await session.sendRealtimeInput({ media: mediaChunk });
|
|
1000
1022
|
}
|
|
1001
1023
|
}
|
|
1024
|
+
if (audio) {
|
|
1025
|
+
await session.sendRealtimeInput({ audio });
|
|
1026
|
+
}
|
|
1002
1027
|
if (text) {
|
|
1003
1028
|
await session.sendRealtimeInput({ text });
|
|
1004
1029
|
}
|
|
@@ -1152,6 +1177,16 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
1152
1177
|
),
|
|
1153
1178
|
};
|
|
1154
1179
|
}
|
|
1180
|
+
if (obj.type === 'realtime_input' && obj.value?.audio) {
|
|
1181
|
+
const ac = obj.value.audio as { mimeType?: string; data?: string };
|
|
1182
|
+
obj.value = {
|
|
1183
|
+
...obj.value,
|
|
1184
|
+
audio: {
|
|
1185
|
+
...ac,
|
|
1186
|
+
data: typeof ac.data === 'string' ? this.truncateString(ac.data, maxLength) : ac.data,
|
|
1187
|
+
},
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1155
1190
|
return obj;
|
|
1156
1191
|
}
|
|
1157
1192
|
|
|
@@ -1265,17 +1300,15 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
1265
1300
|
},
|
|
1266
1301
|
languageCode: opts.language,
|
|
1267
1302
|
},
|
|
1268
|
-
tools:
|
|
1269
|
-
|
|
1270
|
-
functionDeclarations: this.geminiDeclarations,
|
|
1271
|
-
|
|
1272
|
-
},
|
|
1273
|
-
],
|
|
1303
|
+
tools:
|
|
1304
|
+
this.geminiDeclarations.length > 0 || this.options.geminiTools
|
|
1305
|
+
? [{ functionDeclarations: this.geminiDeclarations, ...this.options.geminiTools }]
|
|
1306
|
+
: undefined,
|
|
1274
1307
|
inputAudioTranscription: opts.inputAudioTranscription,
|
|
1275
1308
|
outputAudioTranscription: opts.outputAudioTranscription,
|
|
1276
|
-
sessionResumption:
|
|
1277
|
-
handle: this.sessionResumptionHandle
|
|
1278
|
-
|
|
1309
|
+
sessionResumption: this.sessionResumptionHandle
|
|
1310
|
+
? { handle: this.sessionResumptionHandle }
|
|
1311
|
+
: {},
|
|
1279
1312
|
};
|
|
1280
1313
|
|
|
1281
1314
|
// Add generation fields at TOP LEVEL (NO generationConfig!)
|
|
@@ -1527,6 +1560,8 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
1527
1560
|
}),
|
|
1528
1561
|
);
|
|
1529
1562
|
}
|
|
1563
|
+
// This closes message/text/audio/function streams so the consumer can continue.
|
|
1564
|
+
this.markCurrentGenerationDone();
|
|
1530
1565
|
}
|
|
1531
1566
|
|
|
1532
1567
|
private handleToolCallCancellation(cancellation: types.LiveServerToolCallCancellation): void {
|
|
@@ -1626,6 +1661,7 @@ export class RealtimeSession extends llm.RealtimeSession {
|
|
|
1626
1661
|
if (this.inputResampler) {
|
|
1627
1662
|
if (frame.sampleRate !== this.inputResamplerInputRate) {
|
|
1628
1663
|
// input audio changed to a different sample rate
|
|
1664
|
+
this.inputResampler.close();
|
|
1629
1665
|
this.inputResampler = undefined;
|
|
1630
1666
|
this.inputResamplerInputRate = undefined;
|
|
1631
1667
|
}
|
package/src/llm.ts
CHANGED
|
@@ -80,7 +80,7 @@ export class LLM extends llm.LLM {
|
|
|
80
80
|
* @param presencePenalty - Penalizes the model for generating previously mentioned concepts. Defaults to undefined.
|
|
81
81
|
* @param frequencyPenalty - Penalizes the model for repeating words. Defaults to undefined.
|
|
82
82
|
* @param toolChoice - Specifies whether to use tools during response generation. Defaults to "auto".
|
|
83
|
-
* @param thinkingConfig - The thinking configuration for response generation. Defaults to undefined.
|
|
83
|
+
* @param thinkingConfig - The thinking configuration for response generation. Gemini 3.1 models use `thinkingLevel`; Gemini 2.5 models use `thinkingBudget`. Defaults to undefined.
|
|
84
84
|
* @param automaticFunctionCallingConfig - The automatic function calling configuration for response generation. Defaults to undefined.
|
|
85
85
|
* @param geminiTools - The Gemini-specific tools to use for the session.
|
|
86
86
|
* @param httpOptions - The HTTP options to use for the session.
|