@langchain/google-common 0.0.21 → 0.0.22

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.
@@ -5,10 +5,9 @@ const env_1 = require("@langchain/core/utils/env");
5
5
  const chat_models_1 = require("@langchain/core/language_models/chat_models");
6
6
  const outputs_1 = require("@langchain/core/outputs");
7
7
  const messages_1 = require("@langchain/core/messages");
8
- const base_1 = require("@langchain/core/language_models/base");
9
8
  const runnables_1 = require("@langchain/core/runnables");
10
9
  const openai_tools_1 = require("@langchain/core/output_parsers/openai_tools");
11
- const function_calling_1 = require("@langchain/core/utils/function_calling");
10
+ const stream_1 = require("@langchain/core/utils/stream");
12
11
  const common_js_1 = require("./utils/common.cjs");
13
12
  const connection_js_1 = require("./connection.cjs");
14
13
  const gemini_js_1 = require("./utils/gemini.cjs");
@@ -85,31 +84,6 @@ class ChatConnection extends connection_js_1.AbstractGoogleLLMConnection {
85
84
  return ret;
86
85
  }
87
86
  }
88
- function convertToGeminiTools(structuredTools) {
89
- return [
90
- {
91
- functionDeclarations: structuredTools.map((structuredTool) => {
92
- if ((0, function_calling_1.isStructuredTool)(structuredTool)) {
93
- const jsonSchema = (0, zod_to_gemini_parameters_js_1.zodToGeminiParameters)(structuredTool.schema);
94
- return {
95
- name: structuredTool.name,
96
- description: structuredTool.description,
97
- parameters: jsonSchema,
98
- };
99
- }
100
- if ((0, base_1.isOpenAITool)(structuredTool)) {
101
- return {
102
- name: structuredTool.function.name,
103
- description: structuredTool.function.description ??
104
- `A function available to call.`,
105
- parameters: (0, zod_to_gemini_parameters_js_1.jsonSchemaToGeminiParameters)(structuredTool.function.parameters),
106
- };
107
- }
108
- return structuredTool;
109
- }),
110
- },
111
- ];
112
- }
113
87
  /**
114
88
  * Integration with a chat model.
115
89
  */
@@ -199,6 +173,12 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
199
173
  writable: true,
200
174
  value: true
201
175
  });
176
+ Object.defineProperty(this, "streaming", {
177
+ enumerable: true,
178
+ configurable: true,
179
+ writable: true,
180
+ value: false
181
+ });
202
182
  Object.defineProperty(this, "connection", {
203
183
  enumerable: true,
204
184
  configurable: true,
@@ -252,7 +232,7 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
252
232
  return this.connection.platform;
253
233
  }
254
234
  bindTools(tools, kwargs) {
255
- return this.bind({ tools: convertToGeminiTools(tools), ...kwargs });
235
+ return this.bind({ tools: (0, common_js_1.convertToGeminiTools)(tools), ...kwargs });
256
236
  }
257
237
  // Replace
258
238
  _llmType() {
@@ -264,13 +244,27 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
264
244
  invocationParams(options) {
265
245
  return (0, common_js_1.copyAIModelParams)(this, options);
266
246
  }
267
- async _generate(messages, options, _runManager) {
247
+ async _generate(messages, options, runManager) {
268
248
  const parameters = this.invocationParams(options);
249
+ if (this.streaming) {
250
+ const stream = this._streamResponseChunks(messages, options, runManager);
251
+ let finalChunk = null;
252
+ for await (const chunk of stream) {
253
+ finalChunk = !finalChunk ? chunk : (0, stream_1.concat)(finalChunk, chunk);
254
+ }
255
+ if (!finalChunk) {
256
+ throw new Error("No chunks were returned from the stream.");
257
+ }
258
+ return {
259
+ generations: [finalChunk],
260
+ };
261
+ }
269
262
  const response = await this.connection.request(messages, parameters, options);
270
263
  const ret = (0, gemini_js_1.safeResponseToChatResult)(response, this.safetyHandler);
264
+ await runManager?.handleLLMNewToken(ret.generations[0].text);
271
265
  return ret;
272
266
  }
273
- async *_streamResponseChunks(_messages, options, _runManager) {
267
+ async *_streamResponseChunks(_messages, options, runManager) {
274
268
  // Make the call as a streaming request
275
269
  const parameters = this.invocationParams(options);
276
270
  const response = await this.streamedConnection.request(_messages, parameters, options);
@@ -303,6 +297,7 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
303
297
  }),
304
298
  });
305
299
  yield chunk;
300
+ await runManager?.handleLLMNewToken(chunk.text);
306
301
  }
307
302
  }
308
303
  /** @ignore */
@@ -45,6 +45,7 @@ export declare abstract class ChatGoogleBase<AuthOptions> extends BaseChatModel<
45
45
  convertSystemMessageToHumanContent: boolean | undefined;
46
46
  safetyHandler: GoogleAISafetyHandler;
47
47
  streamUsage: boolean;
48
+ streaming: boolean;
48
49
  protected connection: ChatConnection<AuthOptions>;
49
50
  protected streamedConnection: ChatConnection<AuthOptions>;
50
51
  constructor(fields?: ChatGoogleBaseInput<AuthOptions>);
@@ -61,8 +62,8 @@ export declare abstract class ChatGoogleBase<AuthOptions> extends BaseChatModel<
61
62
  * Get the parameters used to invoke the model
62
63
  */
63
64
  invocationParams(options?: this["ParsedCallOptions"]): import("./types.js").GoogleAIModelRequestParams;
64
- _generate(messages: BaseMessage[], options: this["ParsedCallOptions"], _runManager: CallbackManagerForLLMRun | undefined): Promise<ChatResult>;
65
- _streamResponseChunks(_messages: BaseMessage[], options: this["ParsedCallOptions"], _runManager?: CallbackManagerForLLMRun): AsyncGenerator<ChatGenerationChunk>;
65
+ _generate(messages: BaseMessage[], options: this["ParsedCallOptions"], runManager: CallbackManagerForLLMRun | undefined): Promise<ChatResult>;
66
+ _streamResponseChunks(_messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun): AsyncGenerator<ChatGenerationChunk>;
66
67
  /** @ignore */
67
68
  _combineLLMOutput(): never[];
68
69
  withStructuredOutput<RunOutput extends Record<string, any> = Record<string, any>>(outputSchema: z.ZodType<RunOutput> | Record<string, any>, config?: StructuredOutputMethodOptions<false>): Runnable<BaseLanguageModelInput, RunOutput>;
@@ -2,16 +2,15 @@ import { getEnvironmentVariable } from "@langchain/core/utils/env";
2
2
  import { BaseChatModel, } from "@langchain/core/language_models/chat_models";
3
3
  import { ChatGenerationChunk } from "@langchain/core/outputs";
4
4
  import { AIMessageChunk } from "@langchain/core/messages";
5
- import { isOpenAITool, } from "@langchain/core/language_models/base";
6
5
  import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables";
7
6
  import { JsonOutputKeyToolsParser } from "@langchain/core/output_parsers/openai_tools";
8
- import { isStructuredTool } from "@langchain/core/utils/function_calling";
9
- import { copyAIModelParams, copyAndValidateModelParamsInto, } from "./utils/common.js";
7
+ import { concat } from "@langchain/core/utils/stream";
8
+ import { convertToGeminiTools, copyAIModelParams, copyAndValidateModelParamsInto, } from "./utils/common.js";
10
9
  import { AbstractGoogleLLMConnection } from "./connection.js";
11
10
  import { baseMessageToContent, safeResponseToChatGeneration, safeResponseToChatResult, DefaultGeminiSafetyHandler, } from "./utils/gemini.js";
12
11
  import { ApiKeyGoogleAuth } from "./auth.js";
13
12
  import { ensureParams } from "./utils/failed_handler.js";
14
- import { jsonSchemaToGeminiParameters, zodToGeminiParameters, } from "./utils/zod_to_gemini_parameters.js";
13
+ import { zodToGeminiParameters } from "./utils/zod_to_gemini_parameters.js";
15
14
  class ChatConnection extends AbstractGoogleLLMConnection {
16
15
  constructor(fields, caller, client, streaming) {
17
16
  super(fields, caller, client, streaming);
@@ -82,31 +81,6 @@ class ChatConnection extends AbstractGoogleLLMConnection {
82
81
  return ret;
83
82
  }
84
83
  }
85
- function convertToGeminiTools(structuredTools) {
86
- return [
87
- {
88
- functionDeclarations: structuredTools.map((structuredTool) => {
89
- if (isStructuredTool(structuredTool)) {
90
- const jsonSchema = zodToGeminiParameters(structuredTool.schema);
91
- return {
92
- name: structuredTool.name,
93
- description: structuredTool.description,
94
- parameters: jsonSchema,
95
- };
96
- }
97
- if (isOpenAITool(structuredTool)) {
98
- return {
99
- name: structuredTool.function.name,
100
- description: structuredTool.function.description ??
101
- `A function available to call.`,
102
- parameters: jsonSchemaToGeminiParameters(structuredTool.function.parameters),
103
- };
104
- }
105
- return structuredTool;
106
- }),
107
- },
108
- ];
109
- }
110
84
  /**
111
85
  * Integration with a chat model.
112
86
  */
@@ -196,6 +170,12 @@ export class ChatGoogleBase extends BaseChatModel {
196
170
  writable: true,
197
171
  value: true
198
172
  });
173
+ Object.defineProperty(this, "streaming", {
174
+ enumerable: true,
175
+ configurable: true,
176
+ writable: true,
177
+ value: false
178
+ });
199
179
  Object.defineProperty(this, "connection", {
200
180
  enumerable: true,
201
181
  configurable: true,
@@ -261,13 +241,27 @@ export class ChatGoogleBase extends BaseChatModel {
261
241
  invocationParams(options) {
262
242
  return copyAIModelParams(this, options);
263
243
  }
264
- async _generate(messages, options, _runManager) {
244
+ async _generate(messages, options, runManager) {
265
245
  const parameters = this.invocationParams(options);
246
+ if (this.streaming) {
247
+ const stream = this._streamResponseChunks(messages, options, runManager);
248
+ let finalChunk = null;
249
+ for await (const chunk of stream) {
250
+ finalChunk = !finalChunk ? chunk : concat(finalChunk, chunk);
251
+ }
252
+ if (!finalChunk) {
253
+ throw new Error("No chunks were returned from the stream.");
254
+ }
255
+ return {
256
+ generations: [finalChunk],
257
+ };
258
+ }
266
259
  const response = await this.connection.request(messages, parameters, options);
267
260
  const ret = safeResponseToChatResult(response, this.safetyHandler);
261
+ await runManager?.handleLLMNewToken(ret.generations[0].text);
268
262
  return ret;
269
263
  }
270
- async *_streamResponseChunks(_messages, options, _runManager) {
264
+ async *_streamResponseChunks(_messages, options, runManager) {
271
265
  // Make the call as a streaming request
272
266
  const parameters = this.invocationParams(options);
273
267
  const response = await this.streamedConnection.request(_messages, parameters, options);
@@ -300,6 +294,7 @@ export class ChatGoogleBase extends BaseChatModel {
300
294
  }),
301
295
  });
302
296
  yield chunk;
297
+ await runManager?.handleLLMNewToken(chunk.text);
303
298
  }
304
299
  }
305
300
  /** @ignore */
@@ -256,10 +256,22 @@ class AbstractGoogleLLMConnection extends GoogleAIConnection {
256
256
  return tools;
257
257
  }
258
258
  }
259
+ formatToolConfig(parameters) {
260
+ if (!parameters.tool_choice || typeof parameters.tool_choice !== "string") {
261
+ return undefined;
262
+ }
263
+ return {
264
+ functionCallingConfig: {
265
+ mode: parameters.tool_choice,
266
+ allowedFunctionNames: parameters.allowed_function_names,
267
+ },
268
+ };
269
+ }
259
270
  formatData(input, parameters) {
260
271
  const contents = this.formatContents(input, parameters);
261
272
  const generationConfig = this.formatGenerationConfig(input, parameters);
262
273
  const tools = this.formatTools(input, parameters);
274
+ const toolConfig = this.formatToolConfig(parameters);
263
275
  const safetySettings = this.formatSafetySettings(input, parameters);
264
276
  const systemInstruction = this.formatSystemInstruction(input, parameters);
265
277
  const ret = {
@@ -269,6 +281,9 @@ class AbstractGoogleLLMConnection extends GoogleAIConnection {
269
281
  if (tools && tools.length) {
270
282
  ret.tools = tools;
271
283
  }
284
+ if (toolConfig) {
285
+ ret.toolConfig = toolConfig;
286
+ }
272
287
  if (safetySettings && safetySettings.length) {
273
288
  ret.safetySettings = safetySettings;
274
289
  }
@@ -53,5 +53,6 @@ export declare abstract class AbstractGoogleLLMConnection<MessageType, AuthOptio
53
53
  structuredToolToFunctionDeclaration(tool: StructuredToolInterface): GeminiFunctionDeclaration;
54
54
  structuredToolsToGeminiTools(tools: StructuredToolInterface[]): GeminiTool[];
55
55
  formatTools(_input: MessageType, parameters: GoogleAIModelRequestParams): GeminiTool[];
56
+ formatToolConfig(parameters: GoogleAIModelRequestParams): GeminiRequest["toolConfig"] | undefined;
56
57
  formatData(input: MessageType, parameters: GoogleAIModelRequestParams): GeminiRequest;
57
58
  }
@@ -250,10 +250,22 @@ export class AbstractGoogleLLMConnection extends GoogleAIConnection {
250
250
  return tools;
251
251
  }
252
252
  }
253
+ formatToolConfig(parameters) {
254
+ if (!parameters.tool_choice || typeof parameters.tool_choice !== "string") {
255
+ return undefined;
256
+ }
257
+ return {
258
+ functionCallingConfig: {
259
+ mode: parameters.tool_choice,
260
+ allowedFunctionNames: parameters.allowed_function_names,
261
+ },
262
+ };
263
+ }
253
264
  formatData(input, parameters) {
254
265
  const contents = this.formatContents(input, parameters);
255
266
  const generationConfig = this.formatGenerationConfig(input, parameters);
256
267
  const tools = this.formatTools(input, parameters);
268
+ const toolConfig = this.formatToolConfig(parameters);
257
269
  const safetySettings = this.formatSafetySettings(input, parameters);
258
270
  const systemInstruction = this.formatSystemInstruction(input, parameters);
259
271
  const ret = {
@@ -263,6 +275,9 @@ export class AbstractGoogleLLMConnection extends GoogleAIConnection {
263
275
  if (tools && tools.length) {
264
276
  ret.tools = tools;
265
277
  }
278
+ if (toolConfig) {
279
+ ret.toolConfig = toolConfig;
280
+ }
266
281
  if (safetySettings && safetySettings.length) {
267
282
  ret.safetySettings = safetySettings;
268
283
  }
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { BaseLLMParams } from "@langchain/core/language_models/llms";
2
- import { BaseLanguageModelCallOptions } from "@langchain/core/language_models/base";
3
2
  import { StructuredToolInterface } from "@langchain/core/tools";
3
+ import type { BaseChatModelCallOptions } from "@langchain/core/language_models/chat_models";
4
4
  import type { JsonStream } from "./utils/stream.js";
5
5
  /**
6
6
  * Parameters needed to setup the client connection.
@@ -85,16 +85,39 @@ export interface GoogleAIModelParams {
85
85
  * @default "text/plain"
86
86
  */
87
87
  responseMimeType?: GoogleAIResponseMimeType;
88
+ /**
89
+ * Whether or not to stream.
90
+ * @default false
91
+ */
92
+ streaming?: boolean;
88
93
  }
89
94
  /**
90
95
  * The params which can be passed to the API at request time.
91
96
  */
92
97
  export interface GoogleAIModelRequestParams extends GoogleAIModelParams {
93
98
  tools?: StructuredToolInterface[] | GeminiTool[];
99
+ /**
100
+ * Force the model to use tools in a specific way.
101
+ *
102
+ * | Mode | Description |
103
+ * |----------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
104
+ * | "auto" | The default model behavior. The model decides whether to predict a function call or a natural language response. |
105
+ * | "any" | The model must predict only function calls. To limit the model to a subset of functions, define the allowed function names in `allowed_function_names`. |
106
+ * | "none" | The model must not predict function calls. This behavior is equivalent to a model request without any associated function declarations. |
107
+ * | string | The string value must be one of the function names. This will force the model to predict the specified function call. |
108
+ *
109
+ * The tool configuration's "any" mode ("forced function calling") is supported for Gemini 1.5 Pro models only.
110
+ */
111
+ tool_choice?: string | "auto" | "any" | "none" | Record<string, any>;
112
+ /**
113
+ * Allowed functions to call when the mode is "any".
114
+ * If empty, any one of the provided functions are called.
115
+ */
116
+ allowed_function_names?: string[];
94
117
  }
95
118
  export interface GoogleAIBaseLLMInput<AuthOptions> extends BaseLLMParams, GoogleConnectionParams<AuthOptions>, GoogleAIModelParams, GoogleAISafetyParams {
96
119
  }
97
- export interface GoogleAIBaseLanguageModelCallOptions extends BaseLanguageModelCallOptions, GoogleAIModelRequestParams, GoogleAISafetyParams {
120
+ export interface GoogleAIBaseLanguageModelCallOptions extends BaseChatModelCallOptions, GoogleAIModelRequestParams, GoogleAISafetyParams {
98
121
  /**
99
122
  * Whether or not to include usage data, like token counts
100
123
  * in the streamed response chunks.
@@ -183,6 +206,12 @@ export interface GeminiRequest {
183
206
  contents?: GeminiContent[];
184
207
  systemInstruction?: GeminiContent;
185
208
  tools?: GeminiTool[];
209
+ toolConfig?: {
210
+ functionCallingConfig: {
211
+ mode: "auto" | "any" | "none";
212
+ allowedFunctionNames?: string[];
213
+ };
214
+ };
186
215
  safetySettings?: GeminiSafetySetting[];
187
216
  generationConfig?: GeminiGenerationConfig;
188
217
  }
@@ -1,11 +1,72 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.copyAndValidateModelParamsInto = exports.validateModelParams = exports.modelToFamily = exports.copyAIModelParamsInto = exports.copyAIModelParams = void 0;
3
+ exports.copyAndValidateModelParamsInto = exports.validateModelParams = exports.modelToFamily = exports.copyAIModelParamsInto = exports.convertToGeminiTools = exports.copyAIModelParams = void 0;
4
+ const base_1 = require("@langchain/core/language_models/base");
5
+ const function_calling_1 = require("@langchain/core/utils/function_calling");
4
6
  const gemini_js_1 = require("./gemini.cjs");
7
+ const zod_to_gemini_parameters_js_1 = require("./zod_to_gemini_parameters.cjs");
5
8
  function copyAIModelParams(params, options) {
6
9
  return copyAIModelParamsInto(params, options, {});
7
10
  }
8
11
  exports.copyAIModelParams = copyAIModelParams;
12
+ function processToolChoice(toolChoice, allowedFunctionNames) {
13
+ if (!toolChoice) {
14
+ if (allowedFunctionNames) {
15
+ // Allowed func names is passed, return 'any' so it forces the model to use a tool.
16
+ return {
17
+ tool_choice: "any",
18
+ allowed_function_names: allowedFunctionNames,
19
+ };
20
+ }
21
+ return undefined;
22
+ }
23
+ if (toolChoice === "any" || toolChoice === "auto" || toolChoice === "none") {
24
+ return {
25
+ tool_choice: toolChoice,
26
+ allowed_function_names: allowedFunctionNames,
27
+ };
28
+ }
29
+ if (typeof toolChoice === "string") {
30
+ // String representing the function name.
31
+ // Return any to force the model to predict the specified function call.
32
+ return {
33
+ tool_choice: "any",
34
+ allowed_function_names: [...(allowedFunctionNames ?? []), toolChoice],
35
+ };
36
+ }
37
+ throw new Error("Object inputs for tool_choice not supported.");
38
+ }
39
+ function convertToGeminiTools(structuredTools) {
40
+ const tools = [
41
+ {
42
+ functionDeclarations: [],
43
+ },
44
+ ];
45
+ structuredTools.forEach((tool) => {
46
+ if ("functionDeclarations" in tool &&
47
+ Array.isArray(tool.functionDeclarations)) {
48
+ const funcs = tool.functionDeclarations;
49
+ tools[0].functionDeclarations?.push(...funcs);
50
+ }
51
+ else if ((0, function_calling_1.isStructuredTool)(tool)) {
52
+ const jsonSchema = (0, zod_to_gemini_parameters_js_1.zodToGeminiParameters)(tool.schema);
53
+ tools[0].functionDeclarations?.push({
54
+ name: tool.name,
55
+ description: tool.description,
56
+ parameters: jsonSchema,
57
+ });
58
+ }
59
+ else if ((0, base_1.isOpenAITool)(tool)) {
60
+ tools[0].functionDeclarations?.push({
61
+ name: tool.function.name,
62
+ description: tool.function.description ?? `A function available to call.`,
63
+ parameters: (0, zod_to_gemini_parameters_js_1.jsonSchemaToGeminiParameters)(tool.function.parameters),
64
+ });
65
+ }
66
+ });
67
+ return tools;
68
+ }
69
+ exports.convertToGeminiTools = convertToGeminiTools;
9
70
  function copyAIModelParamsInto(params, options, target) {
10
71
  const ret = target || {};
11
72
  const model = options?.model ?? params?.model ?? target.model;
@@ -32,59 +93,17 @@ function copyAIModelParamsInto(params, options, target) {
32
93
  options?.responseMimeType ??
33
94
  params?.responseMimeType ??
34
95
  target?.responseMimeType;
35
- ret.tools = options?.tools;
36
- // Ensure tools are formatted properly for Gemini
37
- const geminiTools = options?.tools
38
- ?.map((tool) => {
39
- if ("function" in tool &&
40
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
- "parameters" in tool.function) {
42
- // Tool is in OpenAI format. Convert to Gemini then return.
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- const castTool = tool.function;
45
- const cleanedParameters = castTool.parameters;
46
- if ("$schema" in cleanedParameters) {
47
- delete cleanedParameters.$schema;
48
- }
49
- if ("additionalProperties" in cleanedParameters) {
50
- delete cleanedParameters.additionalProperties;
51
- }
52
- const toolInGeminiFormat = {
53
- functionDeclarations: [
54
- {
55
- name: castTool.name,
56
- description: castTool.description,
57
- parameters: cleanedParameters,
58
- },
59
- ],
60
- };
61
- return toolInGeminiFormat;
62
- }
63
- else if ("functionDeclarations" in tool) {
64
- return tool;
65
- }
66
- else {
67
- return null;
68
- }
69
- })
70
- .filter((tool) => tool !== null);
71
- const structuredOutputTools = options?.tools
72
- ?.map((tool) => {
73
- if ("lc_namespace" in tool) {
74
- return tool;
75
- }
76
- else {
77
- return null;
78
- }
79
- })
80
- .filter((tool) => tool !== null);
81
- if (structuredOutputTools &&
82
- structuredOutputTools.length > 0 &&
83
- geminiTools &&
84
- geminiTools.length > 0) {
85
- throw new Error(`Cannot mix structured tools with Gemini tools.\nReceived ${structuredOutputTools.length} structured tools and ${geminiTools.length} Gemini tools.`);
96
+ ret.streaming = options?.streaming ?? params?.streaming ?? target?.streaming;
97
+ const toolChoice = processToolChoice(options?.tool_choice, options?.allowed_function_names);
98
+ if (toolChoice) {
99
+ ret.tool_choice = toolChoice.tool_choice;
100
+ ret.allowed_function_names = toolChoice.allowed_function_names;
101
+ }
102
+ const tools = options?.tools;
103
+ if (tools) {
104
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
+ ret.tools = convertToGeminiTools(tools);
86
106
  }
87
- ret.tools = geminiTools ?? structuredOutputTools;
88
107
  return ret;
89
108
  }
90
109
  exports.copyAIModelParamsInto = copyAIModelParamsInto;
@@ -1,5 +1,9 @@
1
- import type { GoogleAIBaseLanguageModelCallOptions, GoogleAIModelParams, GoogleAIModelRequestParams, GoogleLLMModelFamily } from "../types.js";
1
+ import { StructuredToolInterface } from "@langchain/core/tools";
2
+ import { ToolDefinition } from "@langchain/core/language_models/base";
3
+ import { RunnableToolLike } from "@langchain/core/runnables";
4
+ import type { GeminiTool, GoogleAIBaseLanguageModelCallOptions, GoogleAIModelParams, GoogleAIModelRequestParams, GoogleLLMModelFamily } from "../types.js";
2
5
  export declare function copyAIModelParams(params: GoogleAIModelParams | undefined, options: GoogleAIBaseLanguageModelCallOptions | undefined): GoogleAIModelRequestParams;
6
+ export declare function convertToGeminiTools(structuredTools: (StructuredToolInterface | Record<string, unknown> | ToolDefinition | RunnableToolLike)[]): GeminiTool[];
3
7
  export declare function copyAIModelParamsInto(params: GoogleAIModelParams | undefined, options: GoogleAIBaseLanguageModelCallOptions | undefined, target: GoogleAIModelParams): GoogleAIModelRequestParams;
4
8
  export declare function modelToFamily(modelName: string | undefined): GoogleLLMModelFamily;
5
9
  export declare function validateModelParams(params: GoogleAIModelParams | undefined): void;
@@ -1,7 +1,67 @@
1
+ import { isOpenAITool, } from "@langchain/core/language_models/base";
2
+ import { isStructuredTool } from "@langchain/core/utils/function_calling";
1
3
  import { isModelGemini, validateGeminiParams } from "./gemini.js";
4
+ import { jsonSchemaToGeminiParameters, zodToGeminiParameters, } from "./zod_to_gemini_parameters.js";
2
5
  export function copyAIModelParams(params, options) {
3
6
  return copyAIModelParamsInto(params, options, {});
4
7
  }
8
+ function processToolChoice(toolChoice, allowedFunctionNames) {
9
+ if (!toolChoice) {
10
+ if (allowedFunctionNames) {
11
+ // Allowed func names is passed, return 'any' so it forces the model to use a tool.
12
+ return {
13
+ tool_choice: "any",
14
+ allowed_function_names: allowedFunctionNames,
15
+ };
16
+ }
17
+ return undefined;
18
+ }
19
+ if (toolChoice === "any" || toolChoice === "auto" || toolChoice === "none") {
20
+ return {
21
+ tool_choice: toolChoice,
22
+ allowed_function_names: allowedFunctionNames,
23
+ };
24
+ }
25
+ if (typeof toolChoice === "string") {
26
+ // String representing the function name.
27
+ // Return any to force the model to predict the specified function call.
28
+ return {
29
+ tool_choice: "any",
30
+ allowed_function_names: [...(allowedFunctionNames ?? []), toolChoice],
31
+ };
32
+ }
33
+ throw new Error("Object inputs for tool_choice not supported.");
34
+ }
35
+ export function convertToGeminiTools(structuredTools) {
36
+ const tools = [
37
+ {
38
+ functionDeclarations: [],
39
+ },
40
+ ];
41
+ structuredTools.forEach((tool) => {
42
+ if ("functionDeclarations" in tool &&
43
+ Array.isArray(tool.functionDeclarations)) {
44
+ const funcs = tool.functionDeclarations;
45
+ tools[0].functionDeclarations?.push(...funcs);
46
+ }
47
+ else if (isStructuredTool(tool)) {
48
+ const jsonSchema = zodToGeminiParameters(tool.schema);
49
+ tools[0].functionDeclarations?.push({
50
+ name: tool.name,
51
+ description: tool.description,
52
+ parameters: jsonSchema,
53
+ });
54
+ }
55
+ else if (isOpenAITool(tool)) {
56
+ tools[0].functionDeclarations?.push({
57
+ name: tool.function.name,
58
+ description: tool.function.description ?? `A function available to call.`,
59
+ parameters: jsonSchemaToGeminiParameters(tool.function.parameters),
60
+ });
61
+ }
62
+ });
63
+ return tools;
64
+ }
5
65
  export function copyAIModelParamsInto(params, options, target) {
6
66
  const ret = target || {};
7
67
  const model = options?.model ?? params?.model ?? target.model;
@@ -28,59 +88,17 @@ export function copyAIModelParamsInto(params, options, target) {
28
88
  options?.responseMimeType ??
29
89
  params?.responseMimeType ??
30
90
  target?.responseMimeType;
31
- ret.tools = options?.tools;
32
- // Ensure tools are formatted properly for Gemini
33
- const geminiTools = options?.tools
34
- ?.map((tool) => {
35
- if ("function" in tool &&
36
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
- "parameters" in tool.function) {
38
- // Tool is in OpenAI format. Convert to Gemini then return.
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- const castTool = tool.function;
41
- const cleanedParameters = castTool.parameters;
42
- if ("$schema" in cleanedParameters) {
43
- delete cleanedParameters.$schema;
44
- }
45
- if ("additionalProperties" in cleanedParameters) {
46
- delete cleanedParameters.additionalProperties;
47
- }
48
- const toolInGeminiFormat = {
49
- functionDeclarations: [
50
- {
51
- name: castTool.name,
52
- description: castTool.description,
53
- parameters: cleanedParameters,
54
- },
55
- ],
56
- };
57
- return toolInGeminiFormat;
58
- }
59
- else if ("functionDeclarations" in tool) {
60
- return tool;
61
- }
62
- else {
63
- return null;
64
- }
65
- })
66
- .filter((tool) => tool !== null);
67
- const structuredOutputTools = options?.tools
68
- ?.map((tool) => {
69
- if ("lc_namespace" in tool) {
70
- return tool;
71
- }
72
- else {
73
- return null;
74
- }
75
- })
76
- .filter((tool) => tool !== null);
77
- if (structuredOutputTools &&
78
- structuredOutputTools.length > 0 &&
79
- geminiTools &&
80
- geminiTools.length > 0) {
81
- throw new Error(`Cannot mix structured tools with Gemini tools.\nReceived ${structuredOutputTools.length} structured tools and ${geminiTools.length} Gemini tools.`);
91
+ ret.streaming = options?.streaming ?? params?.streaming ?? target?.streaming;
92
+ const toolChoice = processToolChoice(options?.tool_choice, options?.allowed_function_names);
93
+ if (toolChoice) {
94
+ ret.tool_choice = toolChoice.tool_choice;
95
+ ret.allowed_function_names = toolChoice.allowed_function_names;
96
+ }
97
+ const tools = options?.tools;
98
+ if (tools) {
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ ret.tools = convertToGeminiTools(tools);
82
101
  }
83
- ret.tools = geminiTools ?? structuredOutputTools;
84
102
  return ret;
85
103
  }
86
104
  export function modelToFamily(modelName) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MessageGeminiSafetyHandler = exports.DefaultGeminiSafetyHandler = exports.isModelGemini = exports.validateGeminiParams = exports.safeResponseToChatResult = exports.responseToChatResult = exports.safeResponseToBaseMessage = exports.responseToBaseMessage = exports.partsToBaseMessageFields = exports.responseToBaseMessageFields = exports.responseToChatGenerations = exports.partToChatGeneration = exports.partToMessageChunk = exports.chunkToString = exports.safeResponseToChatGeneration = exports.responseToChatGeneration = exports.safeResponseToGeneration = exports.responseToGeneration = exports.responseToGenerationInfo = exports.safeResponseToString = exports.responseToString = exports.partToText = exports.responseToParts = exports.responseToGenerateContentResponseData = exports.toolsRawToTools = exports.partsToToolsRaw = exports.partsToMessageContent = exports.baseMessageToContent = exports.messageContentToParts = void 0;
3
+ exports.MessageGeminiSafetyHandler = exports.DefaultGeminiSafetyHandler = exports.isModelGemini = exports.validateGeminiParams = exports.safeResponseToChatResult = exports.responseToChatResult = exports.safeResponseToBaseMessage = exports.responseToBaseMessage = exports.partsToBaseMessageChunkFields = exports.responseToBaseMessageFields = exports.responseToChatGenerations = exports.partToChatGeneration = exports.partToMessageChunk = exports.chunkToString = exports.safeResponseToChatGeneration = exports.responseToChatGeneration = exports.safeResponseToGeneration = exports.responseToGeneration = exports.responseToGenerationInfo = exports.safeResponseToString = exports.responseToString = exports.partToText = exports.responseToParts = exports.responseToGenerateContentResponseData = exports.toolsRawToTools = exports.partsToToolsRaw = exports.partsToMessageContent = exports.baseMessageToContent = exports.messageContentToParts = void 0;
4
4
  const uuid_1 = require("uuid");
5
5
  const messages_1 = require("@langchain/core/messages");
6
6
  const outputs_1 = require("@langchain/core/outputs");
@@ -445,7 +445,7 @@ function chunkToString(chunk) {
445
445
  }
446
446
  exports.chunkToString = chunkToString;
447
447
  function partToMessageChunk(part) {
448
- const fields = partsToBaseMessageFields([part]);
448
+ const fields = partsToBaseMessageChunkFields([part]);
449
449
  if (typeof fields.content === "string") {
450
450
  return new messages_1.AIMessageChunk(fields);
451
451
  }
@@ -510,12 +510,13 @@ function responseToChatGenerations(response) {
510
510
  exports.responseToChatGenerations = responseToChatGenerations;
511
511
  function responseToBaseMessageFields(response) {
512
512
  const parts = responseToParts(response);
513
- return partsToBaseMessageFields(parts);
513
+ return partsToBaseMessageChunkFields(parts);
514
514
  }
515
515
  exports.responseToBaseMessageFields = responseToBaseMessageFields;
516
- function partsToBaseMessageFields(parts) {
516
+ function partsToBaseMessageChunkFields(parts) {
517
517
  const fields = {
518
518
  content: partsToMessageContent(parts),
519
+ tool_call_chunks: [],
519
520
  tool_calls: [],
520
521
  invalid_tool_calls: [],
521
522
  };
@@ -523,6 +524,12 @@ function partsToBaseMessageFields(parts) {
523
524
  if (rawTools.length > 0) {
524
525
  const tools = toolsRawToTools(rawTools);
525
526
  for (const tool of tools) {
527
+ fields.tool_call_chunks?.push({
528
+ name: tool.function.name,
529
+ args: tool.function.arguments,
530
+ id: tool.id,
531
+ type: "tool_call_chunk",
532
+ });
526
533
  try {
527
534
  fields.tool_calls?.push({
528
535
  name: tool.function.name,
@@ -535,7 +542,7 @@ function partsToBaseMessageFields(parts) {
535
542
  catch (e) {
536
543
  fields.invalid_tool_calls?.push({
537
544
  name: tool.function.name,
538
- args: JSON.parse(tool.function.arguments),
545
+ args: tool.function.arguments,
539
546
  id: tool.id,
540
547
  error: e.message,
541
548
  type: "invalid_tool_call",
@@ -548,7 +555,7 @@ function partsToBaseMessageFields(parts) {
548
555
  }
549
556
  return fields;
550
557
  }
551
- exports.partsToBaseMessageFields = partsToBaseMessageFields;
558
+ exports.partsToBaseMessageChunkFields = partsToBaseMessageChunkFields;
552
559
  function responseToBaseMessage(response) {
553
560
  const fields = responseToBaseMessageFields(response);
554
561
  return new messages_1.AIMessage(fields);
@@ -1,4 +1,4 @@
1
- import { AIMessageFields, BaseMessage, BaseMessageChunk, BaseMessageFields, MessageContent } from "@langchain/core/messages";
1
+ import { AIMessageChunkFields, BaseMessage, BaseMessageChunk, BaseMessageFields, MessageContent } from "@langchain/core/messages";
2
2
  import { ChatGeneration, ChatGenerationChunk, ChatResult, Generation } from "@langchain/core/outputs";
3
3
  import type { GoogleLLMResponse, GoogleAIModelParams, GeminiPart, GeminiContent, GenerateContentResponseData, GoogleAISafetyHandler } from "../types.js";
4
4
  export declare function messageContentToParts(content: MessageContent): GeminiPart[];
@@ -57,7 +57,7 @@ export declare function partToMessageChunk(part: GeminiPart): BaseMessageChunk;
57
57
  export declare function partToChatGeneration(part: GeminiPart): ChatGeneration;
58
58
  export declare function responseToChatGenerations(response: GoogleLLMResponse): ChatGeneration[];
59
59
  export declare function responseToBaseMessageFields(response: GoogleLLMResponse): BaseMessageFields;
60
- export declare function partsToBaseMessageFields(parts: GeminiPart[]): AIMessageFields;
60
+ export declare function partsToBaseMessageChunkFields(parts: GeminiPart[]): AIMessageChunkFields;
61
61
  export declare function responseToBaseMessage(response: GoogleLLMResponse): BaseMessage;
62
62
  export declare function safeResponseToBaseMessage(response: GoogleLLMResponse, safetyHandler: GoogleAISafetyHandler): BaseMessage;
63
63
  export declare function responseToChatResult(response: GoogleLLMResponse): ChatResult;
@@ -426,7 +426,7 @@ export function chunkToString(chunk) {
426
426
  }
427
427
  }
428
428
  export function partToMessageChunk(part) {
429
- const fields = partsToBaseMessageFields([part]);
429
+ const fields = partsToBaseMessageChunkFields([part]);
430
430
  if (typeof fields.content === "string") {
431
431
  return new AIMessageChunk(fields);
432
432
  }
@@ -488,11 +488,12 @@ export function responseToChatGenerations(response) {
488
488
  }
489
489
  export function responseToBaseMessageFields(response) {
490
490
  const parts = responseToParts(response);
491
- return partsToBaseMessageFields(parts);
491
+ return partsToBaseMessageChunkFields(parts);
492
492
  }
493
- export function partsToBaseMessageFields(parts) {
493
+ export function partsToBaseMessageChunkFields(parts) {
494
494
  const fields = {
495
495
  content: partsToMessageContent(parts),
496
+ tool_call_chunks: [],
496
497
  tool_calls: [],
497
498
  invalid_tool_calls: [],
498
499
  };
@@ -500,6 +501,12 @@ export function partsToBaseMessageFields(parts) {
500
501
  if (rawTools.length > 0) {
501
502
  const tools = toolsRawToTools(rawTools);
502
503
  for (const tool of tools) {
504
+ fields.tool_call_chunks?.push({
505
+ name: tool.function.name,
506
+ args: tool.function.arguments,
507
+ id: tool.id,
508
+ type: "tool_call_chunk",
509
+ });
503
510
  try {
504
511
  fields.tool_calls?.push({
505
512
  name: tool.function.name,
@@ -512,7 +519,7 @@ export function partsToBaseMessageFields(parts) {
512
519
  catch (e) {
513
520
  fields.invalid_tool_calls?.push({
514
521
  name: tool.function.name,
515
- args: JSON.parse(tool.function.arguments),
522
+ args: tool.function.arguments,
516
523
  id: tool.id,
517
524
  error: e.message,
518
525
  type: "invalid_tool_call",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/google-common",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "description": "Core types and classes for Google services.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -15,7 +15,7 @@
15
15
  "homepage": "https://github.com/langchain-ai/langchainjs/tree/main/libs/langchain-google-common/",
16
16
  "scripts": {
17
17
  "build": "yarn turbo:command build:internal --filter=@langchain/google-common",
18
- "build:internal": "yarn lc-build:v2 --create-entrypoints --pre --tree-shaking",
18
+ "build:internal": "yarn lc_build_v2 --create-entrypoints --pre --tree-shaking",
19
19
  "build:deps": "yarn run turbo:command build --filter=@langchain/core",
20
20
  "build:esm": "NODE_OPTIONS=--max-old-space-size=4096 tsc --outDir dist/ && rm -rf dist/tests dist/**/tests",
21
21
  "build:cjs": "NODE_OPTIONS=--max-old-space-size=4096 tsc --outDir dist-cjs/ -p tsconfig.cjs.json && yarn move-cjs-to-dist && rm -rf dist-cjs",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@jest/globals": "^29.5.0",
49
- "@langchain/scripts": "~0.0.14",
49
+ "@langchain/scripts": "~0.0.20",
50
50
  "@swc/core": "^1.3.90",
51
51
  "@swc/jest": "^0.2.29",
52
52
  "@tsconfig/recommended": "^1.0.3",