@warlock.js/ai-google 5.30.0 → 5.32.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 +34 -1
- package/cjs/index.cjs +273 -21
- package/cjs/index.cjs.map +1 -1
- package/esm/config.type.d.mts +4 -6
- package/esm/config.type.d.mts.map +1 -1
- package/esm/embedder.mjs.map +1 -1
- package/esm/gemini-image.d.mts +1 -3
- package/esm/gemini-image.d.mts.map +1 -1
- package/esm/gemini-image.mjs +1 -1
- package/esm/gemini-image.mjs.map +1 -1
- package/esm/image.d.mts +1 -3
- package/esm/image.d.mts.map +1 -1
- package/esm/image.mjs.map +1 -1
- package/esm/index.d.mts +3 -1
- package/esm/index.mjs +2 -1
- package/esm/known-pricing.d.mts +37 -0
- package/esm/known-pricing.d.mts.map +1 -0
- package/esm/known-pricing.mjs +42 -0
- package/esm/known-pricing.mjs.map +1 -0
- package/esm/model.mjs +110 -9
- package/esm/model.mjs.map +1 -1
- package/esm/provider-options.d.mts +8 -0
- package/esm/provider-options.d.mts.map +1 -0
- package/esm/provider-options.mjs +36 -0
- package/esm/provider-options.mjs.map +1 -0
- package/esm/sdk.d.mts +4 -5
- package/esm/sdk.d.mts.map +1 -1
- package/esm/sdk.mjs +5 -3
- package/esm/sdk.mjs.map +1 -1
- package/esm/utils/apply-google-usage.mjs +5 -0
- package/esm/utils/apply-google-usage.mjs.map +1 -1
- package/esm/utils/grounding.mjs +30 -0
- package/esm/utils/grounding.mjs.map +1 -0
- package/esm/utils/index.mjs +1 -1
- package/esm/utils/to-google-contents.mjs +33 -1
- package/esm/utils/to-google-contents.mjs.map +1 -1
- package/esm/utils/wrap-google-error.mjs +21 -8
- package/esm/utils/wrap-google-error.mjs.map +1 -1
- package/llms-full.txt +162 -31
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/skills/setup-google/SKILL.md +162 -31
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,38 @@ 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.32.0 - 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Pin the package development compiler to TypeScript 7.0.2 and use explicit Node types and source roots with supported module resolution.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- 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.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- 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.
|
|
20
|
+
|
|
21
|
+
## 5.31.0 - 2026-10-18
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Provider body options merge plain-object fields one level deep, preserving neutral reasoning, generation and structured-output settings unless the caller overrides their keys.
|
|
26
|
+
- Consecutive messages that map to the same Gemini role are sent as one content with their parts in order. The results of parallel tool calls now travel as a single `user` turn of `functionResponse` parts in call order instead of one turn per result, and an agent compaction summary next to a same-role turn merges into it, so requests keep alternating `user` and `model` turns. Each `functionCall` keeps its own `thoughtSignature`, and `countTokens()` counts the same contents.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Usage is marked `inputIncludesCache: true` (`promptTokenCount` includes cached content), and `complete()` / the streaming `done` chunk attach `usage.cost` when priced.
|
|
31
|
+
- A built-in list price for `gemini-3.5-flash-lite` (`GOOGLE_MODEL_PRICING`, `googlePricingFor`), from the Gemini API pricing page as of 2026-10-17. Models priced by prompt length, by input modality, or with a scheduled price change stay unpriced.
|
|
32
|
+
- 503 `UNAVAILABLE` throws `ProviderOverloadedError`. 400 and 500 failures are marked `billed: false`, as Google's billing docs state.
|
|
33
|
+
- An adapter contract test verifies that aborting `signal` rejects `complete()` and `stream()` on the model and cancels the HTTP request.
|
|
34
|
+
- With `streamChunks`, `stream()` yields `reasoning` for thought parts (sent when `thinkingConfig.includeThoughts` is on) and `usage` for every chunk carrying `usageMetadata`.
|
|
35
|
+
- `ai.providerTool({ provider: "google", definition })` entries (`{ googleSearch: {} }`, `{ codeExecution: {} }`, `{ urlContext: {} }`) are sent verbatim as their own `Tool` after the function declarations; other providers' tools are skipped with a warning.
|
|
36
|
+
- `providerOptions.google.body` forwards native per-call request options for completions and streams while protecting the model, conversation and tool definitions.
|
|
37
|
+
- Models expose native `countTokens()` preflight. Vertex counts system instructions and tools; unsupported Developer API system/tool and cached-content counts fail explicitly.
|
|
38
|
+
|
|
7
39
|
## 5.30.0 - 2026-10-16
|
|
8
40
|
|
|
9
41
|
### Changed
|
|
@@ -81,6 +113,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
81
113
|
### Changed
|
|
82
114
|
|
|
83
115
|
- Lockstep release maintenance and dependency refresh.
|
|
116
|
+
|
|
84
117
|
## 5.20.0 - 2026-09-24
|
|
85
118
|
|
|
86
119
|
### Changed
|
|
@@ -154,7 +187,7 @@ changelogs.
|
|
|
154
187
|
|
|
155
188
|
### Deprecated
|
|
156
189
|
|
|
157
|
-
- **Google has deprecated `generateImages`, the transport the `imagen-*` path still uses.** Verbatim from the `@google/genai` runtime warning:
|
|
190
|
+
- **Google has deprecated `generateImages`, the transport the `imagen-*` path still uses.** Verbatim from the `@google/genai` runtime warning: _"The generateImages method is deprecated and will be removed in the next major release (not before Jan. 1 2027). Please use the generateContent method with image models instead. See https://ai.google.dev/gemini-api/docs/deprecations#imagen-models and https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/capabilities/image-generation#generate-images"_ (`editImage` carries the same notice.) Nothing breaks today and the Imagen path is unchanged, but it is on a clock: new image work should prefer a `gemini-` id. The warning is emitted by `@google/genai` ≥ 2.17; with the bump below, this package now prints it whenever the `imagen-*` path is used
|
|
158
191
|
|
|
159
192
|
### Changed
|
|
160
193
|
|
package/cjs/index.cjs
CHANGED
|
@@ -19,8 +19,13 @@ let _warlock_js_logger = require("@warlock.js/logger");
|
|
|
19
19
|
*
|
|
20
20
|
* `total` falls back to `input + output` when Google omits
|
|
21
21
|
* `totalTokenCount`.
|
|
22
|
+
*
|
|
23
|
+
* `promptTokenCount` "includes the number of tokens in the cached
|
|
24
|
+
* content" (https://ai.google.dev/api/generate-content#UsageMetadata), so
|
|
25
|
+
* the usage is marked `inputIncludesCache: true`.
|
|
22
26
|
*/
|
|
23
27
|
function applyGoogleUsage(usage, raw) {
|
|
28
|
+
usage.inputIncludesCache = true;
|
|
24
29
|
usage.input = raw.promptTokenCount ?? usage.input;
|
|
25
30
|
usage.output = raw.candidatesTokenCount ?? usage.output;
|
|
26
31
|
usage.total = raw.totalTokenCount ?? usage.input + usage.output;
|
|
@@ -78,6 +83,12 @@ function mapFinishReason(raw) {
|
|
|
78
83
|
* 4. **Tool calls are `functionCall` parts.** An assistant message
|
|
79
84
|
* with `toolCalls` becomes a `"model"` content: an optional leading
|
|
80
85
|
* `text` part followed by one `functionCall` part per call.
|
|
86
|
+
* 5. **Adjacent same-role turns merge.** Consecutive contents with the
|
|
87
|
+
* same role become one content with their parts in order, so the
|
|
88
|
+
* results of parallel tool calls travel as one `user` turn (as the
|
|
89
|
+
* `@google/genai` SDK's own automatic function calling sends them)
|
|
90
|
+
* and a compaction summary next to a same-role turn does not break
|
|
91
|
+
* the user/model alternation.
|
|
81
92
|
*
|
|
82
93
|
* @example
|
|
83
94
|
* const { systemInstruction, contents } = toGoogleContents([
|
|
@@ -137,10 +148,36 @@ function toGoogleContents(messages) {
|
|
|
137
148
|
}
|
|
138
149
|
return {
|
|
139
150
|
systemInstruction: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
|
|
140
|
-
contents
|
|
151
|
+
contents: mergeAdjacentContents(contents)
|
|
141
152
|
};
|
|
142
153
|
}
|
|
143
154
|
/**
|
|
155
|
+
* Join consecutive contents that share a role into one, keeping part
|
|
156
|
+
* order: parallel `functionResponse` parts stay in their `functionCall`
|
|
157
|
+
* order, and each `functionCall` keeps its own `thoughtSignature`.
|
|
158
|
+
* Returns new content objects; the input contents are not modified.
|
|
159
|
+
*
|
|
160
|
+
* Source for the single-turn shape: `@google/genai` 2.19.0
|
|
161
|
+
* `Models.generateContent` (automatic function calling) sends every
|
|
162
|
+
* function response of one model turn as a single `{ role: "user",
|
|
163
|
+
* parts }` content, in call order.
|
|
164
|
+
*/
|
|
165
|
+
function mergeAdjacentContents(contents) {
|
|
166
|
+
const merged = [];
|
|
167
|
+
for (const content of contents) {
|
|
168
|
+
const previous = merged.at(-1);
|
|
169
|
+
if (previous && previous.role === content.role) {
|
|
170
|
+
merged[merged.length - 1] = {
|
|
171
|
+
...previous,
|
|
172
|
+
parts: [...previous.parts ?? [], ...content.parts ?? []]
|
|
173
|
+
};
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
merged.push(content);
|
|
177
|
+
}
|
|
178
|
+
return merged;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
144
181
|
* Multipart content is only meaningful on user messages — for any
|
|
145
182
|
* other role collapse a `ContentPart[]` to concatenated text. Plain
|
|
146
183
|
* strings pass through unchanged.
|
|
@@ -252,35 +289,48 @@ function wrapGoogleError(thrown) {
|
|
|
252
289
|
const shape = toShape(thrown);
|
|
253
290
|
const context = buildContext(shape);
|
|
254
291
|
const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));
|
|
292
|
+
const billed = shape.status === 400 || shape.status === 500 ? false : void 0;
|
|
255
293
|
if (isTimeout(shape)) return new _warlock_js_ai.ProviderTimeoutError(message, {
|
|
256
294
|
cause: thrown,
|
|
257
|
-
context
|
|
295
|
+
context,
|
|
296
|
+
billed
|
|
258
297
|
});
|
|
259
298
|
if (shape.status === 401 || shape.status === 403 || /permission_denied|api key not valid|unauthenticated/i.test(message)) return new _warlock_js_ai.ProviderAuthError(message, {
|
|
260
299
|
cause: thrown,
|
|
261
|
-
context
|
|
300
|
+
context,
|
|
301
|
+
billed
|
|
262
302
|
});
|
|
263
303
|
if (shape.status === 429 || /resource_exhausted|quota/i.test(message)) return new _warlock_js_ai.ProviderRateLimitError(message, {
|
|
264
304
|
cause: thrown,
|
|
265
|
-
context
|
|
305
|
+
context,
|
|
306
|
+
billed
|
|
266
307
|
});
|
|
267
308
|
if (shape.status === 400) {
|
|
268
309
|
if (/token count|context length|exceeds the maximum|input is too long/i.test(message)) return new _warlock_js_ai.ContextLengthExceededError(message, {
|
|
269
310
|
cause: thrown,
|
|
270
|
-
context
|
|
311
|
+
context,
|
|
312
|
+
billed
|
|
271
313
|
});
|
|
272
314
|
return new _warlock_js_ai.InvalidRequestError(message, {
|
|
273
315
|
cause: thrown,
|
|
274
|
-
context
|
|
316
|
+
context,
|
|
317
|
+
billed
|
|
275
318
|
});
|
|
276
319
|
}
|
|
277
320
|
if (shape.status === 404 || isClientStatus(shape.status)) return new _warlock_js_ai.InvalidRequestError(message, {
|
|
278
321
|
cause: thrown,
|
|
279
|
-
context
|
|
322
|
+
context,
|
|
323
|
+
billed
|
|
324
|
+
});
|
|
325
|
+
if (shape.status === 503 || /\bUNAVAILABLE\b/.test(message) || /overloaded/i.test(message)) return new _warlock_js_ai.ProviderOverloadedError(message, {
|
|
326
|
+
cause: thrown,
|
|
327
|
+
context,
|
|
328
|
+
billed
|
|
280
329
|
});
|
|
281
330
|
return new _warlock_js_ai.ProviderError(message, {
|
|
282
331
|
cause: thrown,
|
|
283
|
-
context
|
|
332
|
+
context,
|
|
333
|
+
billed
|
|
284
334
|
});
|
|
285
335
|
}
|
|
286
336
|
/**
|
|
@@ -449,7 +499,7 @@ const DEFAULT_RESPONSE_MODALITIES = ["TEXT", "IMAGE"];
|
|
|
449
499
|
* or image generation being "stopped" for safety, prohibited content,
|
|
450
500
|
* recitation, blocklist, or SPII.
|
|
451
501
|
*/
|
|
452
|
-
const FILTERED_FINISH_REASONS = new Set([
|
|
502
|
+
const FILTERED_FINISH_REASONS = /* @__PURE__ */ new Set([
|
|
453
503
|
"SAFETY",
|
|
454
504
|
"IMAGE_SAFETY",
|
|
455
505
|
"PROHIBITED_CONTENT",
|
|
@@ -747,6 +797,106 @@ var GoogleImageModel = class {
|
|
|
747
797
|
}
|
|
748
798
|
};
|
|
749
799
|
|
|
800
|
+
//#endregion
|
|
801
|
+
//#region ../../packages/ai-google/src/known-pricing.ts
|
|
802
|
+
/**
|
|
803
|
+
* Built-in Gemini API list prices (paid tier, Standard), USD per
|
|
804
|
+
* 1,000,000 tokens, keyed by exact model id. `cachedInput` is the
|
|
805
|
+
* context-cache read rate; the hourly cache-storage charge is not a token
|
|
806
|
+
* rate and is never included.
|
|
807
|
+
*
|
|
808
|
+
* Source: https://ai.google.dev/gemini-api/docs/pricing, fetched
|
|
809
|
+
* 2026-10-17. Only models with one flat token price are listed. Left out
|
|
810
|
+
* on purpose — pass `pricing` yourself for them:
|
|
811
|
+
*
|
|
812
|
+
* - Models priced by prompt length (the Pro models, split at 200k
|
|
813
|
+
* prompt tokens).
|
|
814
|
+
* - Models with a higher audio-input rate (most Flash / Flash-Lite
|
|
815
|
+
* models): `Usage` does not split input by modality.
|
|
816
|
+
* - Models with a scheduled price change (gemini-4-argon, the 3.8 / 3.6
|
|
817
|
+
* Flash models).
|
|
818
|
+
*/
|
|
819
|
+
const GOOGLE_MODEL_PRICING = { "gemini-3.5-flash-lite": {
|
|
820
|
+
input: .3,
|
|
821
|
+
output: 2.5,
|
|
822
|
+
cachedInput: .03
|
|
823
|
+
} };
|
|
824
|
+
/**
|
|
825
|
+
* Look up the built-in list price for a Gemini model id (exact match),
|
|
826
|
+
* or `undefined` so cost stays unknown rather than wrong.
|
|
827
|
+
*
|
|
828
|
+
* @example
|
|
829
|
+
* googlePricingFor("gemini-3.5-flash-lite"); // { input: 0.3, output: 2.5, cachedInput: 0.03 }
|
|
830
|
+
* googlePricingFor("gemini-2.5-pro"); // undefined — priced by prompt length
|
|
831
|
+
*/
|
|
832
|
+
function googlePricingFor(modelName) {
|
|
833
|
+
return GOOGLE_MODEL_PRICING[modelName];
|
|
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
|
+
}
|
|
867
|
+
|
|
868
|
+
//#endregion
|
|
869
|
+
//#region ../../packages/ai-google/src/provider-options.ts
|
|
870
|
+
const RESERVED_FIELDS = /* @__PURE__ */ new Set([
|
|
871
|
+
"systemInstruction",
|
|
872
|
+
"tools",
|
|
873
|
+
"abortSignal",
|
|
874
|
+
"httpOptions"
|
|
875
|
+
]);
|
|
876
|
+
function mergeProviderBody(built, options) {
|
|
877
|
+
const body = (options?.providerOptions?.google)?.body ?? {};
|
|
878
|
+
const reserved = Object.keys(body).filter((key) => RESERVED_FIELDS.has(key));
|
|
879
|
+
if (reserved.length > 0) throw new _warlock_js_ai.InvalidRequestError(`providerOptions.google.body cannot set ${reserved.join(", ")}: use the call's messages, tools and model options.`, { context: { fields: reserved } });
|
|
880
|
+
const defaults = built;
|
|
881
|
+
const overrides = Object.fromEntries(Object.entries(body).map(([key, value]) => {
|
|
882
|
+
const previous = defaults[key];
|
|
883
|
+
return [key, isPlainObject(previous) && isPlainObject(value) ? {
|
|
884
|
+
...previous,
|
|
885
|
+
...value
|
|
886
|
+
} : value];
|
|
887
|
+
}));
|
|
888
|
+
return {
|
|
889
|
+
...built,
|
|
890
|
+
...overrides
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
/** Merge only plain objects; arrays, null and provider scalar values replace the field. */
|
|
894
|
+
function isPlainObject(value) {
|
|
895
|
+
if (value === null || typeof value !== "object") return false;
|
|
896
|
+
const prototype = Object.getPrototypeOf(value);
|
|
897
|
+
return prototype === Object.prototype || prototype === null;
|
|
898
|
+
}
|
|
899
|
+
|
|
750
900
|
//#endregion
|
|
751
901
|
//#region ../../packages/ai-google/src/known-vision-models.ts
|
|
752
902
|
/**
|
|
@@ -787,6 +937,8 @@ function inferVisionCapability(modelId) {
|
|
|
787
937
|
//#endregion
|
|
788
938
|
//#region ../../packages/ai-google/src/model.ts
|
|
789
939
|
const LOG_MODULE = "ai.google";
|
|
940
|
+
/** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */
|
|
941
|
+
const GOOGLE_WIRE_FORMAT = "google";
|
|
790
942
|
/**
|
|
791
943
|
* Bucketed `thinkingBudget` (token caps) for the neutral
|
|
792
944
|
* `reasoning.effort` levels when the caller gives no explicit
|
|
@@ -881,22 +1033,90 @@ var GoogleModel = class {
|
|
|
881
1033
|
const toolCalls = this.extractToolCalls(response);
|
|
882
1034
|
const finishReason = toolCalls ? "tool_calls" : mapFinishReason(response.candidates?.[0]?.finishReason);
|
|
883
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);
|
|
884
1040
|
this.logger.debug(LOG_MODULE, "response", "generateContent call succeeded", {
|
|
885
1041
|
finishReason,
|
|
886
1042
|
usage
|
|
887
1043
|
});
|
|
888
1044
|
return {
|
|
1045
|
+
...providerMetadata ? { providerMetadata } : {},
|
|
889
1046
|
content: response.text ?? "",
|
|
890
1047
|
finishReason,
|
|
891
1048
|
usage,
|
|
892
1049
|
toolCalls
|
|
893
1050
|
};
|
|
894
1051
|
}
|
|
1052
|
+
/** Count native input tokens with the same converted content, system and tools. */
|
|
1053
|
+
async countTokens(messages, options) {
|
|
1054
|
+
const { systemInstruction, contents } = toGoogleContents(messages);
|
|
1055
|
+
const config = this.buildConfig(systemInstruction, options);
|
|
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" });
|
|
1057
|
+
try {
|
|
1058
|
+
const response = this.ai.vertexai ? await this.ai.models.countTokens({
|
|
1059
|
+
model: this.name,
|
|
1060
|
+
contents,
|
|
1061
|
+
config: {
|
|
1062
|
+
...config.systemInstruction !== void 0 ? { systemInstruction: config.systemInstruction } : {},
|
|
1063
|
+
...config.tools !== void 0 ? { tools: config.tools } : {},
|
|
1064
|
+
...config.abortSignal ? { abortSignal: config.abortSignal } : {},
|
|
1065
|
+
...config.httpOptions ? { httpOptions: config.httpOptions } : {}
|
|
1066
|
+
}
|
|
1067
|
+
}) : await this.countDeveloperTokens(contents, config);
|
|
1068
|
+
if (response.totalTokens === void 0) throw new _warlock_js_ai.InvalidRequestError("Google countTokens returned no totalTokens.");
|
|
1069
|
+
return response.totalTokens;
|
|
1070
|
+
} catch (thrown) {
|
|
1071
|
+
throw this.logAndWrap(thrown);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
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
|
+
}
|
|
895
1110
|
/**
|
|
896
1111
|
* Incremental streaming completion via `generateContentStream`.
|
|
897
1112
|
* Yields neutral `ModelStreamChunk`s — `delta` for text, `tool-call`
|
|
898
1113
|
* per function call (Gemini emits a fully-formed call, not partial
|
|
899
1114
|
* JSON), and a terminal `done` with the final finish reason + usage.
|
|
1115
|
+
*
|
|
1116
|
+
* With `options.streamChunks` it also yields `reasoning` for thought
|
|
1117
|
+
* parts (only sent when `thinkingConfig.includeThoughts` is on — set it
|
|
1118
|
+
* through `providerOptions.google.body`) and `usage` whenever a chunk
|
|
1119
|
+
* carries `usageMetadata`. No `tool-args`: calls arrive whole.
|
|
900
1120
|
*/
|
|
901
1121
|
async *stream(messages, options) {
|
|
902
1122
|
this.logger.debug(LOG_MODULE, "request", "Starting generateContentStream call", {
|
|
@@ -923,8 +1143,17 @@ var GoogleModel = class {
|
|
|
923
1143
|
output: 0,
|
|
924
1144
|
total: 0
|
|
925
1145
|
};
|
|
1146
|
+
const want = (0, _warlock_js_ai.resolveStreamChunks)(options);
|
|
1147
|
+
const grounding = /* @__PURE__ */ new Map();
|
|
926
1148
|
try {
|
|
927
1149
|
for await (const chunk of iterable) {
|
|
1150
|
+
applyGrounding(usage, grounding, chunk);
|
|
1151
|
+
if (want.reasoning) {
|
|
1152
|
+
for (const part of chunk.candidates?.[0]?.content?.parts ?? []) if (part.thought === true && typeof part.text === "string" && part.text !== "") yield {
|
|
1153
|
+
type: "reasoning",
|
|
1154
|
+
delta: part.text
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
928
1157
|
const text = chunk.text;
|
|
929
1158
|
if (text) yield {
|
|
930
1159
|
type: "delta",
|
|
@@ -944,20 +1173,29 @@ var GoogleModel = class {
|
|
|
944
1173
|
}
|
|
945
1174
|
const candidateFinish = chunk.candidates?.[0]?.finishReason;
|
|
946
1175
|
if (candidateFinish) rawFinishReason = candidateFinish;
|
|
947
|
-
if (chunk.usageMetadata)
|
|
1176
|
+
if (chunk.usageMetadata) {
|
|
1177
|
+
applyGoogleUsage(usage, chunk.usageMetadata);
|
|
1178
|
+
if (want.usage) yield {
|
|
1179
|
+
type: "usage",
|
|
1180
|
+
usage: { ...usage }
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
948
1183
|
}
|
|
949
1184
|
} catch (thrown) {
|
|
950
1185
|
throw this.logAndWrap(thrown);
|
|
951
1186
|
}
|
|
952
1187
|
const finishReason = sawToolCall ? "tool_calls" : mapFinishReason(rawFinishReason);
|
|
1188
|
+
(0, _warlock_js_ai.attachCost)(usage, this.pricing);
|
|
953
1189
|
this.logger.debug(LOG_MODULE, "response", "generateContentStream call succeeded", {
|
|
954
1190
|
finishReason,
|
|
955
1191
|
usage
|
|
956
1192
|
});
|
|
1193
|
+
const providerMetadata = groundingMetadata(grounding);
|
|
957
1194
|
yield {
|
|
958
1195
|
type: "done",
|
|
959
1196
|
finishReason,
|
|
960
|
-
usage
|
|
1197
|
+
usage,
|
|
1198
|
+
...providerMetadata ? { providerMetadata } : {}
|
|
961
1199
|
};
|
|
962
1200
|
}
|
|
963
1201
|
/**
|
|
@@ -969,15 +1207,15 @@ var GoogleModel = class {
|
|
|
969
1207
|
buildConfig(systemInstruction, options) {
|
|
970
1208
|
const temperature = options?.temperature ?? this.config.temperature;
|
|
971
1209
|
const maxOutputTokens = options?.maxTokens ?? this.config.maxTokens;
|
|
972
|
-
return {
|
|
1210
|
+
return mergeProviderBody({
|
|
973
1211
|
...systemInstruction ? { systemInstruction } : {},
|
|
974
1212
|
...temperature !== void 0 ? { temperature } : {},
|
|
975
1213
|
...maxOutputTokens !== void 0 ? { maxOutputTokens } : {},
|
|
976
1214
|
...options?.signal ? { abortSignal: options.signal } : {},
|
|
977
|
-
...this.buildTools(options
|
|
1215
|
+
...this.buildTools(options),
|
|
978
1216
|
...this.buildStructuredOutput(options?.responseSchema),
|
|
979
1217
|
...this.buildThinking(options?.reasoning)
|
|
980
|
-
};
|
|
1218
|
+
}, options);
|
|
981
1219
|
}
|
|
982
1220
|
/**
|
|
983
1221
|
* Translate the neutral `reasoning` option into Gemini's
|
|
@@ -1002,11 +1240,21 @@ var GoogleModel = class {
|
|
|
1002
1240
|
}
|
|
1003
1241
|
/**
|
|
1004
1242
|
* Spread-friendly tools fragment. Empty object when no tools were
|
|
1005
|
-
* supplied so the caller can unconditionally spread it.
|
|
1243
|
+
* supplied so the caller can unconditionally spread it. Function tools
|
|
1244
|
+
* come first; `ai.providerTool({ provider: "google" })` definitions
|
|
1245
|
+
* (`{ googleSearch: {} }`, `{ codeExecution: {} }`, `{ urlContext: {} }`,
|
|
1246
|
+
* …) follow verbatim as their own `Tool` entries. Other providers'
|
|
1247
|
+
* definitions are skipped with a warning.
|
|
1006
1248
|
*/
|
|
1007
|
-
buildTools(
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1249
|
+
buildTools(options) {
|
|
1250
|
+
const { tools, definitions } = (0, _warlock_js_ai.selectProviderTools)(options, {
|
|
1251
|
+
accepts: [GOOGLE_WIRE_FORMAT, this.provider],
|
|
1252
|
+
module: LOG_MODULE,
|
|
1253
|
+
model: this.name,
|
|
1254
|
+
logger: this.logger
|
|
1255
|
+
});
|
|
1256
|
+
const mapped = [...toGoogleTools(tools) ?? [], ...definitions];
|
|
1257
|
+
return mapped.length > 0 ? { tools: mapped } : {};
|
|
1010
1258
|
}
|
|
1011
1259
|
/**
|
|
1012
1260
|
* Translate the neutral `responseSchema` into Gemini's native JSON
|
|
@@ -1154,11 +1402,12 @@ var GoogleSDK = class {
|
|
|
1154
1402
|
* `GoogleGenAI` client. The SDK's `provider` label is forwarded.
|
|
1155
1403
|
*
|
|
1156
1404
|
* Pricing resolution: per-model `config.pricing` wins; otherwise the
|
|
1157
|
-
* SDK-level registry entry keyed by `config.name`; otherwise
|
|
1158
|
-
* `
|
|
1405
|
+
* SDK-level registry entry keyed by `config.name`; otherwise the
|
|
1406
|
+
* built-in list price (`GOOGLE_MODEL_PRICING`); otherwise `undefined`
|
|
1407
|
+
* (no cost computed).
|
|
1159
1408
|
*/
|
|
1160
1409
|
model(config) {
|
|
1161
|
-
const resolvedPricing = config.pricing ?? this.pricing?.[config.name];
|
|
1410
|
+
const resolvedPricing = config.pricing ?? this.pricing?.[config.name] ?? googlePricingFor(config.name);
|
|
1162
1411
|
const resolvedConfig = resolvedPricing === config.pricing ? config : {
|
|
1163
1412
|
...config,
|
|
1164
1413
|
pricing: resolvedPricing
|
|
@@ -1219,7 +1468,10 @@ var GoogleSDK = class {
|
|
|
1219
1468
|
};
|
|
1220
1469
|
|
|
1221
1470
|
//#endregion
|
|
1471
|
+
exports.GOOGLE_MODEL_PRICING = GOOGLE_MODEL_PRICING;
|
|
1472
|
+
exports.GOOGLE_SERVER_TOOL_PRICING = GOOGLE_SERVER_TOOL_PRICING;
|
|
1222
1473
|
exports.GeminiImageModel = GeminiImageModel;
|
|
1223
1474
|
exports.GoogleImageModel = GoogleImageModel;
|
|
1224
1475
|
exports.GoogleSDK = GoogleSDK;
|
|
1476
|
+
exports.googlePricingFor = googlePricingFor;
|
|
1225
1477
|
//# sourceMappingURL=index.cjs.map
|