@warlock.js/ai-google 5.31.0 → 5.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to `@warlock.js/ai-google` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.33.0 - 2026-10-19
8
+
9
+ ### Changed
10
+
11
+ - Lockstep release; package APIs are unchanged.
12
+
13
+ ## 5.32.0 - 2026-10-18
14
+
15
+ ### Changed
16
+
17
+ - Pin the package development compiler to TypeScript 7.0.2 and use explicit Node types and source roots with supported module resolution.
18
+
19
+ ### Fixed
20
+
21
+ - Gemini Developer API token counting sends the full `generateContentRequest`, including system instructions, tools and generation settings, through the existing SDK transport with cancellation and error mapping. Vertex counting is unchanged; unsupported cached-content counts carry the typed signal used by `countInputTokens` to estimate.
22
+
23
+ ### Added
24
+
25
+ - Preserve raw candidate grounding metadata and normalize unique nonempty search queries in completions and streams. Export opt-in Gemini 3+ request prices as `GOOGLE_SERVER_TOOL_PRICING`; document older models’ per-prompt billing.
26
+
7
27
  ## 5.31.0 - 2026-10-18
8
28
 
9
29
  ### Fixed
package/cjs/index.cjs CHANGED
@@ -499,7 +499,7 @@ const DEFAULT_RESPONSE_MODALITIES = ["TEXT", "IMAGE"];
499
499
  * or image generation being "stopped" for safety, prohibited content,
500
500
  * recitation, blocklist, or SPII.
501
501
  */
502
- const FILTERED_FINISH_REASONS = new Set([
502
+ const FILTERED_FINISH_REASONS = /* @__PURE__ */ new Set([
503
503
  "SAFETY",
504
504
  "IMAGE_SAFETY",
505
505
  "PROHIBITED_CONTENT",
@@ -832,10 +832,42 @@ const GOOGLE_MODEL_PRICING = { "gemini-3.5-flash-lite": {
832
832
  function googlePricingFor(modelName) {
833
833
  return GOOGLE_MODEL_PRICING[modelName];
834
834
  }
835
+ /** USD per nonempty unique query for Gemini 3+, before free monthly allowances.
836
+ * Gemini 2.5 and older bill per grounded prompt; do not use this rate for them.
837
+ * https://ai.google.dev/gemini-api/docs/google-search#pricing — verified 2026-10-18. */
838
+ const GOOGLE_SERVER_TOOL_PRICING = { webSearch: .014 };
839
+
840
+ //#endregion
841
+ //#region ../../packages/ai-google/src/utils/grounding.ts
842
+ /** Candidate snapshots are replaced, not added: streamed metadata may repeat.
843
+ * Installed @google/genai 2.19.0 GroundingMetadata.webSearchQueries; REST schema:
844
+ * https://ai.google.dev/api/generate-content#GroundingMetadata
845
+ * Billing ignores empty queries and counts unique queries for Gemini 3+:
846
+ * https://ai.google.dev/gemini-api/docs/google-search#pricing
847
+ * Gemini 2.5 and older bill per grounded prompt; this counter is NOT that unit. */
848
+ function applyGrounding(usage, snapshots, response) {
849
+ for (const [position, candidate] of (response.candidates ?? []).entries()) if (candidate.groundingMetadata) snapshots.set(candidate.index ?? position, candidate.groundingMetadata);
850
+ const reported = [...snapshots.values()].filter((metadata) => metadata.webSearchQueries !== void 0);
851
+ if (reported.length > 0) {
852
+ const queries = reported.flatMap((metadata) => metadata.webSearchQueries ?? []);
853
+ usage.serverToolUse = {
854
+ ...usage.serverToolUse,
855
+ webSearchRequests: new Set(queries.filter((query) => query !== "")).size,
856
+ googleWebSearchQueries: queries.length
857
+ };
858
+ }
859
+ }
860
+ /** Retain chunks, supports, search entry point and queries without flattening them. */
861
+ function groundingMetadata(snapshots) {
862
+ return snapshots.size ? { google: { groundingMetadata: [...snapshots].map(([index, metadata]) => ({
863
+ index,
864
+ ...metadata
865
+ })) } } : void 0;
866
+ }
835
867
 
836
868
  //#endregion
837
869
  //#region ../../packages/ai-google/src/provider-options.ts
838
- const RESERVED_FIELDS = new Set([
870
+ const RESERVED_FIELDS = /* @__PURE__ */ new Set([
839
871
  "systemInstruction",
840
872
  "tools",
841
873
  "abortSignal",
@@ -1000,12 +1032,17 @@ var GoogleModel = class {
1000
1032
  }
1001
1033
  const toolCalls = this.extractToolCalls(response);
1002
1034
  const finishReason = toolCalls ? "tool_calls" : mapFinishReason(response.candidates?.[0]?.finishReason);
1003
- const usage = (0, _warlock_js_ai.attachCost)(this.extractUsage(response), this.pricing);
1035
+ const usage = this.extractUsage(response);
1036
+ const grounding = /* @__PURE__ */ new Map();
1037
+ applyGrounding(usage, grounding, response);
1038
+ const providerMetadata = groundingMetadata(grounding);
1039
+ (0, _warlock_js_ai.attachCost)(usage, this.pricing);
1004
1040
  this.logger.debug(LOG_MODULE, "response", "generateContent call succeeded", {
1005
1041
  finishReason,
1006
1042
  usage
1007
1043
  });
1008
1044
  return {
1045
+ ...providerMetadata ? { providerMetadata } : {},
1009
1046
  content: response.text ?? "",
1010
1047
  finishReason,
1011
1048
  usage,
@@ -1016,10 +1053,9 @@ var GoogleModel = class {
1016
1053
  async countTokens(messages, options) {
1017
1054
  const { systemInstruction, contents } = toGoogleContents(messages);
1018
1055
  const config = this.buildConfig(systemInstruction, options);
1019
- if (!this.ai.vertexai && (config.systemInstruction !== void 0 || config.tools !== void 0)) throw new _warlock_js_ai.InvalidRequestError("The Google SDK countTokens endpoint in Developer API mode cannot count system instructions or tools. Use Vertex mode for this preflight, or SDK.count() for an offline estimate.");
1020
- if (config.cachedContent !== void 0) throw new _warlock_js_ai.InvalidRequestError("The Google SDK countTokens endpoint cannot count a cachedContent reference.");
1056
+ if (config.cachedContent !== void 0) throw new _warlock_js_ai.InvalidRequestError("The Google SDK countTokens endpoint cannot count a cachedContent reference.", { reason: "count-unsupported" });
1021
1057
  try {
1022
- const response = await this.ai.models.countTokens({
1058
+ const response = this.ai.vertexai ? await this.ai.models.countTokens({
1023
1059
  model: this.name,
1024
1060
  contents,
1025
1061
  config: {
@@ -1028,7 +1064,7 @@ var GoogleModel = class {
1028
1064
  ...config.abortSignal ? { abortSignal: config.abortSignal } : {},
1029
1065
  ...config.httpOptions ? { httpOptions: config.httpOptions } : {}
1030
1066
  }
1031
- });
1067
+ }) : await this.countDeveloperTokens(contents, config);
1032
1068
  if (response.totalTokens === void 0) throw new _warlock_js_ai.InvalidRequestError("Google countTokens returned no totalTokens.");
1033
1069
  return response.totalTokens;
1034
1070
  } catch (thrown) {
@@ -1036,6 +1072,42 @@ var GoogleModel = class {
1036
1072
  }
1037
1073
  }
1038
1074
  /**
1075
+ * REST counts the full generation request, unlike the typed Developer API
1076
+ * converter in @google/genai 2.19.0. See https://ai.google.dev/api/tokens.
1077
+ * extraBody keeps the SDK's auth, fetch, base URL, retries and cancellation.
1078
+ */
1079
+ countDeveloperTokens(contents, config) {
1080
+ config.abortSignal?.throwIfAborted();
1081
+ const { systemInstruction, serviceTier, tools, toolConfig, safetySettings, cachedContent, abortSignal, httpOptions, ...generationConfig } = config;
1082
+ delete generationConfig.automaticFunctionCalling;
1083
+ const model = this.name.startsWith("models/") ? this.name : `models/${this.name}`;
1084
+ return this.ai.models.countTokens({
1085
+ model: this.name,
1086
+ contents,
1087
+ config: {
1088
+ abortSignal,
1089
+ httpOptions: {
1090
+ ...httpOptions,
1091
+ extraBody: {
1092
+ ...httpOptions?.extraBody,
1093
+ contents: void 0,
1094
+ generateContentRequest: {
1095
+ model,
1096
+ contents,
1097
+ ...serviceTier !== void 0 ? { serviceTier } : {},
1098
+ ...systemInstruction !== void 0 ? { systemInstruction: { parts: [{ text: systemInstruction }] } } : {},
1099
+ ...tools !== void 0 ? { tools } : {},
1100
+ ...toolConfig !== void 0 ? { toolConfig } : {},
1101
+ ...safetySettings !== void 0 ? { safetySettings } : {},
1102
+ ...cachedContent !== void 0 ? { cachedContent } : {},
1103
+ ...Object.keys(generationConfig).length > 0 ? { generationConfig } : {}
1104
+ }
1105
+ }
1106
+ }
1107
+ }
1108
+ });
1109
+ }
1110
+ /**
1039
1111
  * Incremental streaming completion via `generateContentStream`.
1040
1112
  * Yields neutral `ModelStreamChunk`s — `delta` for text, `tool-call`
1041
1113
  * per function call (Gemini emits a fully-formed call, not partial
@@ -1072,8 +1144,10 @@ var GoogleModel = class {
1072
1144
  total: 0
1073
1145
  };
1074
1146
  const want = (0, _warlock_js_ai.resolveStreamChunks)(options);
1147
+ const grounding = /* @__PURE__ */ new Map();
1075
1148
  try {
1076
1149
  for await (const chunk of iterable) {
1150
+ applyGrounding(usage, grounding, chunk);
1077
1151
  if (want.reasoning) {
1078
1152
  for (const part of chunk.candidates?.[0]?.content?.parts ?? []) if (part.thought === true && typeof part.text === "string" && part.text !== "") yield {
1079
1153
  type: "reasoning",
@@ -1116,10 +1190,12 @@ var GoogleModel = class {
1116
1190
  finishReason,
1117
1191
  usage
1118
1192
  });
1193
+ const providerMetadata = groundingMetadata(grounding);
1119
1194
  yield {
1120
1195
  type: "done",
1121
1196
  finishReason,
1122
- usage
1197
+ usage,
1198
+ ...providerMetadata ? { providerMetadata } : {}
1123
1199
  };
1124
1200
  }
1125
1201
  /**
@@ -1393,6 +1469,7 @@ var GoogleSDK = class {
1393
1469
 
1394
1470
  //#endregion
1395
1471
  exports.GOOGLE_MODEL_PRICING = GOOGLE_MODEL_PRICING;
1472
+ exports.GOOGLE_SERVER_TOOL_PRICING = GOOGLE_SERVER_TOOL_PRICING;
1396
1473
  exports.GeminiImageModel = GeminiImageModel;
1397
1474
  exports.GoogleImageModel = GoogleImageModel;
1398
1475
  exports.GoogleSDK = GoogleSDK;