@warlock.js/ai-google 5.30.0 → 5.31.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 +20 -1
- package/cjs/index.cjs +195 -20
- package/cjs/index.cjs.map +1 -1
- package/esm/config.type.d.mts.map +1 -1
- package/esm/embedder.mjs.map +1 -1
- package/esm/gemini-image.mjs.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 +33 -0
- package/esm/known-pricing.d.mts.map +1 -0
- package/esm/known-pricing.mjs +38 -0
- package/esm/known-pricing.mjs.map +1 -0
- package/esm/model.mjs +65 -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 +3 -2
- 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/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 +113 -31
- package/llms.txt +1 -1
- package/package.json +3 -3
- package/skills/setup-google/SKILL.md +113 -31
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ 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.31.0 - 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Provider body options merge plain-object fields one level deep, preserving neutral reasoning, generation and structured-output settings unless the caller overrides their keys.
|
|
12
|
+
- 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.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Usage is marked `inputIncludesCache: true` (`promptTokenCount` includes cached content), and `complete()` / the streaming `done` chunk attach `usage.cost` when priced.
|
|
17
|
+
- 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.
|
|
18
|
+
- 503 `UNAVAILABLE` throws `ProviderOverloadedError`. 400 and 500 failures are marked `billed: false`, as Google's billing docs state.
|
|
19
|
+
- An adapter contract test verifies that aborting `signal` rejects `complete()` and `stream()` on the model and cancels the HTTP request.
|
|
20
|
+
- With `streamChunks`, `stream()` yields `reasoning` for thought parts (sent when `thinkingConfig.includeThoughts` is on) and `usage` for every chunk carrying `usageMetadata`.
|
|
21
|
+
- `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.
|
|
22
|
+
- `providerOptions.google.body` forwards native per-call request options for completions and streams while protecting the model, conversation and tool definitions.
|
|
23
|
+
- Models expose native `countTokens()` preflight. Vertex counts system instructions and tools; unsupported Developer API system/tool and cached-content counts fail explicitly.
|
|
24
|
+
|
|
7
25
|
## 5.30.0 - 2026-10-16
|
|
8
26
|
|
|
9
27
|
### Changed
|
|
@@ -81,6 +99,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
81
99
|
### Changed
|
|
82
100
|
|
|
83
101
|
- Lockstep release maintenance and dependency refresh.
|
|
102
|
+
|
|
84
103
|
## 5.20.0 - 2026-09-24
|
|
85
104
|
|
|
86
105
|
### Changed
|
|
@@ -154,7 +173,7 @@ changelogs.
|
|
|
154
173
|
|
|
155
174
|
### Deprecated
|
|
156
175
|
|
|
157
|
-
- **Google has deprecated `generateImages`, the transport the `imagen-*` path still uses.** Verbatim from the `@google/genai` runtime warning:
|
|
176
|
+
- **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
177
|
|
|
159
178
|
### Changed
|
|
160
179
|
|
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
|
/**
|
|
@@ -747,6 +797,74 @@ 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
|
+
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region ../../packages/ai-google/src/provider-options.ts
|
|
838
|
+
const RESERVED_FIELDS = new Set([
|
|
839
|
+
"systemInstruction",
|
|
840
|
+
"tools",
|
|
841
|
+
"abortSignal",
|
|
842
|
+
"httpOptions"
|
|
843
|
+
]);
|
|
844
|
+
function mergeProviderBody(built, options) {
|
|
845
|
+
const body = (options?.providerOptions?.google)?.body ?? {};
|
|
846
|
+
const reserved = Object.keys(body).filter((key) => RESERVED_FIELDS.has(key));
|
|
847
|
+
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 } });
|
|
848
|
+
const defaults = built;
|
|
849
|
+
const overrides = Object.fromEntries(Object.entries(body).map(([key, value]) => {
|
|
850
|
+
const previous = defaults[key];
|
|
851
|
+
return [key, isPlainObject(previous) && isPlainObject(value) ? {
|
|
852
|
+
...previous,
|
|
853
|
+
...value
|
|
854
|
+
} : value];
|
|
855
|
+
}));
|
|
856
|
+
return {
|
|
857
|
+
...built,
|
|
858
|
+
...overrides
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
/** Merge only plain objects; arrays, null and provider scalar values replace the field. */
|
|
862
|
+
function isPlainObject(value) {
|
|
863
|
+
if (value === null || typeof value !== "object") return false;
|
|
864
|
+
const prototype = Object.getPrototypeOf(value);
|
|
865
|
+
return prototype === Object.prototype || prototype === null;
|
|
866
|
+
}
|
|
867
|
+
|
|
750
868
|
//#endregion
|
|
751
869
|
//#region ../../packages/ai-google/src/known-vision-models.ts
|
|
752
870
|
/**
|
|
@@ -787,6 +905,8 @@ function inferVisionCapability(modelId) {
|
|
|
787
905
|
//#endregion
|
|
788
906
|
//#region ../../packages/ai-google/src/model.ts
|
|
789
907
|
const LOG_MODULE = "ai.google";
|
|
908
|
+
/** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */
|
|
909
|
+
const GOOGLE_WIRE_FORMAT = "google";
|
|
790
910
|
/**
|
|
791
911
|
* Bucketed `thinkingBudget` (token caps) for the neutral
|
|
792
912
|
* `reasoning.effort` levels when the caller gives no explicit
|
|
@@ -880,7 +1000,7 @@ var GoogleModel = class {
|
|
|
880
1000
|
}
|
|
881
1001
|
const toolCalls = this.extractToolCalls(response);
|
|
882
1002
|
const finishReason = toolCalls ? "tool_calls" : mapFinishReason(response.candidates?.[0]?.finishReason);
|
|
883
|
-
const usage = this.extractUsage(response);
|
|
1003
|
+
const usage = (0, _warlock_js_ai.attachCost)(this.extractUsage(response), this.pricing);
|
|
884
1004
|
this.logger.debug(LOG_MODULE, "response", "generateContent call succeeded", {
|
|
885
1005
|
finishReason,
|
|
886
1006
|
usage
|
|
@@ -892,11 +1012,39 @@ var GoogleModel = class {
|
|
|
892
1012
|
toolCalls
|
|
893
1013
|
};
|
|
894
1014
|
}
|
|
1015
|
+
/** Count native input tokens with the same converted content, system and tools. */
|
|
1016
|
+
async countTokens(messages, options) {
|
|
1017
|
+
const { systemInstruction, contents } = toGoogleContents(messages);
|
|
1018
|
+
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.");
|
|
1021
|
+
try {
|
|
1022
|
+
const response = await this.ai.models.countTokens({
|
|
1023
|
+
model: this.name,
|
|
1024
|
+
contents,
|
|
1025
|
+
config: {
|
|
1026
|
+
...config.systemInstruction !== void 0 ? { systemInstruction: config.systemInstruction } : {},
|
|
1027
|
+
...config.tools !== void 0 ? { tools: config.tools } : {},
|
|
1028
|
+
...config.abortSignal ? { abortSignal: config.abortSignal } : {},
|
|
1029
|
+
...config.httpOptions ? { httpOptions: config.httpOptions } : {}
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
if (response.totalTokens === void 0) throw new _warlock_js_ai.InvalidRequestError("Google countTokens returned no totalTokens.");
|
|
1033
|
+
return response.totalTokens;
|
|
1034
|
+
} catch (thrown) {
|
|
1035
|
+
throw this.logAndWrap(thrown);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
895
1038
|
/**
|
|
896
1039
|
* Incremental streaming completion via `generateContentStream`.
|
|
897
1040
|
* Yields neutral `ModelStreamChunk`s — `delta` for text, `tool-call`
|
|
898
1041
|
* per function call (Gemini emits a fully-formed call, not partial
|
|
899
1042
|
* JSON), and a terminal `done` with the final finish reason + usage.
|
|
1043
|
+
*
|
|
1044
|
+
* With `options.streamChunks` it also yields `reasoning` for thought
|
|
1045
|
+
* parts (only sent when `thinkingConfig.includeThoughts` is on — set it
|
|
1046
|
+
* through `providerOptions.google.body`) and `usage` whenever a chunk
|
|
1047
|
+
* carries `usageMetadata`. No `tool-args`: calls arrive whole.
|
|
900
1048
|
*/
|
|
901
1049
|
async *stream(messages, options) {
|
|
902
1050
|
this.logger.debug(LOG_MODULE, "request", "Starting generateContentStream call", {
|
|
@@ -923,8 +1071,15 @@ var GoogleModel = class {
|
|
|
923
1071
|
output: 0,
|
|
924
1072
|
total: 0
|
|
925
1073
|
};
|
|
1074
|
+
const want = (0, _warlock_js_ai.resolveStreamChunks)(options);
|
|
926
1075
|
try {
|
|
927
1076
|
for await (const chunk of iterable) {
|
|
1077
|
+
if (want.reasoning) {
|
|
1078
|
+
for (const part of chunk.candidates?.[0]?.content?.parts ?? []) if (part.thought === true && typeof part.text === "string" && part.text !== "") yield {
|
|
1079
|
+
type: "reasoning",
|
|
1080
|
+
delta: part.text
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
928
1083
|
const text = chunk.text;
|
|
929
1084
|
if (text) yield {
|
|
930
1085
|
type: "delta",
|
|
@@ -944,12 +1099,19 @@ var GoogleModel = class {
|
|
|
944
1099
|
}
|
|
945
1100
|
const candidateFinish = chunk.candidates?.[0]?.finishReason;
|
|
946
1101
|
if (candidateFinish) rawFinishReason = candidateFinish;
|
|
947
|
-
if (chunk.usageMetadata)
|
|
1102
|
+
if (chunk.usageMetadata) {
|
|
1103
|
+
applyGoogleUsage(usage, chunk.usageMetadata);
|
|
1104
|
+
if (want.usage) yield {
|
|
1105
|
+
type: "usage",
|
|
1106
|
+
usage: { ...usage }
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
948
1109
|
}
|
|
949
1110
|
} catch (thrown) {
|
|
950
1111
|
throw this.logAndWrap(thrown);
|
|
951
1112
|
}
|
|
952
1113
|
const finishReason = sawToolCall ? "tool_calls" : mapFinishReason(rawFinishReason);
|
|
1114
|
+
(0, _warlock_js_ai.attachCost)(usage, this.pricing);
|
|
953
1115
|
this.logger.debug(LOG_MODULE, "response", "generateContentStream call succeeded", {
|
|
954
1116
|
finishReason,
|
|
955
1117
|
usage
|
|
@@ -969,15 +1131,15 @@ var GoogleModel = class {
|
|
|
969
1131
|
buildConfig(systemInstruction, options) {
|
|
970
1132
|
const temperature = options?.temperature ?? this.config.temperature;
|
|
971
1133
|
const maxOutputTokens = options?.maxTokens ?? this.config.maxTokens;
|
|
972
|
-
return {
|
|
1134
|
+
return mergeProviderBody({
|
|
973
1135
|
...systemInstruction ? { systemInstruction } : {},
|
|
974
1136
|
...temperature !== void 0 ? { temperature } : {},
|
|
975
1137
|
...maxOutputTokens !== void 0 ? { maxOutputTokens } : {},
|
|
976
1138
|
...options?.signal ? { abortSignal: options.signal } : {},
|
|
977
|
-
...this.buildTools(options
|
|
1139
|
+
...this.buildTools(options),
|
|
978
1140
|
...this.buildStructuredOutput(options?.responseSchema),
|
|
979
1141
|
...this.buildThinking(options?.reasoning)
|
|
980
|
-
};
|
|
1142
|
+
}, options);
|
|
981
1143
|
}
|
|
982
1144
|
/**
|
|
983
1145
|
* Translate the neutral `reasoning` option into Gemini's
|
|
@@ -1002,11 +1164,21 @@ var GoogleModel = class {
|
|
|
1002
1164
|
}
|
|
1003
1165
|
/**
|
|
1004
1166
|
* Spread-friendly tools fragment. Empty object when no tools were
|
|
1005
|
-
* supplied so the caller can unconditionally spread it.
|
|
1167
|
+
* supplied so the caller can unconditionally spread it. Function tools
|
|
1168
|
+
* come first; `ai.providerTool({ provider: "google" })` definitions
|
|
1169
|
+
* (`{ googleSearch: {} }`, `{ codeExecution: {} }`, `{ urlContext: {} }`,
|
|
1170
|
+
* …) follow verbatim as their own `Tool` entries. Other providers'
|
|
1171
|
+
* definitions are skipped with a warning.
|
|
1006
1172
|
*/
|
|
1007
|
-
buildTools(
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1173
|
+
buildTools(options) {
|
|
1174
|
+
const { tools, definitions } = (0, _warlock_js_ai.selectProviderTools)(options, {
|
|
1175
|
+
accepts: [GOOGLE_WIRE_FORMAT, this.provider],
|
|
1176
|
+
module: LOG_MODULE,
|
|
1177
|
+
model: this.name,
|
|
1178
|
+
logger: this.logger
|
|
1179
|
+
});
|
|
1180
|
+
const mapped = [...toGoogleTools(tools) ?? [], ...definitions];
|
|
1181
|
+
return mapped.length > 0 ? { tools: mapped } : {};
|
|
1010
1182
|
}
|
|
1011
1183
|
/**
|
|
1012
1184
|
* Translate the neutral `responseSchema` into Gemini's native JSON
|
|
@@ -1154,11 +1326,12 @@ var GoogleSDK = class {
|
|
|
1154
1326
|
* `GoogleGenAI` client. The SDK's `provider` label is forwarded.
|
|
1155
1327
|
*
|
|
1156
1328
|
* Pricing resolution: per-model `config.pricing` wins; otherwise the
|
|
1157
|
-
* SDK-level registry entry keyed by `config.name`; otherwise
|
|
1158
|
-
* `
|
|
1329
|
+
* SDK-level registry entry keyed by `config.name`; otherwise the
|
|
1330
|
+
* built-in list price (`GOOGLE_MODEL_PRICING`); otherwise `undefined`
|
|
1331
|
+
* (no cost computed).
|
|
1159
1332
|
*/
|
|
1160
1333
|
model(config) {
|
|
1161
|
-
const resolvedPricing = config.pricing ?? this.pricing?.[config.name];
|
|
1334
|
+
const resolvedPricing = config.pricing ?? this.pricing?.[config.name] ?? googlePricingFor(config.name);
|
|
1162
1335
|
const resolvedConfig = resolvedPricing === config.pricing ? config : {
|
|
1163
1336
|
...config,
|
|
1164
1337
|
pricing: resolvedPricing
|
|
@@ -1219,7 +1392,9 @@ var GoogleSDK = class {
|
|
|
1219
1392
|
};
|
|
1220
1393
|
|
|
1221
1394
|
//#endregion
|
|
1395
|
+
exports.GOOGLE_MODEL_PRICING = GOOGLE_MODEL_PRICING;
|
|
1222
1396
|
exports.GeminiImageModel = GeminiImageModel;
|
|
1223
1397
|
exports.GoogleImageModel = GoogleImageModel;
|
|
1224
1398
|
exports.GoogleSDK = GoogleSDK;
|
|
1399
|
+
exports.googlePricingFor = googlePricingFor;
|
|
1225
1400
|
//# sourceMappingURL=index.cjs.map
|