@langchain/google-common 0.1.8 → 0.2.1

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.
@@ -131,25 +131,25 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
131
131
  enumerable: true,
132
132
  configurable: true,
133
133
  writable: true,
134
- value: 0.7
134
+ value: void 0
135
135
  });
136
136
  Object.defineProperty(this, "maxOutputTokens", {
137
137
  enumerable: true,
138
138
  configurable: true,
139
139
  writable: true,
140
- value: 1024
140
+ value: void 0
141
141
  });
142
142
  Object.defineProperty(this, "topP", {
143
143
  enumerable: true,
144
144
  configurable: true,
145
145
  writable: true,
146
- value: 0.8
146
+ value: void 0
147
147
  });
148
148
  Object.defineProperty(this, "topK", {
149
149
  enumerable: true,
150
150
  configurable: true,
151
151
  writable: true,
152
- value: 40
152
+ value: void 0
153
153
  });
154
154
  Object.defineProperty(this, "presencePenalty", {
155
155
  enumerable: true,
@@ -246,13 +246,7 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
246
246
  return new auth_js_1.ApiKeyGoogleAuth(apiKey);
247
247
  }
248
248
  buildApiKey(fields) {
249
- if (fields?.platformType !== "gcp") {
250
- return fields?.apiKey ?? (0, env_1.getEnvironmentVariable)("GOOGLE_API_KEY");
251
- }
252
- else {
253
- // GCP doesn't support API Keys
254
- return undefined;
255
- }
249
+ return fields?.apiKey ?? (0, env_1.getEnvironmentVariable)("GOOGLE_API_KEY");
256
250
  }
257
251
  buildClient(fields) {
258
252
  const apiKey = this.buildApiKey(fields);
@@ -127,25 +127,25 @@ export class ChatGoogleBase extends BaseChatModel {
127
127
  enumerable: true,
128
128
  configurable: true,
129
129
  writable: true,
130
- value: 0.7
130
+ value: void 0
131
131
  });
132
132
  Object.defineProperty(this, "maxOutputTokens", {
133
133
  enumerable: true,
134
134
  configurable: true,
135
135
  writable: true,
136
- value: 1024
136
+ value: void 0
137
137
  });
138
138
  Object.defineProperty(this, "topP", {
139
139
  enumerable: true,
140
140
  configurable: true,
141
141
  writable: true,
142
- value: 0.8
142
+ value: void 0
143
143
  });
144
144
  Object.defineProperty(this, "topK", {
145
145
  enumerable: true,
146
146
  configurable: true,
147
147
  writable: true,
148
- value: 40
148
+ value: void 0
149
149
  });
150
150
  Object.defineProperty(this, "presencePenalty", {
151
151
  enumerable: true,
@@ -242,13 +242,7 @@ export class ChatGoogleBase extends BaseChatModel {
242
242
  return new ApiKeyGoogleAuth(apiKey);
243
243
  }
244
244
  buildApiKey(fields) {
245
- if (fields?.platformType !== "gcp") {
246
- return fields?.apiKey ?? getEnvironmentVariable("GOOGLE_API_KEY");
247
- }
248
- else {
249
- // GCP doesn't support API Keys
250
- return undefined;
251
- }
245
+ return fields?.apiKey ?? getEnvironmentVariable("GOOGLE_API_KEY");
252
246
  }
253
247
  buildClient(fields) {
254
248
  const apiKey = this.buildApiKey(fields);
@@ -223,8 +223,14 @@ class GoogleAIConnection extends GoogleHostConnection {
223
223
  throw new Error(`Unknown API: ${this.apiName}`);
224
224
  }
225
225
  }
226
+ get isApiKey() {
227
+ return this.client.clientType === "apiKey";
228
+ }
226
229
  get computedPlatformType() {
227
- if (this.client.clientType === "apiKey") {
230
+ // This is not a completely correct assumption, since GCP can
231
+ // have an API Key. But if so, then people need to set the platform
232
+ // type explicitly.
233
+ if (this.isApiKey) {
228
234
  return "gai";
229
235
  }
230
236
  else {
@@ -246,13 +252,27 @@ class GoogleAIConnection extends GoogleHostConnection {
246
252
  const url = `https://generativelanguage.googleapis.com/${this.apiVersion}/models/${this.model}:${method}`;
247
253
  return url;
248
254
  }
249
- async buildUrlVertex() {
255
+ async buildUrlVertexExpress() {
256
+ const method = await this.buildUrlMethod();
257
+ const publisher = this.modelPublisher;
258
+ const url = `https://aiplatform.googleapis.com/${this.apiVersion}/publishers/${publisher}/models/${this.model}:${method}`;
259
+ return url;
260
+ }
261
+ async buildUrlVertexLocation() {
250
262
  const projectId = await this.client.getProjectId();
251
263
  const method = await this.buildUrlMethod();
252
264
  const publisher = this.modelPublisher;
253
265
  const url = `https://${this.endpoint}/${this.apiVersion}/projects/${projectId}/locations/${this.location}/publishers/${publisher}/models/${this.model}:${method}`;
254
266
  return url;
255
267
  }
268
+ async buildUrlVertex() {
269
+ if (this.isApiKey) {
270
+ return this.buildUrlVertexExpress();
271
+ }
272
+ else {
273
+ return this.buildUrlVertexLocation();
274
+ }
275
+ }
256
276
  async buildUrl() {
257
277
  switch (this.platform) {
258
278
  case "gai":
@@ -50,10 +50,13 @@ export declare abstract class GoogleAIConnection<CallOptions extends AsyncCaller
50
50
  get computedAPIName(): string;
51
51
  get apiName(): string;
52
52
  get api(): GoogleAIAPI;
53
+ get isApiKey(): boolean;
53
54
  get computedPlatformType(): GooglePlatformType;
54
55
  get computedLocation(): string;
55
56
  abstract buildUrlMethod(): Promise<string>;
56
57
  buildUrlGenerativeLanguage(): Promise<string>;
58
+ buildUrlVertexExpress(): Promise<string>;
59
+ buildUrlVertexLocation(): Promise<string>;
57
60
  buildUrlVertex(): Promise<string>;
58
61
  buildUrl(): Promise<string>;
59
62
  abstract formatData(input: InputType, parameters: GoogleAIModelRequestParams): Promise<unknown>;
@@ -217,8 +217,14 @@ export class GoogleAIConnection extends GoogleHostConnection {
217
217
  throw new Error(`Unknown API: ${this.apiName}`);
218
218
  }
219
219
  }
220
+ get isApiKey() {
221
+ return this.client.clientType === "apiKey";
222
+ }
220
223
  get computedPlatformType() {
221
- if (this.client.clientType === "apiKey") {
224
+ // This is not a completely correct assumption, since GCP can
225
+ // have an API Key. But if so, then people need to set the platform
226
+ // type explicitly.
227
+ if (this.isApiKey) {
222
228
  return "gai";
223
229
  }
224
230
  else {
@@ -240,13 +246,27 @@ export class GoogleAIConnection extends GoogleHostConnection {
240
246
  const url = `https://generativelanguage.googleapis.com/${this.apiVersion}/models/${this.model}:${method}`;
241
247
  return url;
242
248
  }
243
- async buildUrlVertex() {
249
+ async buildUrlVertexExpress() {
250
+ const method = await this.buildUrlMethod();
251
+ const publisher = this.modelPublisher;
252
+ const url = `https://aiplatform.googleapis.com/${this.apiVersion}/publishers/${publisher}/models/${this.model}:${method}`;
253
+ return url;
254
+ }
255
+ async buildUrlVertexLocation() {
244
256
  const projectId = await this.client.getProjectId();
245
257
  const method = await this.buildUrlMethod();
246
258
  const publisher = this.modelPublisher;
247
259
  const url = `https://${this.endpoint}/${this.apiVersion}/projects/${projectId}/locations/${this.location}/publishers/${publisher}/models/${this.model}:${method}`;
248
260
  return url;
249
261
  }
262
+ async buildUrlVertex() {
263
+ if (this.isApiKey) {
264
+ return this.buildUrlVertexExpress();
265
+ }
266
+ else {
267
+ return this.buildUrlVertexLocation();
268
+ }
269
+ }
250
270
  async buildUrl() {
251
271
  switch (this.platform) {
252
272
  case "gai":
@@ -17,6 +17,15 @@ export interface AnthropicMessageContentImage extends AnthropicMessageContentBas
17
17
  data: string;
18
18
  };
19
19
  }
20
+ export interface AnthropicMessageContentThinking extends AnthropicMessageContentBase {
21
+ type: "thinking";
22
+ thinking: string;
23
+ signature: string;
24
+ }
25
+ export interface AnthropicMessageContentRedactedThinking extends AnthropicMessageContentBase {
26
+ type: "redacted_thinking";
27
+ data: string;
28
+ }
20
29
  export type AnthropicMessageContentToolUseInput = object;
21
30
  export interface AnthropicMessageContentToolUse extends AnthropicMessageContentBase {
22
31
  type: "tool_use";
@@ -31,7 +40,7 @@ export interface AnthropicMessageContentToolResult extends AnthropicMessageConte
31
40
  is_error?: boolean;
32
41
  content: string | AnthropicMessageContentToolResultContent[];
33
42
  }
34
- export type AnthropicMessageContent = AnthropicMessageContentText | AnthropicMessageContentImage | AnthropicMessageContentToolUse | AnthropicMessageContentToolResult;
43
+ export type AnthropicMessageContent = AnthropicMessageContentText | AnthropicMessageContentImage | AnthropicMessageContentToolUse | AnthropicMessageContentToolResult | AnthropicMessageContentThinking | AnthropicMessageContentRedactedThinking;
35
44
  export interface AnthropicMessage {
36
45
  role: string;
37
46
  content: string | AnthropicMessageContent[];
@@ -61,6 +70,14 @@ export interface AnthropicTool {
61
70
  cache_control?: AnthropicCacheControl;
62
71
  input_schema: AnthropicToolInputSchema;
63
72
  }
73
+ export interface AnthropicThinkingEnabled {
74
+ type: "enabled";
75
+ budget_tokens: number;
76
+ }
77
+ export interface AnthropicThinkingDisabled {
78
+ type: "disabled";
79
+ }
80
+ export type AnthropicThinking = AnthropicThinkingEnabled | AnthropicThinkingDisabled;
64
81
  export interface AnthropicRequest {
65
82
  anthropic_version: string;
66
83
  messages: AnthropicMessage[];
@@ -74,6 +91,7 @@ export interface AnthropicRequest {
74
91
  metadata?: AnthropicMetadata;
75
92
  tool_choice?: AnthropicToolChoice;
76
93
  tools?: AnthropicTool[];
94
+ thinking?: AnthropicThinking;
77
95
  }
78
96
  export type AnthropicRequestSettings = Pick<AnthropicRequest, "max_tokens" | "temperature" | "top_k" | "top_p" | "stop_sequences" | "stream">;
79
97
  export interface AnthropicContentText {
@@ -86,7 +104,16 @@ export interface AnthropicContentToolUse {
86
104
  name: string;
87
105
  input: object;
88
106
  }
89
- export type AnthropicContent = AnthropicContentText | AnthropicContentToolUse;
107
+ export interface AnthropicContentThinking {
108
+ type: "thinking";
109
+ thinking: string;
110
+ signature: string;
111
+ }
112
+ export interface AnthropicContentRedactedThinking {
113
+ type: "redacted_thinking";
114
+ data: string;
115
+ }
116
+ export type AnthropicContent = AnthropicContentText | AnthropicContentToolUse | AnthropicContentThinking | AnthropicContentRedactedThinking;
90
117
  export interface AnthropicUsage {
91
118
  input_tokens: number;
92
119
  output_tokens: number;
@@ -106,6 +133,7 @@ export interface AnthropicResponseMessage {
106
133
  }
107
134
  export interface AnthropicAPIConfig {
108
135
  version?: string;
136
+ thinking?: AnthropicThinking;
109
137
  }
110
138
  export type AnthropicStreamEventType = "message_start" | "content_block_start" | "content_block_delta" | "content_block_stop" | "message_delta" | "message_stop" | "ping" | "error";
111
139
  export type AnthropicStreamDeltaType = "text_delta" | "input_json_delta";
@@ -70,6 +70,18 @@ function getAnthropicAPI(config) {
70
70
  tool_calls: [tool],
71
71
  };
72
72
  }
73
+ function thinkingContentToMessageFields(thinkingContent) {
74
+ // TODO: Once a reasoning/thinking type is defined in LangChain, use it
75
+ return {
76
+ content: [thinkingContent],
77
+ };
78
+ }
79
+ function redactedThinkingContentToMessageFields(thinkingContent) {
80
+ // TODO: Once a reasoning/thinking type is defined in LangChain, use it
81
+ return {
82
+ content: [thinkingContent],
83
+ };
84
+ }
73
85
  function anthropicContentToMessageFields(anthropicContent) {
74
86
  const type = anthropicContent?.type;
75
87
  switch (type) {
@@ -77,7 +89,12 @@ function getAnthropicAPI(config) {
77
89
  return textContentToMessageFields(anthropicContent);
78
90
  case "tool_use":
79
91
  return toolUseContentToMessageFields(anthropicContent);
92
+ case "thinking":
93
+ return thinkingContentToMessageFields(anthropicContent);
94
+ case "redacted_thinking":
95
+ return redactedThinkingContentToMessageFields(anthropicContent);
80
96
  default:
97
+ console.error(`Unknown message type: ${type}`, anthropicContent);
81
98
  return undefined;
82
99
  }
83
100
  }
@@ -321,6 +338,25 @@ function getAnthropicAPI(config) {
321
338
  },
322
339
  };
323
340
  }
341
+ function thinkingContentToAnthropicContent(
342
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
343
+ content) {
344
+ // TODO: Once a Langchain Thinking type is defined, use it
345
+ return {
346
+ type: "thinking",
347
+ thinking: content.thinking,
348
+ signature: content.signature,
349
+ };
350
+ }
351
+ function redactedThinkingContentToAnthropicContent(
352
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
353
+ content) {
354
+ // TODO: Once a Langchain Thinking type is defined, use it
355
+ return {
356
+ type: "redacted_thinking",
357
+ data: content.data,
358
+ };
359
+ }
324
360
  function contentComplexToAnthropicContent(content) {
325
361
  const type = content?.type;
326
362
  switch (type) {
@@ -328,8 +364,12 @@ function getAnthropicAPI(config) {
328
364
  return textContentToAnthropicContent(content);
329
365
  case "image_url":
330
366
  return imageContentToAnthropicContent(content);
367
+ case "thinking":
368
+ return thinkingContentToAnthropicContent(content);
369
+ case "redacted_thinking":
370
+ return redactedThinkingContentToAnthropicContent(content);
331
371
  default:
332
- console.warn(`Unexpected content type: ${type}`);
372
+ console.warn(`Unexpected content type: ${type}`, content);
333
373
  return undefined;
334
374
  }
335
375
  }
@@ -344,6 +384,20 @@ function getAnthropicAPI(config) {
344
384
  });
345
385
  return ret;
346
386
  }
387
+ function toolCallToAnthropicContent(toolCall) {
388
+ return {
389
+ type: "tool_use",
390
+ id: toolCall.id,
391
+ name: toolCall.name,
392
+ input: toolCall.args,
393
+ };
394
+ }
395
+ function toolCallsToAnthropicContent(toolCalls) {
396
+ if (toolCalls === undefined) {
397
+ return [];
398
+ }
399
+ return toolCalls.map(toolCallToAnthropicContent);
400
+ }
347
401
  function baseRoleToAnthropicMessage(base, role) {
348
402
  const content = contentToAnthropicContent(base.content);
349
403
  return {
@@ -351,6 +405,15 @@ function getAnthropicAPI(config) {
351
405
  content,
352
406
  };
353
407
  }
408
+ function aiMessageToAnthropicMessage(base) {
409
+ const ret = baseRoleToAnthropicMessage(base, "assistant");
410
+ const toolContent = toolCallsToAnthropicContent(base.tool_calls);
411
+ if (toolContent.length > 0) {
412
+ const content = ret.content;
413
+ ret.content = [...content, ...toolContent];
414
+ }
415
+ return ret;
416
+ }
354
417
  function toolMessageToAnthropicMessage(base) {
355
418
  const role = "user";
356
419
  const toolUseId = base.tool_call_id;
@@ -373,10 +436,11 @@ function getAnthropicAPI(config) {
373
436
  case "human":
374
437
  return baseRoleToAnthropicMessage(base, "user");
375
438
  case "ai":
376
- return baseRoleToAnthropicMessage(base, "assistant");
439
+ return aiMessageToAnthropicMessage(base);
377
440
  case "tool":
378
441
  return toolMessageToAnthropicMessage(base);
379
442
  default:
443
+ console.warn(`Unknown BaseMessage type: ${type}`, base);
380
444
  return undefined;
381
445
  }
382
446
  }
@@ -519,6 +583,9 @@ function getAnthropicAPI(config) {
519
583
  if (system?.length) {
520
584
  ret.system = system;
521
585
  }
586
+ if (config?.thinking) {
587
+ ret.thinking = config?.thinking;
588
+ }
522
589
  return ret;
523
590
  }
524
591
  return {
@@ -67,6 +67,18 @@ export function getAnthropicAPI(config) {
67
67
  tool_calls: [tool],
68
68
  };
69
69
  }
70
+ function thinkingContentToMessageFields(thinkingContent) {
71
+ // TODO: Once a reasoning/thinking type is defined in LangChain, use it
72
+ return {
73
+ content: [thinkingContent],
74
+ };
75
+ }
76
+ function redactedThinkingContentToMessageFields(thinkingContent) {
77
+ // TODO: Once a reasoning/thinking type is defined in LangChain, use it
78
+ return {
79
+ content: [thinkingContent],
80
+ };
81
+ }
70
82
  function anthropicContentToMessageFields(anthropicContent) {
71
83
  const type = anthropicContent?.type;
72
84
  switch (type) {
@@ -74,7 +86,12 @@ export function getAnthropicAPI(config) {
74
86
  return textContentToMessageFields(anthropicContent);
75
87
  case "tool_use":
76
88
  return toolUseContentToMessageFields(anthropicContent);
89
+ case "thinking":
90
+ return thinkingContentToMessageFields(anthropicContent);
91
+ case "redacted_thinking":
92
+ return redactedThinkingContentToMessageFields(anthropicContent);
77
93
  default:
94
+ console.error(`Unknown message type: ${type}`, anthropicContent);
78
95
  return undefined;
79
96
  }
80
97
  }
@@ -318,6 +335,25 @@ export function getAnthropicAPI(config) {
318
335
  },
319
336
  };
320
337
  }
338
+ function thinkingContentToAnthropicContent(
339
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
340
+ content) {
341
+ // TODO: Once a Langchain Thinking type is defined, use it
342
+ return {
343
+ type: "thinking",
344
+ thinking: content.thinking,
345
+ signature: content.signature,
346
+ };
347
+ }
348
+ function redactedThinkingContentToAnthropicContent(
349
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
350
+ content) {
351
+ // TODO: Once a Langchain Thinking type is defined, use it
352
+ return {
353
+ type: "redacted_thinking",
354
+ data: content.data,
355
+ };
356
+ }
321
357
  function contentComplexToAnthropicContent(content) {
322
358
  const type = content?.type;
323
359
  switch (type) {
@@ -325,8 +361,12 @@ export function getAnthropicAPI(config) {
325
361
  return textContentToAnthropicContent(content);
326
362
  case "image_url":
327
363
  return imageContentToAnthropicContent(content);
364
+ case "thinking":
365
+ return thinkingContentToAnthropicContent(content);
366
+ case "redacted_thinking":
367
+ return redactedThinkingContentToAnthropicContent(content);
328
368
  default:
329
- console.warn(`Unexpected content type: ${type}`);
369
+ console.warn(`Unexpected content type: ${type}`, content);
330
370
  return undefined;
331
371
  }
332
372
  }
@@ -341,6 +381,20 @@ export function getAnthropicAPI(config) {
341
381
  });
342
382
  return ret;
343
383
  }
384
+ function toolCallToAnthropicContent(toolCall) {
385
+ return {
386
+ type: "tool_use",
387
+ id: toolCall.id,
388
+ name: toolCall.name,
389
+ input: toolCall.args,
390
+ };
391
+ }
392
+ function toolCallsToAnthropicContent(toolCalls) {
393
+ if (toolCalls === undefined) {
394
+ return [];
395
+ }
396
+ return toolCalls.map(toolCallToAnthropicContent);
397
+ }
344
398
  function baseRoleToAnthropicMessage(base, role) {
345
399
  const content = contentToAnthropicContent(base.content);
346
400
  return {
@@ -348,6 +402,15 @@ export function getAnthropicAPI(config) {
348
402
  content,
349
403
  };
350
404
  }
405
+ function aiMessageToAnthropicMessage(base) {
406
+ const ret = baseRoleToAnthropicMessage(base, "assistant");
407
+ const toolContent = toolCallsToAnthropicContent(base.tool_calls);
408
+ if (toolContent.length > 0) {
409
+ const content = ret.content;
410
+ ret.content = [...content, ...toolContent];
411
+ }
412
+ return ret;
413
+ }
351
414
  function toolMessageToAnthropicMessage(base) {
352
415
  const role = "user";
353
416
  const toolUseId = base.tool_call_id;
@@ -370,10 +433,11 @@ export function getAnthropicAPI(config) {
370
433
  case "human":
371
434
  return baseRoleToAnthropicMessage(base, "user");
372
435
  case "ai":
373
- return baseRoleToAnthropicMessage(base, "assistant");
436
+ return aiMessageToAnthropicMessage(base);
374
437
  case "tool":
375
438
  return toolMessageToAnthropicMessage(base);
376
439
  default:
440
+ console.warn(`Unknown BaseMessage type: ${type}`, base);
377
441
  return undefined;
378
442
  }
379
443
  }
@@ -516,6 +580,9 @@ export function getAnthropicAPI(config) {
516
580
  if (system?.length) {
517
581
  ret.system = system;
518
582
  }
583
+ if (config?.thinking) {
584
+ ret.thinking = config?.thinking;
585
+ }
519
586
  return ret;
520
587
  }
521
588
  return {
@@ -5,6 +5,7 @@ const uuid_1 = require("uuid");
5
5
  const messages_1 = require("@langchain/core/messages");
6
6
  const outputs_1 = require("@langchain/core/outputs");
7
7
  const function_calling_1 = require("@langchain/core/utils/function_calling");
8
+ const stream_1 = require("@langchain/core/utils/stream");
8
9
  const safety_js_1 = require("./safety.cjs");
9
10
  const types_js_1 = require("../types.cjs");
10
11
  const zod_to_gemini_parameters_js_1 = require("./zod_to_gemini_parameters.cjs");
@@ -604,9 +605,12 @@ function getGeminiAPI(config) {
604
605
  function partToChatGeneration(part) {
605
606
  const message = partToMessageChunk(part);
606
607
  const text = partToText(part);
608
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
609
+ const generationInfo = {};
607
610
  return new outputs_1.ChatGenerationChunk({
608
611
  text,
609
612
  message,
613
+ generationInfo,
610
614
  });
611
615
  }
612
616
  function groundingSupportByPart(groundingSupports) {
@@ -657,43 +661,134 @@ function getGeminiAPI(config) {
657
661
  });
658
662
  return ret;
659
663
  }
664
+ function combineContent(gen, forceComplex = false) {
665
+ const allString = gen.every((item) => typeof item.message.content === "string");
666
+ if (allString && !forceComplex) {
667
+ // Everything is a string, and we don't want to force it to return
668
+ // MessageContentComplex[], so concatenate the content into one string
669
+ return gen.map((item) => item.message.content).join("");
670
+ }
671
+ else {
672
+ // We either have complex types, or we want to force them, so turn
673
+ // it into an array of complex types.
674
+ const ret = [];
675
+ gen.forEach((item) => {
676
+ if (typeof item.message.content === "string") {
677
+ // If this is a string, turn it into a text type
678
+ ret.push({
679
+ text: item.message.content,
680
+ });
681
+ }
682
+ else {
683
+ // Otherwise, add all the complex types to what we're returning
684
+ item.message.content.forEach((c) => {
685
+ ret.push(c);
686
+ });
687
+ }
688
+ });
689
+ return ret;
690
+ }
691
+ }
692
+ function combineText(gen) {
693
+ return gen.map((item) => item.text ?? "").join("");
694
+ }
695
+ /*
696
+ * We don't really need the entire AIMessageChunk here, but it is
697
+ * a conventient way to combine all the Tool Calling information.
698
+ */
699
+ function combineToolCalls(gen) {
700
+ let ret = new messages_1.AIMessageChunk("");
701
+ gen.forEach((item) => {
702
+ const message = item?.message;
703
+ ret = (0, stream_1.concat)(ret, message);
704
+ });
705
+ return ret;
706
+ }
707
+ function combineAdditionalKwargs(gen) {
708
+ const ret = {};
709
+ gen.forEach((item) => {
710
+ const message = item?.message;
711
+ const kwargs = message?.additional_kwargs ?? {};
712
+ const keys = Object.keys(kwargs);
713
+ keys.forEach((key) => {
714
+ const value = kwargs[key];
715
+ if (Object.hasOwn(ret, key) &&
716
+ Array.isArray(ret[key]) &&
717
+ Array.isArray(value)) {
718
+ ret[key].push(...value);
719
+ }
720
+ else {
721
+ ret[key] = value;
722
+ }
723
+ });
724
+ });
725
+ return ret;
726
+ }
727
+ function combineGenerations(generations, response) {
728
+ const gen = splitGenerationTypes(generations, response);
729
+ const combinedContent = combineContent(gen.content);
730
+ const combinedText = combineText(gen.content);
731
+ const combinedToolCalls = combineToolCalls(gen.content);
732
+ const kwargs = combineAdditionalKwargs(gen.content);
733
+ const lastContent = gen.content[gen.content.length - 1];
734
+ // Add usage metadata
735
+ let usageMetadata;
736
+ if ("usageMetadata" in response.data) {
737
+ usageMetadata = {
738
+ input_tokens: response.data.usageMetadata.promptTokenCount,
739
+ output_tokens: response.data.usageMetadata
740
+ .candidatesTokenCount,
741
+ total_tokens: response.data.usageMetadata.totalTokenCount,
742
+ };
743
+ }
744
+ // Add thinking / reasoning
745
+ // if (gen.reasoning && gen.reasoning.length > 0) {
746
+ // kwargs.reasoning_content = combineContent(gen.reasoning, true);
747
+ // }
748
+ // Build the message and the generation chunk to return
749
+ const message = new messages_1.AIMessageChunk({
750
+ content: combinedContent,
751
+ additional_kwargs: kwargs,
752
+ usage_metadata: usageMetadata,
753
+ tool_calls: combinedToolCalls.tool_calls,
754
+ invalid_tool_calls: combinedToolCalls.invalid_tool_calls,
755
+ });
756
+ return [
757
+ new outputs_1.ChatGenerationChunk({
758
+ message,
759
+ text: combinedText,
760
+ generationInfo: lastContent.generationInfo,
761
+ }),
762
+ ];
763
+ }
764
+ function splitGenerationTypes(generations, _response) {
765
+ const content = [];
766
+ const reasoning = [];
767
+ generations.forEach((gen) => {
768
+ if (gen?.generationInfo?.thought) {
769
+ reasoning.push(gen);
770
+ }
771
+ else {
772
+ content.push(gen);
773
+ }
774
+ });
775
+ return {
776
+ content,
777
+ reasoning,
778
+ };
779
+ }
780
+ /**
781
+ * Although this returns an array, only the first (or maybe last)
782
+ * element in the array is used. So we need to combine them into
783
+ * just one element that contains everything we need.
784
+ * @param response
785
+ */
660
786
  function responseToChatGenerations(response) {
661
- let ret = responseToGroundedChatGenerations(response);
662
- if (ret.length === 0) {
787
+ const generations = responseToGroundedChatGenerations(response);
788
+ if (generations.length === 0) {
663
789
  return [];
664
790
  }
665
- if (ret.every((item) => typeof item.message.content === "string")) {
666
- const combinedContent = ret.map((item) => item.message.content).join("");
667
- const combinedText = ret.map((item) => item.text).join("");
668
- const toolCallChunks = ret[ret.length - 1]?.message.additional_kwargs?.tool_calls?.map((toolCall, i) => ({
669
- name: toolCall.function.name,
670
- args: toolCall.function.arguments,
671
- id: toolCall.id,
672
- index: i,
673
- type: "tool_call_chunk",
674
- }));
675
- let usageMetadata;
676
- if ("usageMetadata" in response.data) {
677
- usageMetadata = {
678
- input_tokens: response.data.usageMetadata.promptTokenCount,
679
- output_tokens: response.data.usageMetadata
680
- .candidatesTokenCount,
681
- total_tokens: response.data.usageMetadata.totalTokenCount,
682
- };
683
- }
684
- ret = [
685
- new outputs_1.ChatGenerationChunk({
686
- message: new messages_1.AIMessageChunk({
687
- content: combinedContent,
688
- additional_kwargs: ret[ret.length - 1]?.message.additional_kwargs,
689
- tool_call_chunks: toolCallChunks,
690
- usage_metadata: usageMetadata,
691
- }),
692
- text: combinedText,
693
- generationInfo: ret[ret.length - 1].generationInfo,
694
- }),
695
- ];
696
- }
791
+ const ret = combineGenerations(generations, response);
697
792
  // Add logprobs information to the message
698
793
  const candidate = response?.data
699
794
  ?.candidates?.[0];
@@ -847,6 +942,13 @@ function getGeminiAPI(config) {
847
942
  ret.logprobs = parameters.topLogprobs;
848
943
  }
849
944
  }
945
+ // Remove any undefined properties, so we don't send them
946
+ let attribute;
947
+ for (attribute in ret) {
948
+ if (ret[attribute] === undefined) {
949
+ delete ret[attribute];
950
+ }
951
+ }
850
952
  return ret;
851
953
  }
852
954
  function formatSafetySettings(parameters) {
@@ -2,6 +2,7 @@ import { v4 as uuidv4 } from "uuid";
2
2
  import { AIMessage, AIMessageChunk, isAIMessage, } from "@langchain/core/messages";
3
3
  import { ChatGenerationChunk, } from "@langchain/core/outputs";
4
4
  import { isLangChainTool } from "@langchain/core/utils/function_calling";
5
+ import { concat } from "@langchain/core/utils/stream";
5
6
  import { GoogleAISafetyError } from "./safety.js";
6
7
  import { GeminiSearchToolAttributes, } from "../types.js";
7
8
  import { zodToGeminiParameters } from "./zod_to_gemini_parameters.js";
@@ -599,9 +600,12 @@ export function getGeminiAPI(config) {
599
600
  function partToChatGeneration(part) {
600
601
  const message = partToMessageChunk(part);
601
602
  const text = partToText(part);
603
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
604
+ const generationInfo = {};
602
605
  return new ChatGenerationChunk({
603
606
  text,
604
607
  message,
608
+ generationInfo,
605
609
  });
606
610
  }
607
611
  function groundingSupportByPart(groundingSupports) {
@@ -652,43 +656,134 @@ export function getGeminiAPI(config) {
652
656
  });
653
657
  return ret;
654
658
  }
659
+ function combineContent(gen, forceComplex = false) {
660
+ const allString = gen.every((item) => typeof item.message.content === "string");
661
+ if (allString && !forceComplex) {
662
+ // Everything is a string, and we don't want to force it to return
663
+ // MessageContentComplex[], so concatenate the content into one string
664
+ return gen.map((item) => item.message.content).join("");
665
+ }
666
+ else {
667
+ // We either have complex types, or we want to force them, so turn
668
+ // it into an array of complex types.
669
+ const ret = [];
670
+ gen.forEach((item) => {
671
+ if (typeof item.message.content === "string") {
672
+ // If this is a string, turn it into a text type
673
+ ret.push({
674
+ text: item.message.content,
675
+ });
676
+ }
677
+ else {
678
+ // Otherwise, add all the complex types to what we're returning
679
+ item.message.content.forEach((c) => {
680
+ ret.push(c);
681
+ });
682
+ }
683
+ });
684
+ return ret;
685
+ }
686
+ }
687
+ function combineText(gen) {
688
+ return gen.map((item) => item.text ?? "").join("");
689
+ }
690
+ /*
691
+ * We don't really need the entire AIMessageChunk here, but it is
692
+ * a conventient way to combine all the Tool Calling information.
693
+ */
694
+ function combineToolCalls(gen) {
695
+ let ret = new AIMessageChunk("");
696
+ gen.forEach((item) => {
697
+ const message = item?.message;
698
+ ret = concat(ret, message);
699
+ });
700
+ return ret;
701
+ }
702
+ function combineAdditionalKwargs(gen) {
703
+ const ret = {};
704
+ gen.forEach((item) => {
705
+ const message = item?.message;
706
+ const kwargs = message?.additional_kwargs ?? {};
707
+ const keys = Object.keys(kwargs);
708
+ keys.forEach((key) => {
709
+ const value = kwargs[key];
710
+ if (Object.hasOwn(ret, key) &&
711
+ Array.isArray(ret[key]) &&
712
+ Array.isArray(value)) {
713
+ ret[key].push(...value);
714
+ }
715
+ else {
716
+ ret[key] = value;
717
+ }
718
+ });
719
+ });
720
+ return ret;
721
+ }
722
+ function combineGenerations(generations, response) {
723
+ const gen = splitGenerationTypes(generations, response);
724
+ const combinedContent = combineContent(gen.content);
725
+ const combinedText = combineText(gen.content);
726
+ const combinedToolCalls = combineToolCalls(gen.content);
727
+ const kwargs = combineAdditionalKwargs(gen.content);
728
+ const lastContent = gen.content[gen.content.length - 1];
729
+ // Add usage metadata
730
+ let usageMetadata;
731
+ if ("usageMetadata" in response.data) {
732
+ usageMetadata = {
733
+ input_tokens: response.data.usageMetadata.promptTokenCount,
734
+ output_tokens: response.data.usageMetadata
735
+ .candidatesTokenCount,
736
+ total_tokens: response.data.usageMetadata.totalTokenCount,
737
+ };
738
+ }
739
+ // Add thinking / reasoning
740
+ // if (gen.reasoning && gen.reasoning.length > 0) {
741
+ // kwargs.reasoning_content = combineContent(gen.reasoning, true);
742
+ // }
743
+ // Build the message and the generation chunk to return
744
+ const message = new AIMessageChunk({
745
+ content: combinedContent,
746
+ additional_kwargs: kwargs,
747
+ usage_metadata: usageMetadata,
748
+ tool_calls: combinedToolCalls.tool_calls,
749
+ invalid_tool_calls: combinedToolCalls.invalid_tool_calls,
750
+ });
751
+ return [
752
+ new ChatGenerationChunk({
753
+ message,
754
+ text: combinedText,
755
+ generationInfo: lastContent.generationInfo,
756
+ }),
757
+ ];
758
+ }
759
+ function splitGenerationTypes(generations, _response) {
760
+ const content = [];
761
+ const reasoning = [];
762
+ generations.forEach((gen) => {
763
+ if (gen?.generationInfo?.thought) {
764
+ reasoning.push(gen);
765
+ }
766
+ else {
767
+ content.push(gen);
768
+ }
769
+ });
770
+ return {
771
+ content,
772
+ reasoning,
773
+ };
774
+ }
775
+ /**
776
+ * Although this returns an array, only the first (or maybe last)
777
+ * element in the array is used. So we need to combine them into
778
+ * just one element that contains everything we need.
779
+ * @param response
780
+ */
655
781
  function responseToChatGenerations(response) {
656
- let ret = responseToGroundedChatGenerations(response);
657
- if (ret.length === 0) {
782
+ const generations = responseToGroundedChatGenerations(response);
783
+ if (generations.length === 0) {
658
784
  return [];
659
785
  }
660
- if (ret.every((item) => typeof item.message.content === "string")) {
661
- const combinedContent = ret.map((item) => item.message.content).join("");
662
- const combinedText = ret.map((item) => item.text).join("");
663
- const toolCallChunks = ret[ret.length - 1]?.message.additional_kwargs?.tool_calls?.map((toolCall, i) => ({
664
- name: toolCall.function.name,
665
- args: toolCall.function.arguments,
666
- id: toolCall.id,
667
- index: i,
668
- type: "tool_call_chunk",
669
- }));
670
- let usageMetadata;
671
- if ("usageMetadata" in response.data) {
672
- usageMetadata = {
673
- input_tokens: response.data.usageMetadata.promptTokenCount,
674
- output_tokens: response.data.usageMetadata
675
- .candidatesTokenCount,
676
- total_tokens: response.data.usageMetadata.totalTokenCount,
677
- };
678
- }
679
- ret = [
680
- new ChatGenerationChunk({
681
- message: new AIMessageChunk({
682
- content: combinedContent,
683
- additional_kwargs: ret[ret.length - 1]?.message.additional_kwargs,
684
- tool_call_chunks: toolCallChunks,
685
- usage_metadata: usageMetadata,
686
- }),
687
- text: combinedText,
688
- generationInfo: ret[ret.length - 1].generationInfo,
689
- }),
690
- ];
691
- }
786
+ const ret = combineGenerations(generations, response);
692
787
  // Add logprobs information to the message
693
788
  const candidate = response?.data
694
789
  ?.candidates?.[0];
@@ -842,6 +937,13 @@ export function getGeminiAPI(config) {
842
937
  ret.logprobs = parameters.topLogprobs;
843
938
  }
844
939
  }
940
+ // Remove any undefined properties, so we don't send them
941
+ let attribute;
942
+ for (attribute in ret) {
943
+ if (ret[attribute] === undefined) {
944
+ delete ret[attribute];
945
+ }
946
+ }
845
947
  return ret;
846
948
  }
847
949
  function formatSafetySettings(parameters) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/google-common",
3
- "version": "0.1.8",
3
+ "version": "0.2.1",
4
4
  "description": "Core types and classes for Google services.",
5
5
  "type": "module",
6
6
  "engines": {