@langchain/google-genai 0.2.1 → 0.2.3

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.
@@ -756,7 +756,7 @@ class ChatGoogleGenerativeAI extends chat_models_1.BaseChatModel {
756
756
  let functionName = name ?? "extract";
757
757
  let tools;
758
758
  if ((0, types_1.isZodSchema)(schema)) {
759
- const jsonSchema = (0, zod_to_genai_parameters_js_1.zodToGenerativeAIParameters)(schema);
759
+ const jsonSchema = (0, zod_to_genai_parameters_js_1.schemaToGenerativeAIParameters)(schema);
760
760
  tools = [
761
761
  {
762
762
  functionDeclarations: [
@@ -806,9 +806,7 @@ class ChatGoogleGenerativeAI extends chat_models_1.BaseChatModel {
806
806
  });
807
807
  }
808
808
  else {
809
- const jsonSchema = (0, types_1.isZodSchema)(schema)
810
- ? (0, zod_to_genai_parameters_js_1.zodToGenerativeAIParameters)(schema)
811
- : (0, zod_to_genai_parameters_js_1.removeAdditionalProperties)(schema);
809
+ const jsonSchema = (0, zod_to_genai_parameters_js_1.schemaToGenerativeAIParameters)(schema);
812
810
  llm = this.bind({
813
811
  responseSchema: jsonSchema,
814
812
  });
@@ -4,7 +4,7 @@ import { BaseChatModel, } from "@langchain/core/language_models/chat_models";
4
4
  import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables";
5
5
  import { isZodSchema } from "@langchain/core/utils/types";
6
6
  import { JsonOutputParser, } from "@langchain/core/output_parsers";
7
- import { zodToGenerativeAIParameters, removeAdditionalProperties, } from "./utils/zod_to_genai_parameters.js";
7
+ import { schemaToGenerativeAIParameters, removeAdditionalProperties, } from "./utils/zod_to_genai_parameters.js";
8
8
  import { convertBaseMessagesToContent, convertResponseContentToChatGenerationChunk, mapGenerateContentResultToChatResult, } from "./utils/common.js";
9
9
  import { GoogleGenerativeAIToolsOutputParser } from "./output_parsers.js";
10
10
  import { convertToolsToGenAI } from "./utils/tools.js";
@@ -753,7 +753,7 @@ export class ChatGoogleGenerativeAI extends BaseChatModel {
753
753
  let functionName = name ?? "extract";
754
754
  let tools;
755
755
  if (isZodSchema(schema)) {
756
- const jsonSchema = zodToGenerativeAIParameters(schema);
756
+ const jsonSchema = schemaToGenerativeAIParameters(schema);
757
757
  tools = [
758
758
  {
759
759
  functionDeclarations: [
@@ -803,9 +803,7 @@ export class ChatGoogleGenerativeAI extends BaseChatModel {
803
803
  });
804
804
  }
805
805
  else {
806
- const jsonSchema = isZodSchema(schema)
807
- ? zodToGenerativeAIParameters(schema)
808
- : removeAdditionalProperties(schema);
806
+ const jsonSchema = schemaToGenerativeAIParameters(schema);
809
807
  llm = this.bind({
810
808
  responseSchema: jsonSchema,
811
809
  });
@@ -5,6 +5,7 @@ const messages_1 = require("@langchain/core/messages");
5
5
  const outputs_1 = require("@langchain/core/outputs");
6
6
  const function_calling_1 = require("@langchain/core/utils/function_calling");
7
7
  const base_1 = require("@langchain/core/language_models/base");
8
+ const uuid_1 = require("uuid");
8
9
  const zod_to_genai_parameters_js_1 = require("./zod_to_genai_parameters.cjs");
9
10
  function getMessageAuthor(message) {
10
11
  const type = message._getType();
@@ -63,8 +64,23 @@ function messageContentMedia(content) {
63
64
  }
64
65
  throw new Error("Invalid media content");
65
66
  }
66
- function convertMessageContentToParts(message, isMultimodalModel) {
67
- if (typeof message.content === "string" && message.content !== "") {
67
+ function inferToolNameFromPreviousMessages(message, previousMessages) {
68
+ return previousMessages
69
+ .map((msg) => {
70
+ if ((0, messages_1.isAIMessage)(msg)) {
71
+ return msg.tool_calls ?? [];
72
+ }
73
+ return [];
74
+ })
75
+ .flat()
76
+ .find((toolCall) => {
77
+ return toolCall.id === message.tool_call_id;
78
+ })?.name;
79
+ }
80
+ function convertMessageContentToParts(message, isMultimodalModel, previousMessages) {
81
+ if (typeof message.content === "string" &&
82
+ message.content !== "" &&
83
+ !(0, messages_1.isToolMessage)(message)) {
68
84
  return [{ text: message.content }];
69
85
  }
70
86
  let functionCalls = [];
@@ -80,12 +96,19 @@ function convertMessageContentToParts(message, isMultimodalModel) {
80
96
  },
81
97
  }));
82
98
  }
83
- else if (message.getType() === "tool" && message.name && message.content) {
99
+ else if ((0, messages_1.isToolMessage)(message) && message.content) {
100
+ const messageName = message.name ??
101
+ inferToolNameFromPreviousMessages(message, previousMessages);
102
+ if (messageName === undefined) {
103
+ throw new Error(`Google requires a tool name for each tool call response, and we could not infer a called tool name for ToolMessage "${message.id}" from your passed messages. Please populate a "name" field on that ToolMessage explicitly.`);
104
+ }
84
105
  functionResponses = [
85
106
  {
86
107
  functionResponse: {
87
- name: message.name,
88
- response: message.content,
108
+ name: messageName,
109
+ response: typeof message.content === "string"
110
+ ? { result: message.content }
111
+ : message.content,
89
112
  },
90
113
  },
91
114
  ];
@@ -181,7 +204,7 @@ function convertBaseMessagesToContent(messages, isMultimodalModel, convertSystem
181
204
  prevContent.role === role) {
182
205
  throw new Error("Google Generative AI requires alternate messages between authors");
183
206
  }
184
- const parts = convertMessageContentToParts(message, isMultimodalModel);
207
+ const parts = convertMessageContentToParts(message, isMultimodalModel, messages.slice(0, index));
185
208
  if (acc.mergeWithPreviousContent) {
186
209
  const prevContent = acc.content[acc.content.length - 1];
187
210
  if (!prevContent) {
@@ -263,10 +286,13 @@ function mapGenerateContentResultToChatResult(response, extra) {
263
286
  text,
264
287
  message: new messages_1.AIMessage({
265
288
  content,
266
- tool_calls: functionCalls?.map((fc) => ({
267
- ...fc,
268
- type: "tool_call",
269
- })),
289
+ tool_calls: functionCalls?.map((fc) => {
290
+ return {
291
+ ...fc,
292
+ type: "tool_call",
293
+ id: "id" in fc && typeof fc.id === "string" ? fc.id : (0, uuid_1.v4)(),
294
+ };
295
+ }),
270
296
  additional_kwargs: {
271
297
  ...generationInfo,
272
298
  },
@@ -336,6 +362,7 @@ function convertResponseContentToChatGenerationChunk(response, extra) {
336
362
  args: JSON.stringify(fc.args),
337
363
  index: extra.index,
338
364
  type: "tool_call_chunk",
365
+ id: "id" in fc && typeof fc.id === "string" ? fc.id : (0, uuid_1.v4)(),
339
366
  })));
340
367
  }
341
368
  return new outputs_1.ChatGenerationChunk({
@@ -362,7 +389,15 @@ function convertToGenerativeAITools(tools) {
362
389
  {
363
390
  functionDeclarations: tools.map((tool) => {
364
391
  if ((0, function_calling_1.isLangChainTool)(tool)) {
365
- const jsonSchema = (0, zod_to_genai_parameters_js_1.zodToGenerativeAIParameters)(tool.schema);
392
+ const jsonSchema = (0, zod_to_genai_parameters_js_1.schemaToGenerativeAIParameters)(tool.schema);
393
+ if (jsonSchema.type === "object" &&
394
+ "properties" in jsonSchema &&
395
+ Object.keys(jsonSchema.properties).length === 0) {
396
+ return {
397
+ name: tool.name,
398
+ description: tool.description,
399
+ };
400
+ }
366
401
  return {
367
402
  name: tool.name,
368
403
  description: tool.description,
@@ -10,7 +10,7 @@ export declare function getMessageAuthor(message: BaseMessage): string;
10
10
  * @returns The message type mapped to a Google Generative AI chat author.
11
11
  */
12
12
  export declare function convertAuthorToRole(author: string): (typeof POSSIBLE_ROLES)[number];
13
- export declare function convertMessageContentToParts(message: BaseMessage, isMultimodalModel: boolean): Part[];
13
+ export declare function convertMessageContentToParts(message: BaseMessage, isMultimodalModel: boolean, previousMessages: BaseMessage[]): Part[];
14
14
  export declare function convertBaseMessagesToContent(messages: BaseMessage[], isMultimodalModel: boolean, convertSystemMessageToHumanContent?: boolean): Content[];
15
15
  export declare function mapGenerateContentResultToChatResult(response: EnhancedGenerateContentResponse, extra?: {
16
16
  usageMetadata: UsageMetadata | undefined;
@@ -1,8 +1,9 @@
1
- import { AIMessage, AIMessageChunk, ChatMessage, isBaseMessage, } from "@langchain/core/messages";
1
+ import { AIMessage, AIMessageChunk, ChatMessage, isAIMessage, isBaseMessage, isToolMessage, } from "@langchain/core/messages";
2
2
  import { ChatGenerationChunk, } from "@langchain/core/outputs";
3
3
  import { isLangChainTool } from "@langchain/core/utils/function_calling";
4
4
  import { isOpenAITool } from "@langchain/core/language_models/base";
5
- import { jsonSchemaToGeminiParameters, zodToGenerativeAIParameters, } from "./zod_to_genai_parameters.js";
5
+ import { v4 as uuidv4 } from "uuid";
6
+ import { jsonSchemaToGeminiParameters, schemaToGenerativeAIParameters, } from "./zod_to_genai_parameters.js";
6
7
  export function getMessageAuthor(message) {
7
8
  const type = message._getType();
8
9
  if (ChatMessage.isInstance(message)) {
@@ -58,8 +59,23 @@ function messageContentMedia(content) {
58
59
  }
59
60
  throw new Error("Invalid media content");
60
61
  }
61
- export function convertMessageContentToParts(message, isMultimodalModel) {
62
- if (typeof message.content === "string" && message.content !== "") {
62
+ function inferToolNameFromPreviousMessages(message, previousMessages) {
63
+ return previousMessages
64
+ .map((msg) => {
65
+ if (isAIMessage(msg)) {
66
+ return msg.tool_calls ?? [];
67
+ }
68
+ return [];
69
+ })
70
+ .flat()
71
+ .find((toolCall) => {
72
+ return toolCall.id === message.tool_call_id;
73
+ })?.name;
74
+ }
75
+ export function convertMessageContentToParts(message, isMultimodalModel, previousMessages) {
76
+ if (typeof message.content === "string" &&
77
+ message.content !== "" &&
78
+ !isToolMessage(message)) {
63
79
  return [{ text: message.content }];
64
80
  }
65
81
  let functionCalls = [];
@@ -75,12 +91,19 @@ export function convertMessageContentToParts(message, isMultimodalModel) {
75
91
  },
76
92
  }));
77
93
  }
78
- else if (message.getType() === "tool" && message.name && message.content) {
94
+ else if (isToolMessage(message) && message.content) {
95
+ const messageName = message.name ??
96
+ inferToolNameFromPreviousMessages(message, previousMessages);
97
+ if (messageName === undefined) {
98
+ throw new Error(`Google requires a tool name for each tool call response, and we could not infer a called tool name for ToolMessage "${message.id}" from your passed messages. Please populate a "name" field on that ToolMessage explicitly.`);
99
+ }
79
100
  functionResponses = [
80
101
  {
81
102
  functionResponse: {
82
- name: message.name,
83
- response: message.content,
103
+ name: messageName,
104
+ response: typeof message.content === "string"
105
+ ? { result: message.content }
106
+ : message.content,
84
107
  },
85
108
  },
86
109
  ];
@@ -175,7 +198,7 @@ export function convertBaseMessagesToContent(messages, isMultimodalModel, conver
175
198
  prevContent.role === role) {
176
199
  throw new Error("Google Generative AI requires alternate messages between authors");
177
200
  }
178
- const parts = convertMessageContentToParts(message, isMultimodalModel);
201
+ const parts = convertMessageContentToParts(message, isMultimodalModel, messages.slice(0, index));
179
202
  if (acc.mergeWithPreviousContent) {
180
203
  const prevContent = acc.content[acc.content.length - 1];
181
204
  if (!prevContent) {
@@ -256,10 +279,13 @@ export function mapGenerateContentResultToChatResult(response, extra) {
256
279
  text,
257
280
  message: new AIMessage({
258
281
  content,
259
- tool_calls: functionCalls?.map((fc) => ({
260
- ...fc,
261
- type: "tool_call",
262
- })),
282
+ tool_calls: functionCalls?.map((fc) => {
283
+ return {
284
+ ...fc,
285
+ type: "tool_call",
286
+ id: "id" in fc && typeof fc.id === "string" ? fc.id : uuidv4(),
287
+ };
288
+ }),
263
289
  additional_kwargs: {
264
290
  ...generationInfo,
265
291
  },
@@ -328,6 +354,7 @@ export function convertResponseContentToChatGenerationChunk(response, extra) {
328
354
  args: JSON.stringify(fc.args),
329
355
  index: extra.index,
330
356
  type: "tool_call_chunk",
357
+ id: "id" in fc && typeof fc.id === "string" ? fc.id : uuidv4(),
331
358
  })));
332
359
  }
333
360
  return new ChatGenerationChunk({
@@ -353,7 +380,15 @@ export function convertToGenerativeAITools(tools) {
353
380
  {
354
381
  functionDeclarations: tools.map((tool) => {
355
382
  if (isLangChainTool(tool)) {
356
- const jsonSchema = zodToGenerativeAIParameters(tool.schema);
383
+ const jsonSchema = schemaToGenerativeAIParameters(tool.schema);
384
+ if (jsonSchema.type === "object" &&
385
+ "properties" in jsonSchema &&
386
+ Object.keys(jsonSchema.properties).length === 0) {
387
+ return {
388
+ name: tool.name,
389
+ description: tool.description,
390
+ };
391
+ }
357
392
  return {
358
393
  name: tool.name,
359
394
  description: tool.description,
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  /* eslint-disable @typescript-eslint/no-unused-vars */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.jsonSchemaToGeminiParameters = exports.zodToGenerativeAIParameters = exports.removeAdditionalProperties = void 0;
4
+ exports.jsonSchemaToGeminiParameters = exports.schemaToGenerativeAIParameters = exports.removeAdditionalProperties = void 0;
5
+ const types_1 = require("@langchain/core/utils/types");
5
6
  const zod_to_json_schema_1 = require("zod-to-json-schema");
6
7
  function removeAdditionalProperties(
7
8
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -32,16 +33,14 @@ obj) {
32
33
  return obj;
33
34
  }
34
35
  exports.removeAdditionalProperties = removeAdditionalProperties;
35
- function zodToGenerativeAIParameters(
36
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
- zodObj) {
36
+ function schemaToGenerativeAIParameters(schema) {
38
37
  // GenerativeAI doesn't accept either the $schema or additionalProperties
39
38
  // attributes, so we need to explicitly remove them.
40
- const jsonSchema = removeAdditionalProperties((0, zod_to_json_schema_1.zodToJsonSchema)(zodObj));
39
+ const jsonSchema = removeAdditionalProperties((0, types_1.isZodSchema)(schema) ? (0, zod_to_json_schema_1.zodToJsonSchema)(schema) : schema);
41
40
  const { $schema, ...rest } = jsonSchema;
42
41
  return rest;
43
42
  }
44
- exports.zodToGenerativeAIParameters = zodToGenerativeAIParameters;
43
+ exports.schemaToGenerativeAIParameters = schemaToGenerativeAIParameters;
45
44
  function jsonSchemaToGeminiParameters(
46
45
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
46
  schema) {
@@ -1,5 +1,6 @@
1
1
  import type { z } from "zod";
2
2
  import { type FunctionDeclarationSchema as GenerativeAIFunctionDeclarationSchema, type SchemaType as FunctionDeclarationSchemaType } from "@google/generative-ai";
3
+ import { type JsonSchema7Type } from "zod-to-json-schema";
3
4
  export interface GenerativeAIJsonSchema extends Record<string, unknown> {
4
5
  properties?: Record<string, GenerativeAIJsonSchema>;
5
6
  type: FunctionDeclarationSchemaType;
@@ -9,5 +10,5 @@ export interface GenerativeAIJsonSchemaDirty extends GenerativeAIJsonSchema {
9
10
  additionalProperties?: boolean;
10
11
  }
11
12
  export declare function removeAdditionalProperties(obj: Record<string, any>): GenerativeAIJsonSchema;
12
- export declare function zodToGenerativeAIParameters(zodObj: z.ZodType<any>): GenerativeAIFunctionDeclarationSchema;
13
+ export declare function schemaToGenerativeAIParameters<RunOutput extends Record<string, any> = Record<string, any>>(schema: z.ZodType<RunOutput> | z.ZodEffects<z.ZodType<RunOutput>> | JsonSchema7Type): GenerativeAIFunctionDeclarationSchema;
13
14
  export declare function jsonSchemaToGeminiParameters(schema: Record<string, any>): GenerativeAIFunctionDeclarationSchema;
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { isZodSchema } from "@langchain/core/utils/types";
2
3
  import { zodToJsonSchema } from "zod-to-json-schema";
3
4
  export function removeAdditionalProperties(
4
5
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -28,12 +29,10 @@ obj) {
28
29
  }
29
30
  return obj;
30
31
  }
31
- export function zodToGenerativeAIParameters(
32
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
- zodObj) {
32
+ export function schemaToGenerativeAIParameters(schema) {
34
33
  // GenerativeAI doesn't accept either the $schema or additionalProperties
35
34
  // attributes, so we need to explicitly remove them.
36
- const jsonSchema = removeAdditionalProperties(zodToJsonSchema(zodObj));
35
+ const jsonSchema = removeAdditionalProperties(isZodSchema(schema) ? zodToJsonSchema(schema) : schema);
37
36
  const { $schema, ...rest } = jsonSchema;
38
37
  return rest;
39
38
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/google-genai",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Google Generative AI integration for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -36,6 +36,7 @@
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
38
  "@google/generative-ai": "^0.24.0",
39
+ "uuid": "^11.1.0",
39
40
  "zod-to-json-schema": "^3.22.4"
40
41
  },
41
42
  "peerDependencies": {
@@ -43,7 +44,7 @@
43
44
  },
44
45
  "devDependencies": {
45
46
  "@jest/globals": "^29.5.0",
46
- "@langchain/core": "workspace:*",
47
+ "@langchain/core": "0.3.44",
47
48
  "@langchain/scripts": ">=0.1.0 <0.2.0",
48
49
  "@langchain/standard-tests": "0.0.0",
49
50
  "@swc/core": "^1.3.90",
@@ -91,4 +92,4 @@
91
92
  "index.d.ts",
92
93
  "index.d.cts"
93
94
  ]
94
- }
95
+ }