@langchain/google-common 0.2.8 → 0.2.10

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.
@@ -162,6 +162,12 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
162
162
  writable: true,
163
163
  value: void 0
164
164
  });
165
+ Object.defineProperty(this, "seed", {
166
+ enumerable: true,
167
+ configurable: true,
168
+ writable: true,
169
+ value: void 0
170
+ });
165
171
  Object.defineProperty(this, "presencePenalty", {
166
172
  enumerable: true,
167
173
  configurable: true,
@@ -282,7 +288,7 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
282
288
  return this.connection.platform;
283
289
  }
284
290
  bindTools(tools, kwargs) {
285
- return this.bind({ tools: (0, common_js_1.convertToGeminiTools)(tools), ...kwargs });
291
+ return this.withConfig({ tools: (0, common_js_1.convertToGeminiTools)(tools), ...kwargs });
286
292
  }
287
293
  // Replace
288
294
  _llmType() {
@@ -418,10 +424,7 @@ class ChatGoogleBase extends chat_models_1.BaseChatModel {
418
424
  keyName: functionName,
419
425
  });
420
426
  }
421
- const llm = this.bind({
422
- tools,
423
- tool_choice: functionName,
424
- });
427
+ const llm = this.bindTools(tools).withConfig({ tool_choice: functionName });
425
428
  if (!includeRaw) {
426
429
  return llm.pipe(outputParser).withConfig({
427
430
  runName: "ChatGoogleStructuredOutput",
@@ -42,6 +42,7 @@ export declare abstract class ChatGoogleBase<AuthOptions> extends BaseChatModel<
42
42
  maxReasoningTokens: number;
43
43
  topP: number;
44
44
  topK: number;
45
+ seed: number;
45
46
  presencePenalty: number;
46
47
  frequencyPenalty: number;
47
48
  stopSequences: string[];
@@ -158,6 +158,12 @@ export class ChatGoogleBase extends BaseChatModel {
158
158
  writable: true,
159
159
  value: void 0
160
160
  });
161
+ Object.defineProperty(this, "seed", {
162
+ enumerable: true,
163
+ configurable: true,
164
+ writable: true,
165
+ value: void 0
166
+ });
161
167
  Object.defineProperty(this, "presencePenalty", {
162
168
  enumerable: true,
163
169
  configurable: true,
@@ -278,7 +284,7 @@ export class ChatGoogleBase extends BaseChatModel {
278
284
  return this.connection.platform;
279
285
  }
280
286
  bindTools(tools, kwargs) {
281
- return this.bind({ tools: convertToGeminiTools(tools), ...kwargs });
287
+ return this.withConfig({ tools: convertToGeminiTools(tools), ...kwargs });
282
288
  }
283
289
  // Replace
284
290
  _llmType() {
@@ -414,10 +420,7 @@ export class ChatGoogleBase extends BaseChatModel {
414
420
  keyName: functionName,
415
421
  });
416
422
  }
417
- const llm = this.bind({
418
- tools,
419
- tool_choice: functionName,
420
- });
423
+ const llm = this.bindTools(tools).withConfig({ tool_choice: functionName });
421
424
  if (!includeRaw) {
422
425
  return llm.pipe(outputParser).withConfig({
423
426
  runName: "ChatGoogleStructuredOutput",
@@ -121,12 +121,24 @@ class GoogleHostConnection extends GoogleConnection {
121
121
  value: void 0
122
122
  });
123
123
  this.caller = caller;
124
- this.platformType = fields?.platformType;
124
+ this.platformType = this.fieldPlatformType(fields);
125
125
  this._endpoint = fields?.endpoint;
126
126
  this._location = fields?.location;
127
127
  this._apiVersion = fields?.apiVersion;
128
128
  this.client = client;
129
129
  }
130
+ fieldPlatformType(fields) {
131
+ if (typeof fields === "undefined") {
132
+ return undefined;
133
+ }
134
+ if (typeof fields.platformType !== "undefined") {
135
+ return fields.platformType;
136
+ }
137
+ if (fields.vertexai === true) {
138
+ return "gcp";
139
+ }
140
+ return undefined;
141
+ }
130
142
  get platform() {
131
143
  return this.platformType ?? this.computedPlatformType;
132
144
  }
@@ -149,7 +161,12 @@ class GoogleHostConnection extends GoogleConnection {
149
161
  return this._endpoint ?? this.computedEndpoint;
150
162
  }
151
163
  get computedEndpoint() {
152
- return `${this.location}-aiplatform.googleapis.com`;
164
+ if (this.location === "global") {
165
+ return "aiplatform.googleapis.com";
166
+ }
167
+ else {
168
+ return `${this.location}-aiplatform.googleapis.com`;
169
+ }
153
170
  }
154
171
  buildMethod() {
155
172
  return "POST";
@@ -233,6 +250,16 @@ class GoogleAIConnection extends GoogleHostConnection {
233
250
  get isApiKey() {
234
251
  return this.client.clientType === "apiKey";
235
252
  }
253
+ fieldPlatformType(fields) {
254
+ const ret = super.fieldPlatformType(fields);
255
+ if (typeof ret !== "undefined") {
256
+ return ret;
257
+ }
258
+ if (fields?.vertexai === false) {
259
+ return "gai";
260
+ }
261
+ return undefined;
262
+ }
236
263
  get computedPlatformType() {
237
264
  // This is not a completely correct assumption, since GCP can
238
265
  // have an API Key. But if so, then people need to set the platform
@@ -27,6 +27,7 @@ export declare abstract class GoogleHostConnection<CallOptions extends AsyncCall
27
27
  _location: string | undefined;
28
28
  _apiVersion: string | undefined;
29
29
  constructor(fields: GoogleConnectionParams<AuthOptions> | undefined, caller: AsyncCaller, client: GoogleAbstractedClient, streaming?: boolean);
30
+ fieldPlatformType(fields: GoogleConnectionParams<any> | undefined): GooglePlatformType | undefined;
30
31
  get platform(): GooglePlatformType;
31
32
  get computedPlatformType(): GooglePlatformType;
32
33
  get computedApiVersion(): string;
@@ -53,6 +54,7 @@ export declare abstract class GoogleAIConnection<CallOptions extends AsyncCaller
53
54
  get apiName(): string;
54
55
  get api(): GoogleAIAPI;
55
56
  get isApiKey(): boolean;
57
+ fieldPlatformType(fields: GoogleConnectionParams<any> | undefined): GooglePlatformType | undefined;
56
58
  get computedPlatformType(): GooglePlatformType;
57
59
  get computedApiVersion(): string;
58
60
  get computedLocation(): string;
@@ -117,12 +117,24 @@ export class GoogleHostConnection extends GoogleConnection {
117
117
  value: void 0
118
118
  });
119
119
  this.caller = caller;
120
- this.platformType = fields?.platformType;
120
+ this.platformType = this.fieldPlatformType(fields);
121
121
  this._endpoint = fields?.endpoint;
122
122
  this._location = fields?.location;
123
123
  this._apiVersion = fields?.apiVersion;
124
124
  this.client = client;
125
125
  }
126
+ fieldPlatformType(fields) {
127
+ if (typeof fields === "undefined") {
128
+ return undefined;
129
+ }
130
+ if (typeof fields.platformType !== "undefined") {
131
+ return fields.platformType;
132
+ }
133
+ if (fields.vertexai === true) {
134
+ return "gcp";
135
+ }
136
+ return undefined;
137
+ }
126
138
  get platform() {
127
139
  return this.platformType ?? this.computedPlatformType;
128
140
  }
@@ -145,7 +157,12 @@ export class GoogleHostConnection extends GoogleConnection {
145
157
  return this._endpoint ?? this.computedEndpoint;
146
158
  }
147
159
  get computedEndpoint() {
148
- return `${this.location}-aiplatform.googleapis.com`;
160
+ if (this.location === "global") {
161
+ return "aiplatform.googleapis.com";
162
+ }
163
+ else {
164
+ return `${this.location}-aiplatform.googleapis.com`;
165
+ }
149
166
  }
150
167
  buildMethod() {
151
168
  return "POST";
@@ -227,6 +244,16 @@ export class GoogleAIConnection extends GoogleHostConnection {
227
244
  get isApiKey() {
228
245
  return this.client.clientType === "apiKey";
229
246
  }
247
+ fieldPlatformType(fields) {
248
+ const ret = super.fieldPlatformType(fields);
249
+ if (typeof ret !== "undefined") {
250
+ return ret;
251
+ }
252
+ if (fields?.vertexai === false) {
253
+ return "gai";
254
+ }
255
+ return undefined;
256
+ }
230
257
  get computedPlatformType() {
231
258
  // This is not a completely correct assumption, since GCP can
232
259
  // have an API Key. But if so, then people need to set the platform
package/dist/types.cjs CHANGED
@@ -61,5 +61,6 @@ exports.GeminiSearchToolAttributes = [
61
61
  exports.GeminiToolAttributes = [
62
62
  "functionDeclaration",
63
63
  "retrieval",
64
+ "urlContext",
64
65
  ...exports.GeminiSearchToolAttributes,
65
66
  ];
package/dist/types.d.ts CHANGED
@@ -36,6 +36,11 @@ export interface GoogleConnectionParams<AuthOptions> extends GoogleClientParams<
36
36
  * the "platform" getter.
37
37
  */
38
38
  platformType?: GooglePlatformType;
39
+ /**
40
+ * For compatibility with Google's libraries, should this use Vertex?
41
+ * The "platformType" parmeter takes precedence.
42
+ */
43
+ vertexai?: boolean;
39
44
  }
40
45
  export declare const GoogleAISafetyCategory: {
41
46
  readonly Harassment: "HARM_CATEGORY_HARASSMENT";
@@ -137,6 +142,10 @@ export interface GoogleAIModelParams {
137
142
  * among the 3 most probable tokens (using temperature).
138
143
  */
139
144
  topK?: number;
145
+ /**
146
+ * Seed used in decoding. If not set, the request uses a randomly generated seed.
147
+ */
148
+ seed?: number;
140
149
  /**
141
150
  * Presence penalty applied to the next token's logprobs
142
151
  * if the token has already been seen in the response.
@@ -257,28 +266,39 @@ export interface GoogleResponse {
257
266
  export interface GoogleRawResponse extends GoogleResponse {
258
267
  data: Blob;
259
268
  }
260
- export interface GeminiPartText {
269
+ export interface GeminiPartBase {
270
+ thought?: boolean;
271
+ }
272
+ export interface GeminiVideoMetadata {
273
+ fps?: number;
274
+ startOffset?: string;
275
+ endOffset?: string;
276
+ }
277
+ export interface GeminiPartBaseFile extends GeminiPartBase {
278
+ videoMetadata?: GeminiVideoMetadata;
279
+ }
280
+ export interface GeminiPartText extends GeminiPartBase {
261
281
  text: string;
262
282
  }
263
- export interface GeminiPartInlineData {
283
+ export interface GeminiPartInlineData extends GeminiPartBaseFile {
264
284
  inlineData: {
265
285
  mimeType: string;
266
286
  data: string;
267
287
  };
268
288
  }
269
- export interface GeminiPartFileData {
289
+ export interface GeminiPartFileData extends GeminiPartBaseFile {
270
290
  fileData: {
271
291
  mimeType: string;
272
292
  fileUri: string;
273
293
  };
274
294
  }
275
- export interface GeminiPartFunctionCall {
295
+ export interface GeminiPartFunctionCall extends GeminiPartBase {
276
296
  functionCall: {
277
297
  name: string;
278
298
  args?: object;
279
299
  };
280
300
  }
281
- export interface GeminiPartFunctionResponse {
301
+ export interface GeminiPartFunctionResponse extends GeminiPartBase {
282
302
  functionResponse: {
283
303
  name: string;
284
304
  response: object;
@@ -347,6 +367,18 @@ export interface GeminiSegment {
347
367
  export interface GeminiRetrievalMetadata {
348
368
  googleSearchDynamicRetrievalScore: number;
349
369
  }
370
+ export type GeminiUrlRetrievalStatus = "URL_RETRIEVAL_STATUS_SUCCESS" | "URL_RETRIEVAL_STATUS_ERROR";
371
+ export interface GeminiUrlRetrievalContext {
372
+ retrievedUrl: string;
373
+ urlRetrievalStatus: GeminiUrlRetrievalStatus;
374
+ }
375
+ export interface GeminiUrlRetrievalMetadata {
376
+ urlRetrievalContexts: GeminiUrlRetrievalContext[];
377
+ }
378
+ export type GeminiUrlMetadata = GeminiUrlRetrievalContext;
379
+ export interface GeminiUrlContextMetadata {
380
+ urlMetadata: GeminiUrlMetadata[];
381
+ }
350
382
  export interface GeminiLogprobsResult {
351
383
  topCandidates: GeminiLogprobsTopCandidate[];
352
384
  chosenCandidates: GeminiLogprobsResultCandidate[];
@@ -368,6 +400,7 @@ export interface GeminiTool {
368
400
  functionDeclarations?: GeminiFunctionDeclaration[];
369
401
  googleSearchRetrieval?: GoogleSearchRetrieval;
370
402
  googleSearch?: GoogleSearch;
403
+ urlContext?: UrlContext;
371
404
  retrieval?: VertexAIRetrieval;
372
405
  }
373
406
  export type GoogleSearchToolSetting = boolean | "googleSearchRetrieval" | "googleSearch" | string;
@@ -381,6 +414,8 @@ export interface GoogleSearchRetrieval {
381
414
  }
382
415
  export interface GoogleSearch {
383
416
  }
417
+ export interface UrlContext {
418
+ }
384
419
  export interface VertexAIRetrieval {
385
420
  vertexAiSearch: {
386
421
  datastore: string;
@@ -410,6 +445,7 @@ export interface GeminiGenerationConfig {
410
445
  temperature?: number;
411
446
  topP?: number;
412
447
  topK?: number;
448
+ seed?: number;
413
449
  presencePenalty?: number;
414
450
  frequencyPenalty?: number;
415
451
  responseMimeType?: GoogleAIResponseMimeType;
@@ -443,6 +479,8 @@ export interface GeminiResponseCandidate {
443
479
  safetyRatings: GeminiSafetyRating[];
444
480
  citationMetadata?: GeminiCitationMetadata;
445
481
  groundingMetadata?: GeminiGroundingMetadata;
482
+ urlRetrievalMetadata?: GeminiUrlRetrievalMetadata;
483
+ urlContextMetadata?: GeminiUrlContextMetadata;
446
484
  avgLogprobs?: number;
447
485
  logprobsResult: GeminiLogprobsResult;
448
486
  finishMessage?: string;
@@ -495,6 +533,7 @@ export interface GoogleAISafetyParams {
495
533
  export type GeminiJsonSchema = Record<string, unknown> & {
496
534
  properties?: Record<string, GeminiJsonSchema>;
497
535
  type: GeminiFunctionSchemaType;
536
+ nullable?: boolean;
498
537
  };
499
538
  export interface GeminiJsonSchemaDirty extends GeminiJsonSchema {
500
539
  items?: GeminiJsonSchemaDirty;
package/dist/types.js CHANGED
@@ -44,5 +44,6 @@ export const GeminiSearchToolAttributes = [
44
44
  export const GeminiToolAttributes = [
45
45
  "functionDeclaration",
46
46
  "retrieval",
47
+ "urlContext",
47
48
  ...GeminiSearchToolAttributes,
48
49
  ];
@@ -131,6 +131,7 @@ function copyAIModelParamsInto(params, options, target) {
131
131
  reasoningEffortToReasoningTokens(ret.modelName, options?.reasoningEffort);
132
132
  ret.topP = options?.topP ?? params?.topP ?? target.topP;
133
133
  ret.topK = options?.topK ?? params?.topK ?? target.topK;
134
+ ret.seed = options?.seed ?? params?.seed ?? target.seed;
134
135
  ret.presencePenalty =
135
136
  options?.presencePenalty ??
136
137
  params?.presencePenalty ??
@@ -126,6 +126,7 @@ export function copyAIModelParamsInto(params, options, target) {
126
126
  reasoningEffortToReasoningTokens(ret.modelName, options?.reasoningEffort);
127
127
  ret.topP = options?.topP ?? params?.topP ?? target.topP;
128
128
  ret.topK = options?.topK ?? params?.topK ?? target.topK;
129
+ ret.seed = options?.seed ?? params?.seed ?? target.seed;
129
130
  ret.presencePenalty =
130
131
  options?.presencePenalty ??
131
132
  params?.presencePenalty ??
@@ -136,7 +136,7 @@ function getGeminiAPI(config) {
136
136
  return null;
137
137
  }
138
138
  }
139
- function messageContentImageUrl(content) {
139
+ function messageContentImageUrlData(content) {
140
140
  const url = typeof content.image_url === "string"
141
141
  ? content.image_url
142
142
  : content.image_url.url;
@@ -159,6 +159,11 @@ function getGeminiAPI(config) {
159
159
  };
160
160
  }
161
161
  }
162
+ function messageContentImageUrl(content) {
163
+ const ret = messageContentImageUrlData(content);
164
+ supplementVideoMetadata(content, ret);
165
+ return ret;
166
+ }
162
167
  async function blobToFileData(blob) {
163
168
  return {
164
169
  fileData: {
@@ -170,7 +175,7 @@ function getGeminiAPI(config) {
170
175
  async function fileUriContentToBlob(uri) {
171
176
  return config?.mediaManager?.getMediaBlob(uri);
172
177
  }
173
- async function messageContentMedia(
178
+ async function messageContentMediaData(
174
179
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
180
  content) {
176
181
  if ("mimeType" in content && "data" in content) {
@@ -198,6 +203,34 @@ function getGeminiAPI(config) {
198
203
  }
199
204
  throw new Error(`Invalid media content: ${JSON.stringify(content, null, 1)}`);
200
205
  }
206
+ function supplementVideoMetadata(
207
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
208
+ content, ret) {
209
+ // Add videoMetadata if defined
210
+ if ("videoMetadata" in content && typeof ret === "object") {
211
+ // eslint-disable-next-line no-param-reassign
212
+ ret.videoMetadata = content.videoMetadata;
213
+ }
214
+ return ret;
215
+ }
216
+ async function messageContentMedia(
217
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
218
+ content) {
219
+ const ret = await messageContentMediaData(content);
220
+ supplementVideoMetadata(content, ret);
221
+ return ret;
222
+ }
223
+ function messageContentReasoning(content) {
224
+ if (content?.reasoning && content?.reasoning.length > 0) {
225
+ return {
226
+ text: content.reasoning,
227
+ thought: true,
228
+ };
229
+ }
230
+ else {
231
+ return null;
232
+ }
233
+ }
201
234
  const standardContentBlockConverter = {
202
235
  providerName: "Google Gemini",
203
236
  fromStandardTextBlock(block) {
@@ -316,8 +349,10 @@ function getGeminiAPI(config) {
316
349
  break;
317
350
  case "media":
318
351
  return await messageContentMedia(content);
352
+ case "reasoning":
353
+ return messageContentReasoning(content);
319
354
  default:
320
- throw new Error(`Unsupported type "${content.type}" received while converting message to message parts: ${content}`);
355
+ throw new Error(`Unsupported type "${content.type}" received while converting message to message parts: ${JSON.stringify(content)}`);
321
356
  }
322
357
  throw new Error(`Cannot coerce "${content.type}" message part into a string.`);
323
358
  }
@@ -471,6 +506,12 @@ function getGeminiAPI(config) {
471
506
  return [];
472
507
  }
473
508
  }
509
+ function thoughtPartToMessageContent(part) {
510
+ return {
511
+ type: "reasoning",
512
+ reasoning: part.text,
513
+ };
514
+ }
474
515
  function textPartToMessageContent(part) {
475
516
  return {
476
517
  type: "text",
@@ -495,6 +536,9 @@ function getGeminiAPI(config) {
495
536
  if (part === undefined || part === null) {
496
537
  return null;
497
538
  }
539
+ else if (part.thought) {
540
+ return thoughtPartToMessageContent(part);
541
+ }
498
542
  else if ("text" in part) {
499
543
  return textPartToMessageContent(part);
500
544
  }
@@ -639,6 +683,19 @@ function getGeminiAPI(config) {
639
683
  content,
640
684
  };
641
685
  }
686
+ function candidateToUrlContextMetadata(candidate) {
687
+ const retrieval = candidate?.urlRetrievalMetadata?.urlRetrievalContexts ?? [];
688
+ const context = candidate?.urlContextMetadata?.urlMetadata ?? [];
689
+ const all = [...retrieval, ...context];
690
+ if (all.length === 0) {
691
+ return undefined;
692
+ }
693
+ else {
694
+ return {
695
+ urlMetadata: all,
696
+ };
697
+ }
698
+ }
642
699
  function addModalityCounts(modalityTokenCounts, details) {
643
700
  modalityTokenCounts?.forEach((modalityTokenCount) => {
644
701
  const { modality, tokenCount } = modalityTokenCount;
@@ -659,6 +716,9 @@ function getGeminiAPI(config) {
659
716
  const total_tokens = usageMetadata.totalTokenCount ?? input_tokens + output_tokens;
660
717
  const input_token_details = {};
661
718
  addModalityCounts(usageMetadata.promptTokensDetails, input_token_details);
719
+ if (typeof usageMetadata?.cachedContentTokenCount === "number") {
720
+ input_token_details.cache_read = usageMetadata.cachedContentTokenCount;
721
+ }
662
722
  const output_token_details = {};
663
723
  addModalityCounts(usageMetadata?.candidatesTokensDetails, output_token_details);
664
724
  if (typeof usageMetadata?.thoughtsTokenCount === "number") {
@@ -701,6 +761,7 @@ function getGeminiAPI(config) {
701
761
  grounding_metadata: data.candidates[0]?.groundingMetadata,
702
762
  finish_reason,
703
763
  finish_message: data.candidates[0]?.finishMessage,
764
+ url_context_metadata: candidateToUrlContextMetadata(data.candidates[0]),
704
765
  avgLogprobs: data.candidates[0]?.avgLogprobs,
705
766
  logprobs: candidateToLogprobs(data.candidates[0]),
706
767
  };
@@ -828,6 +889,7 @@ function getGeminiAPI(config) {
828
889
  if (typeof item.message.content === "string") {
829
890
  // If this is a string, turn it into a text type
830
891
  ret.push({
892
+ type: "text",
831
893
  text: item.message.content,
832
894
  });
833
895
  }
@@ -1072,6 +1134,7 @@ function getGeminiAPI(config) {
1072
1134
  temperature: parameters.temperature,
1073
1135
  topK: parameters.topK,
1074
1136
  topP: parameters.topP,
1137
+ seed: parameters.seed,
1075
1138
  presencePenalty: parameters.presencePenalty,
1076
1139
  frequencyPenalty: parameters.frequencyPenalty,
1077
1140
  maxOutputTokens: parameters.maxOutputTokens,
@@ -1090,10 +1153,10 @@ function getGeminiAPI(config) {
1090
1153
  // Add thinking configuration if explicitly set
1091
1154
  // Note that you cannot have thinkingBudget set to 0 and includeThoughts true
1092
1155
  if (typeof parameters.maxReasoningTokens !== "undefined") {
1156
+ const includeThoughts = parameters.maxReasoningTokens > 0;
1093
1157
  ret.thinkingConfig = {
1094
1158
  thinkingBudget: parameters.maxReasoningTokens,
1095
- // TODO: Expose this configuration to the user once google fully supports it
1096
- includeThoughts: false,
1159
+ includeThoughts,
1097
1160
  };
1098
1161
  }
1099
1162
  // Remove any undefined properties, so we don't send them
@@ -131,7 +131,7 @@ export function getGeminiAPI(config) {
131
131
  return null;
132
132
  }
133
133
  }
134
- function messageContentImageUrl(content) {
134
+ function messageContentImageUrlData(content) {
135
135
  const url = typeof content.image_url === "string"
136
136
  ? content.image_url
137
137
  : content.image_url.url;
@@ -154,6 +154,11 @@ export function getGeminiAPI(config) {
154
154
  };
155
155
  }
156
156
  }
157
+ function messageContentImageUrl(content) {
158
+ const ret = messageContentImageUrlData(content);
159
+ supplementVideoMetadata(content, ret);
160
+ return ret;
161
+ }
157
162
  async function blobToFileData(blob) {
158
163
  return {
159
164
  fileData: {
@@ -165,7 +170,7 @@ export function getGeminiAPI(config) {
165
170
  async function fileUriContentToBlob(uri) {
166
171
  return config?.mediaManager?.getMediaBlob(uri);
167
172
  }
168
- async function messageContentMedia(
173
+ async function messageContentMediaData(
169
174
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
170
175
  content) {
171
176
  if ("mimeType" in content && "data" in content) {
@@ -193,6 +198,34 @@ export function getGeminiAPI(config) {
193
198
  }
194
199
  throw new Error(`Invalid media content: ${JSON.stringify(content, null, 1)}`);
195
200
  }
201
+ function supplementVideoMetadata(
202
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
203
+ content, ret) {
204
+ // Add videoMetadata if defined
205
+ if ("videoMetadata" in content && typeof ret === "object") {
206
+ // eslint-disable-next-line no-param-reassign
207
+ ret.videoMetadata = content.videoMetadata;
208
+ }
209
+ return ret;
210
+ }
211
+ async function messageContentMedia(
212
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
213
+ content) {
214
+ const ret = await messageContentMediaData(content);
215
+ supplementVideoMetadata(content, ret);
216
+ return ret;
217
+ }
218
+ function messageContentReasoning(content) {
219
+ if (content?.reasoning && content?.reasoning.length > 0) {
220
+ return {
221
+ text: content.reasoning,
222
+ thought: true,
223
+ };
224
+ }
225
+ else {
226
+ return null;
227
+ }
228
+ }
196
229
  const standardContentBlockConverter = {
197
230
  providerName: "Google Gemini",
198
231
  fromStandardTextBlock(block) {
@@ -311,8 +344,10 @@ export function getGeminiAPI(config) {
311
344
  break;
312
345
  case "media":
313
346
  return await messageContentMedia(content);
347
+ case "reasoning":
348
+ return messageContentReasoning(content);
314
349
  default:
315
- throw new Error(`Unsupported type "${content.type}" received while converting message to message parts: ${content}`);
350
+ throw new Error(`Unsupported type "${content.type}" received while converting message to message parts: ${JSON.stringify(content)}`);
316
351
  }
317
352
  throw new Error(`Cannot coerce "${content.type}" message part into a string.`);
318
353
  }
@@ -466,6 +501,12 @@ export function getGeminiAPI(config) {
466
501
  return [];
467
502
  }
468
503
  }
504
+ function thoughtPartToMessageContent(part) {
505
+ return {
506
+ type: "reasoning",
507
+ reasoning: part.text,
508
+ };
509
+ }
469
510
  function textPartToMessageContent(part) {
470
511
  return {
471
512
  type: "text",
@@ -490,6 +531,9 @@ export function getGeminiAPI(config) {
490
531
  if (part === undefined || part === null) {
491
532
  return null;
492
533
  }
534
+ else if (part.thought) {
535
+ return thoughtPartToMessageContent(part);
536
+ }
493
537
  else if ("text" in part) {
494
538
  return textPartToMessageContent(part);
495
539
  }
@@ -634,6 +678,19 @@ export function getGeminiAPI(config) {
634
678
  content,
635
679
  };
636
680
  }
681
+ function candidateToUrlContextMetadata(candidate) {
682
+ const retrieval = candidate?.urlRetrievalMetadata?.urlRetrievalContexts ?? [];
683
+ const context = candidate?.urlContextMetadata?.urlMetadata ?? [];
684
+ const all = [...retrieval, ...context];
685
+ if (all.length === 0) {
686
+ return undefined;
687
+ }
688
+ else {
689
+ return {
690
+ urlMetadata: all,
691
+ };
692
+ }
693
+ }
637
694
  function addModalityCounts(modalityTokenCounts, details) {
638
695
  modalityTokenCounts?.forEach((modalityTokenCount) => {
639
696
  const { modality, tokenCount } = modalityTokenCount;
@@ -654,6 +711,9 @@ export function getGeminiAPI(config) {
654
711
  const total_tokens = usageMetadata.totalTokenCount ?? input_tokens + output_tokens;
655
712
  const input_token_details = {};
656
713
  addModalityCounts(usageMetadata.promptTokensDetails, input_token_details);
714
+ if (typeof usageMetadata?.cachedContentTokenCount === "number") {
715
+ input_token_details.cache_read = usageMetadata.cachedContentTokenCount;
716
+ }
657
717
  const output_token_details = {};
658
718
  addModalityCounts(usageMetadata?.candidatesTokensDetails, output_token_details);
659
719
  if (typeof usageMetadata?.thoughtsTokenCount === "number") {
@@ -696,6 +756,7 @@ export function getGeminiAPI(config) {
696
756
  grounding_metadata: data.candidates[0]?.groundingMetadata,
697
757
  finish_reason,
698
758
  finish_message: data.candidates[0]?.finishMessage,
759
+ url_context_metadata: candidateToUrlContextMetadata(data.candidates[0]),
699
760
  avgLogprobs: data.candidates[0]?.avgLogprobs,
700
761
  logprobs: candidateToLogprobs(data.candidates[0]),
701
762
  };
@@ -823,6 +884,7 @@ export function getGeminiAPI(config) {
823
884
  if (typeof item.message.content === "string") {
824
885
  // If this is a string, turn it into a text type
825
886
  ret.push({
887
+ type: "text",
826
888
  text: item.message.content,
827
889
  });
828
890
  }
@@ -1067,6 +1129,7 @@ export function getGeminiAPI(config) {
1067
1129
  temperature: parameters.temperature,
1068
1130
  topK: parameters.topK,
1069
1131
  topP: parameters.topP,
1132
+ seed: parameters.seed,
1070
1133
  presencePenalty: parameters.presencePenalty,
1071
1134
  frequencyPenalty: parameters.frequencyPenalty,
1072
1135
  maxOutputTokens: parameters.maxOutputTokens,
@@ -1085,10 +1148,10 @@ export function getGeminiAPI(config) {
1085
1148
  // Add thinking configuration if explicitly set
1086
1149
  // Note that you cannot have thinkingBudget set to 0 and includeThoughts true
1087
1150
  if (typeof parameters.maxReasoningTokens !== "undefined") {
1151
+ const includeThoughts = parameters.maxReasoningTokens > 0;
1088
1152
  ret.thinkingConfig = {
1089
1153
  thinkingBudget: parameters.maxReasoningTokens,
1090
- // TODO: Expose this configuration to the user once google fully supports it
1091
- includeThoughts: false,
1154
+ includeThoughts,
1092
1155
  };
1093
1156
  }
1094
1157
  // Remove any undefined properties, so we don't send them
@@ -12,6 +12,31 @@ obj) {
12
12
  if ("additionalProperties" in newObj) {
13
13
  delete newObj.additionalProperties;
14
14
  }
15
+ if (Array.isArray(obj.type)) {
16
+ const len = obj.type.length;
17
+ const nullIndex = obj.type.indexOf("null");
18
+ if (len === 2 && nullIndex >= 0) {
19
+ // There are only two values set for the type, and one of them is "null".
20
+ // Set the type to the other one and set nullable to true.
21
+ const typeIndex = nullIndex === 0 ? 1 : 0;
22
+ newObj.type = obj.type[typeIndex];
23
+ newObj.nullable = true;
24
+ }
25
+ else if (len === 1 && nullIndex === 0) {
26
+ // This is nullable only without a type, which doesn't
27
+ // make sense for Gemini
28
+ throw new Error("zod_to_gemini_parameters: Gemini cannot handle null type");
29
+ }
30
+ else if (len === 1) {
31
+ // Although an array, it has only one value.
32
+ // So set it to the string to match what Gemini expects.
33
+ newObj.type = obj?.type[0];
34
+ }
35
+ else {
36
+ // Anything else could be a union type, so reject it.
37
+ throw new Error("zod_to_gemini_parameters: Gemini cannot handle union types");
38
+ }
39
+ }
15
40
  for (const key in newObj) {
16
41
  if (key in newObj) {
17
42
  if (Array.isArray(newObj[key])) {
@@ -30,6 +55,8 @@ exports.removeAdditionalProperties = removeAdditionalProperties;
30
55
  function schemaToGeminiParameters(schema) {
31
56
  // Gemini doesn't accept either the $schema or additionalProperties
32
57
  // attributes, so we need to explicitly remove them.
58
+ // Zod sometimes also makes an array of type (because of .nullish()),
59
+ // which needs cleaning up.
33
60
  const jsonSchema = removeAdditionalProperties((0, types_1.isZodSchema)(schema) ? (0, zod_to_json_schema_1.zodToJsonSchema)(schema) : schema);
34
61
  const { $schema, ...rest } = jsonSchema;
35
62
  return rest;
@@ -9,6 +9,31 @@ obj) {
9
9
  if ("additionalProperties" in newObj) {
10
10
  delete newObj.additionalProperties;
11
11
  }
12
+ if (Array.isArray(obj.type)) {
13
+ const len = obj.type.length;
14
+ const nullIndex = obj.type.indexOf("null");
15
+ if (len === 2 && nullIndex >= 0) {
16
+ // There are only two values set for the type, and one of them is "null".
17
+ // Set the type to the other one and set nullable to true.
18
+ const typeIndex = nullIndex === 0 ? 1 : 0;
19
+ newObj.type = obj.type[typeIndex];
20
+ newObj.nullable = true;
21
+ }
22
+ else if (len === 1 && nullIndex === 0) {
23
+ // This is nullable only without a type, which doesn't
24
+ // make sense for Gemini
25
+ throw new Error("zod_to_gemini_parameters: Gemini cannot handle null type");
26
+ }
27
+ else if (len === 1) {
28
+ // Although an array, it has only one value.
29
+ // So set it to the string to match what Gemini expects.
30
+ newObj.type = obj?.type[0];
31
+ }
32
+ else {
33
+ // Anything else could be a union type, so reject it.
34
+ throw new Error("zod_to_gemini_parameters: Gemini cannot handle union types");
35
+ }
36
+ }
12
37
  for (const key in newObj) {
13
38
  if (key in newObj) {
14
39
  if (Array.isArray(newObj[key])) {
@@ -26,6 +51,8 @@ obj) {
26
51
  export function schemaToGeminiParameters(schema) {
27
52
  // Gemini doesn't accept either the $schema or additionalProperties
28
53
  // attributes, so we need to explicitly remove them.
54
+ // Zod sometimes also makes an array of type (because of .nullish()),
55
+ // which needs cleaning up.
29
56
  const jsonSchema = removeAdditionalProperties(isZodSchema(schema) ? zodToJsonSchema(schema) : schema);
30
57
  const { $schema, ...rest } = jsonSchema;
31
58
  return rest;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/google-common",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "Core types and classes for Google services.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@jest/globals": "^29.5.0",
43
- "@langchain/core": "0.3.55",
43
+ "@langchain/core": "0.3.57",
44
44
  "@langchain/scripts": ">=0.1.0 <0.2.0",
45
45
  "@swc/core": "^1.3.90",
46
46
  "@swc/jest": "^0.2.29",