@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/llms-full.txt
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
name: setup-google
|
|
11
|
-
description: 'Wire @warlock.js/ai-google — new GoogleSDK({apiKey} | {vertexai, project, location}) for Gemini API + Vertex AI. generateContent / embedContent + thoughtSignature round-trip for thinking models, batched embeddings. .model({name, vision?, reasoning?, audio?, pdf?}) with cost-truth capabilities (PDF + audio input map to Gemini inlineData), .image({name, pricing?}) for image generation via ai.image — the id picks the transport: gemini-* → generateContent + responseModalities IMAGE (usage passed through; generateImages is deprecated by Google), everything else → generateImages (Imagen, per-image, zero usage) — extended thinking via options.reasoning → thinkingConfig.thinkingBudget, usage reasoningTokens (thoughtsTokenCount) / cachedTokens (cachedContentTokenCount). Triggers: `GoogleSDK`, `google.model`, `google.embedder`, `google.image`, `imagen`, `generateImages`, `gemini image`, `nano banana`, `responseModalities`, `imageConfig`, `ai.image`, `inlineData`, `pdf input`, `audio input`, `thoughtSignature`, `responseJsonSchema`, `vertexai`, `reasoning`, `thinkingConfig`, `thinkingBudget`, `thoughtsTokenCount`, `reasoningTokens`, `cachedTokens`, `promptCaching`, `cacheControl`; "use gemini", "wire Vertex AI", "gemini embeddings", "gemini thinking tool calls", "gemini 2.5 thinking budget", "gemini cached content cost", "generate images with imagen", "generate an image with gemini", "gemini image model 404 not supported for predict", "send a pdf / audio to gemini"; import `import { GoogleSDK } from "@warlock.js/ai-google"`. Skip: the ai.image verb surface — the `generate-images` topic of the `warlock-js-ai` skill; agent loop the `run-ai-agent` topic of the `warlock-js-ai` skill; provider picking the `pick-ai-provider` topic of the `warlock-js-ai` skill; embedder usage the `embed-text` topic of the `warlock-js-ai` skill; siblings `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-ollama`; raw `@google/genai`, `@google-cloud/vertexai`, Vercel `@ai-sdk/google`.'
|
|
11
|
+
description: 'Wire @warlock.js/ai-google — new GoogleSDK({apiKey} | {vertexai, project, location}) for Gemini API + Vertex AI. generateContent / embedContent + thoughtSignature round-trip for thinking models, batched embeddings. .model({name, vision?, reasoning?, audio?, pdf?}) with cost-truth capabilities (PDF + audio input map to Gemini inlineData), .image({name, pricing?}) for image generation via ai.image — the id picks the transport: gemini-* → generateContent + responseModalities IMAGE (usage passed through; generateImages is deprecated by Google), everything else → generateImages (Imagen, per-image, zero usage) — extended thinking via options.reasoning → thinkingConfig.thinkingBudget, usage reasoningTokens (thoughtsTokenCount) / cachedTokens (cachedContentTokenCount). Triggers: `providerOptions`, `GoogleCallOptions`, `countTokens`, `GoogleSDK`, `google.model`, `google.embedder`, `google.image`, `imagen`, `generateImages`, `gemini image`, `nano banana`, `responseModalities`, `imageConfig`, `ai.image`, `inlineData`, `pdf input`, `audio input`, `thoughtSignature`, `responseJsonSchema`, `ai.providerTool`, `googleSearch`, `includeThoughts`, `streamChunks`, `vertexai`, `reasoning`, `thinkingConfig`, `thinkingBudget`, `thoughtsTokenCount`, `reasoningTokens`, `cachedTokens`, `inputIncludesCache`, `GOOGLE_MODEL_PRICING`, `googlePricingFor`, `ProviderOverloadedError`, `promptCaching`, `cacheControl`; "use gemini", "wire Vertex AI", "gemini embeddings", "gemini thinking tool calls", "gemini 2.5 thinking budget", "gemini cached content cost", "generate images with imagen", "generate an image with gemini", "gemini image model 404 not supported for predict", "send a pdf / audio to gemini"; import `import { GoogleSDK } from "@warlock.js/ai-google"`. Skip: the ai.image verb surface — the `generate-images` topic of the `warlock-js-ai` skill; agent loop the `run-ai-agent` topic of the `warlock-js-ai` skill; provider picking the `pick-ai-provider` topic of the `warlock-js-ai` skill; embedder usage the `embed-text` topic of the `warlock-js-ai` skill; siblings `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-ollama`; raw `@google/genai`, `@google-cloud/vertexai`, Vercel `@ai-sdk/google`.'
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# `@warlock.js/ai-google`
|
|
@@ -37,21 +37,21 @@ const vertex = new GoogleSDK({
|
|
|
37
37
|
## Producing a model
|
|
38
38
|
|
|
39
39
|
```ts
|
|
40
|
-
google.model({ name: "gemini-2.5-flash" })
|
|
41
|
-
google.model({ name: "gemini-2.5-pro", temperature: 0.2 })
|
|
42
|
-
google.model({ name: "gemini-1.5-flash", maxTokens: 2048 })
|
|
40
|
+
google.model({ name: "gemini-2.5-flash" });
|
|
41
|
+
google.model({ name: "gemini-2.5-pro", temperature: 0.2 });
|
|
42
|
+
google.model({ name: "gemini-1.5-flash", maxTokens: 2048 });
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Capabilities — what's auto-set
|
|
46
46
|
|
|
47
|
-
| Flag
|
|
48
|
-
|
|
|
49
|
-
| `structuredOutput` | `true` (via `responseMimeType` + `responseJsonSchema`)
|
|
50
|
-
| `vision`
|
|
51
|
-
| `reasoning`
|
|
52
|
-
| `promptCaching`
|
|
53
|
-
| `audio`
|
|
54
|
-
| `pdf`
|
|
47
|
+
| Flag | Default |
|
|
48
|
+
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `structuredOutput` | `true` (via `responseMimeType` + `responseJsonSchema`) |
|
|
50
|
+
| `vision` | Inferred from model id substring. `true` when the id contains `gemini-1.5`, `gemini-2` (covers 2.x / 2.5), `gemini-exp`, `gemini-flash`, or `gemini-pro-vision`; `false` for bare `gemini-pro` / `gemini-1.0-pro` and any unknown id. Case-insensitive, tolerates date/preview/vendor-prefix suffixes (`models/gemini-1.5-flash-001`). |
|
|
51
|
+
| `reasoning` | `true` — every Gemini 2.5 model thinks; older families harmlessly ignore an empty budget. Set `reasoning: false` to stop forwarding `thinkingConfig` to a model that rejects it. |
|
|
52
|
+
| `promptCaching` | Always `true` — Gemini reports cache-read hits (`cachedContentTokenCount`) via implicit caching and accepts explicit context caching. |
|
|
53
|
+
| `audio` | Mirrors the `vision` inference (the multimodal Gemini families accept audio parts). Override with `audio: true \| false`. |
|
|
54
|
+
| `pdf` | Mirrors the `vision` inference (the multimodal Gemini families accept PDF / document parts). Override with `pdf: true \| false`. |
|
|
55
55
|
|
|
56
56
|
Explicit config always wins (`google.model({ name, reasoning: false, audio: false, pdf: true })`).
|
|
57
57
|
|
|
@@ -64,6 +64,15 @@ Gemini content roles must be `"user"` or `"model"` — there is no `"system"` ro
|
|
|
64
64
|
- Neutral `assistant` → Gemini `"model"`; `user` stays `"user"`.
|
|
65
65
|
- Assistant tool calls → `"model"` content: optional leading `{ text }` + one `{ functionCall: { name, args } }` part per call (`id` deliberately omitted — see below).
|
|
66
66
|
- Tool results (`role: "tool"`) → a `"user"` content with one `{ functionResponse: { name, response } }` part. `response` must be a JSON object, so a stringified-JSON tool result is parsed back to an object; a non-object result is wrapped as `{ result: <raw> }`.
|
|
67
|
+
- Adjacent contents with the same role merge into one, parts kept in order: parallel tool results become ONE `"user"` content with several `functionResponse` parts in call order (as `@google/genai`'s automatic function calling sends them), and an agent compaction summary merges with a same-role neighbour (user-framed summary + input; assistant summary + the next tool-call turn, text part first, each `functionCall` keeping its `thoughtSignature`). Roles therefore alternate `user`/`model`; `countTokens()` uses the same builder.
|
|
68
|
+
|
|
69
|
+
Neutral history and the `contents` the adapter sends for it:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
user "look both up" → { role: "user", parts: [{ text }] }
|
|
73
|
+
assistant toolCalls [a, b] → { role: "model", parts: [{ functionCall: a }, { functionCall: b }] }
|
|
74
|
+
tool a, tool b → { role: "user", parts: [{ functionResponse: a }, { functionResponse: b }] }
|
|
75
|
+
```
|
|
67
76
|
|
|
68
77
|
**Gemini matches a result to its call by name, not id** — the Developer API assigns no function-call ids. The adapter sets neutral `id` = tool name so `toolCallId` is non-empty. The wire `id` is omitted from both `functionCall` and `functionResponse` (echoing an empty/synthetic id is rejected by Gemini). **Limitation:** two parallel calls to the same tool in one turn share an id — inherent to the Developer API's id-less design.
|
|
69
78
|
|
|
@@ -91,12 +100,12 @@ PDF and audio reach the wire only when the model declares the matching capabilit
|
|
|
91
100
|
|
|
92
101
|
`google.image({ name })` returns an `ImageModelContract` for the `ai.image()` verb. **The model id picks the transport:**
|
|
93
102
|
|
|
94
|
-
| Model id
|
|
95
|
-
|
|
|
103
|
+
| Model id | Transport | Usage it can report |
|
|
104
|
+
| ----------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
96
105
|
| `gemini-*` (e.g. `gemini-3.1-flash-lite-image`) | `ai.models.generateContent` with `responseModalities` including `"IMAGE"` | whatever `usageMetadata` Google attaches — price with `{ input, output }` |
|
|
97
|
-
| anything else (`imagen-*`, unknown ids)
|
|
106
|
+
| anything else (`imagen-*`, unknown ids) | `ai.models.generateImages` (Imagen) | none, always zero — price with `{ perImage }` |
|
|
98
107
|
|
|
99
|
-
A `gemini-` id sent to `generateImages` comes back `404 … is not supported for predict` (observed verbatim from Google) — `generateImages` routes to `{model}:predict`. `generateContent` is the SDK's own named replacement: its runtime deprecation notice for `generateImages` says
|
|
108
|
+
A `gemini-` id sent to `generateImages` comes back `404 … is not supported for predict` (observed verbatim from Google) — `generateImages` routes to `{model}:predict`. `generateContent` is the SDK's own named replacement: its runtime deprecation notice for `generateImages` says _"Please use the generateContent method with image models instead"_. Routing on the id is what lets both families go through one factory. A leading `models/` resource prefix is tolerated.
|
|
100
109
|
|
|
101
110
|
This is **routing, not validation** — no id is refused locally. An id matching neither family takes the `generateImages` route (the pre-existing one) and fails at Google as a typed provider error, not with a local throw.
|
|
102
111
|
|
|
@@ -107,13 +116,13 @@ This is **routing, not validation** — no id is refused locally. An id matching
|
|
|
107
116
|
```ts
|
|
108
117
|
const gemini = google.image({
|
|
109
118
|
name: "gemini-3.1-flash-lite-image",
|
|
110
|
-
pricing: { input: 0.3, output: 30 },
|
|
119
|
+
pricing: { input: 0.3, output: 30 }, // token-metered
|
|
111
120
|
});
|
|
112
121
|
|
|
113
122
|
const { data, error, usage } = await ai.image({
|
|
114
123
|
model: gemini,
|
|
115
124
|
prompt: "a red bicycle on a white background",
|
|
116
|
-
aspectRatio: "16:9",
|
|
125
|
+
aspectRatio: "16:9", // → config.imageConfig.aspectRatio
|
|
117
126
|
options: { imageSize: "2K", personGeneration: "ALLOW_ADULT" }, // → imageConfig passthroughs
|
|
118
127
|
});
|
|
119
128
|
```
|
|
@@ -135,7 +144,7 @@ const imagen = google.image({ name: "imagen-4.0-generate-001", pricing: { perIma
|
|
|
135
144
|
const { data, error } = await ai.image({
|
|
136
145
|
model: imagen,
|
|
137
146
|
prompt: "a watercolor lighthouse at dawn",
|
|
138
|
-
aspectRatio: "3:4",
|
|
147
|
+
aspectRatio: "3:4", // Imagen ratio (vs OpenAI's WxH `size`)
|
|
139
148
|
negativePrompt: "text, watermark",
|
|
140
149
|
options: { imageSize: "2K", personGeneration: "allow_adult" }, // Imagen passthroughs
|
|
141
150
|
});
|
|
@@ -143,7 +152,7 @@ const { data, error } = await ai.image({
|
|
|
143
152
|
|
|
144
153
|
- Imagen is **per-image-metered** (price with `{ perImage }`) and returns base64 bytes — no hosted URL, and `usage` is an honest `{ input: 0, output: 0, total: 0 }`.
|
|
145
154
|
- When every candidate is safety-filtered (`raiFilteredReason`), the run surfaces a typed `ContentFilterError` on `result.error`.
|
|
146
|
-
- ⏳ **This path is on a clock.** `@google/genai` warns at runtime:
|
|
155
|
+
- ⏳ **This path is on a clock.** `@google/genai` warns at runtime: _"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."_ This package depends on `@google/genai` `^2.17.1`, which does emit that warning. Nothing breaks today, but new work should prefer a `gemini-*` id.
|
|
147
156
|
|
|
148
157
|
### Not implemented: the `interactions` API
|
|
149
158
|
|
|
@@ -155,6 +164,17 @@ The verb surface lives in the `generate-images` topic of the `warlock-js-ai` ski
|
|
|
155
164
|
|
|
156
165
|
`model.stream()` drains `generateContentStream`. Each chunk's `.text` → `{ type: "delta" }`; `functionCall` parts (read from `candidates[0].content.parts`, not the `.functionCalls` getter — the getter drops `thoughtSignature`) are emitted as `{ type: "tool-call" }` **fully formed** (Gemini streams a complete call with parsed `args`, not partial JSON), carrying `providerMetadata.thoughtSignature` when present. Terminal `{ type: "done", finishReason, usage }` — usage from the final chunk's `usageMetadata`.
|
|
157
166
|
|
|
167
|
+
Opt-in chunks (`options.streamChunks`, set by `agent.stream()`): `{ type: "reasoning", delta }` per thought part (`thought: true`; sent only with `providerOptions.google.body.thinkingConfig.includeThoughts: true`) and `{ type: "usage", usage }` per chunk carrying `usageMetadata`. No `tool-args` (calls arrive whole).
|
|
168
|
+
|
|
169
|
+
**Provider-defined tools:** `ai.providerTool({ provider: "google", definition: { googleSearch: {} } })` (also `{ codeExecution: {} }`, `{ urlContext: {} }`) is appended verbatim as its own `Tool` after the function declarations; other providers' tools are skipped with a warning.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
ai.agent({
|
|
173
|
+
model: google.model({ name: "gemini-2.5-flash" }),
|
|
174
|
+
tools: [ai.providerTool({ provider: "google", definition: { googleSearch: {} } })],
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
158
178
|
## Finish-reason mapping
|
|
159
179
|
|
|
160
180
|
`STOP` → `stop` · `MAX_TOKENS` → `length` · `SAFETY` / `RECITATION` / `BLOCKLIST` / `PROHIBITED_CONTENT` / `MALFORMED_FUNCTION_CALL` / unknown / null → `error`. `tool_calls` is derived from function-call presence.
|
|
@@ -164,7 +184,7 @@ The verb surface lives in the `generate-images` topic of the `warlock-js-ai` ski
|
|
|
164
184
|
```ts
|
|
165
185
|
const embedder = google.embedder({ name: "gemini-embedding-001" });
|
|
166
186
|
const { vector } = await embedder.embed("Hello world");
|
|
167
|
-
const { vectors } = await embedder.embedMany(["a", "b"]);
|
|
187
|
+
const { vectors } = await embedder.embedMany(["a", "b"]); // single batched call
|
|
168
188
|
```
|
|
169
189
|
|
|
170
190
|
`embedContent` accepts an array natively, so `embedMany` is **one request** (unlike Bedrock/Titan). Pass `dimensions` to forward Gemini's `outputDimensionality` truncation hint.
|
|
@@ -180,14 +200,31 @@ Wrapped into the typed `@warlock.js/ai` `AIError` hierarchy. Gemini has no machi
|
|
|
180
200
|
- 400 with token/context phrasing → `ContextLengthExceededError`, else `InvalidRequestError`
|
|
181
201
|
- 404 / any other 4xx → `InvalidRequestError`
|
|
182
202
|
- 504 / `DEADLINE_EXCEEDED` / `ETIMEDOUT` / `ECONNABORTED` / `AbortError` → `ProviderTimeoutError`
|
|
203
|
+
- 503 / `UNAVAILABLE` / "overloaded" → `ProviderOverloadedError` (`code: "PROVIDER_OVERLOADED"`, `retryable: true`) — Gemini documents 503 as "temporarily overloaded or down" (https://ai.google.dev/gemini-api/docs/api-errors)
|
|
183
204
|
- anything else (5xx and status-less) → `ProviderError`
|
|
184
205
|
|
|
206
|
+
Every wrapped error carries `status`, `retryable` and `billed`. Google states "If your request fails with a 400 or 500 error, you won't be charged for the tokens used" (https://ai.google.dev/gemini-api/docs/billing, checked 2026-10-17), so a 400 or 500 is `billed: false`; 401 / 403 are `false` too (never reached a billable account); everything else is `"unknown"`.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { ai, ProviderError, ProviderOverloadedError } from "@warlock.js/ai";
|
|
210
|
+
import { GoogleSDK } from "@warlock.js/ai-google";
|
|
211
|
+
|
|
212
|
+
const google = new GoogleSDK({ apiKey: process.env.GEMINI_API_KEY ?? "" });
|
|
213
|
+
const result = await ai.agent({ model: google.model({ name: "gemini-2.5-flash" }) }).execute("hi");
|
|
214
|
+
|
|
215
|
+
if (result.error instanceof ProviderOverloadedError) {
|
|
216
|
+
console.log(result.error.status, result.error.retryable); // 503, true — retry or fall back
|
|
217
|
+
} else if (result.error instanceof ProviderError) {
|
|
218
|
+
console.log(result.error.billed); // false for a 400 / 500
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
185
222
|
Timeout is checked first, so an aborted/`AbortError` request always classifies as `ProviderTimeoutError` regardless of status. `AIError` instances pass through unwrapped (no double-wrap).
|
|
186
223
|
|
|
187
224
|
## Token counting
|
|
188
225
|
|
|
189
226
|
```ts
|
|
190
|
-
await google.count("some text")
|
|
227
|
+
await google.count("some text"); // approximate heuristic, offline
|
|
191
228
|
```
|
|
192
229
|
|
|
193
230
|
`count(text, model?)` accepts an optional model arg for signature parity but **ignores it** — the estimate is `approximateTokenCount` (≈ `ceil(chars / 4)`) from `@warlock.js/ai`, never a `countTokens` network call.
|
|
@@ -208,25 +245,81 @@ Gemini's `thinkingBudget` semantics (for direct `maxTokens` use): `0` disables t
|
|
|
208
245
|
|
|
209
246
|
## Token usage
|
|
210
247
|
|
|
211
|
-
`complete()` / `stream()` map Gemini's `usageMetadata` into the neutral `Usage` (`{ input, output, total }`): `promptTokenCount` → `input`, `candidatesTokenCount` → `output`, `totalTokenCount` → `total` (falling back to `input + output` when absent). `cachedContentTokenCount`, when > 0, is surfaced as `usage.cachedTokens`; `thoughtsTokenCount` (a reasoning model's thinking-phase tokens), when > 0, as `usage.reasoningTokens`. Both are omitted when zero/absent. Embeddings have no usage (see above).
|
|
248
|
+
`complete()` / `stream()` map Gemini's `usageMetadata` into the neutral `Usage` (`{ input, output, total }`): `promptTokenCount` → `input`, `candidatesTokenCount` → `output`, `totalTokenCount` → `total` (falling back to `input + output` when absent). `cachedContentTokenCount`, when > 0, is surfaced as `usage.cachedTokens`; `thoughtsTokenCount` (a reasoning model's thinking-phase tokens), when > 0, as `usage.reasoningTokens`. Both are omitted when zero/absent. `promptTokenCount` "includes the number of tokens in the cached content" (https://ai.google.dev/api/generate-content#UsageMetadata), so the usage is marked `inputIncludesCache: true` and cost bills cached tokens out of `input` once. When the model is priced, `usage.cost` is attached on `complete()` and the streaming `done` chunk. Embeddings have no usage (see above).
|
|
212
249
|
|
|
213
250
|
## Pricing
|
|
214
251
|
|
|
215
|
-
|
|
252
|
+
Resolution per `model()` call: per-model `pricing` > SDK-level registry entry > built-in `GOOGLE_MODEL_PRICING` > `undefined` (no cost computed). The resolved `ModelPricing` is attached to the produced model, and each response's `usage.cost` is computed from it.
|
|
216
253
|
|
|
217
254
|
```ts
|
|
218
|
-
|
|
255
|
+
import { totalCost } from "@warlock.js/ai";
|
|
256
|
+
import { GOOGLE_MODEL_PRICING, GoogleSDK, googlePricingFor } from "@warlock.js/ai-google";
|
|
257
|
+
|
|
219
258
|
const google = new GoogleSDK({
|
|
220
|
-
apiKey,
|
|
221
|
-
pricing: { "gemini-2.5-flash": { input: 0.3, output: 2.5 } },
|
|
259
|
+
apiKey: process.env.GEMINI_API_KEY ?? "",
|
|
260
|
+
pricing: { "gemini-2.5-flash": { input: 0.3, output: 2.5, cachedInput: 0.03 } }, // text-input rate
|
|
222
261
|
});
|
|
223
262
|
|
|
224
|
-
google.model({ name: "gemini-2.5-flash" });
|
|
225
|
-
google.model({ name: "gemini-
|
|
226
|
-
google.model({ name: "gemini-2.5-pro" });
|
|
263
|
+
google.model({ name: "gemini-2.5-flash" }); // → the registry entry
|
|
264
|
+
google.model({ name: "gemini-3.5-flash-lite" }).pricing; // → built-in { input: 0.3, output: 2.5, cachedInput: 0.03 }
|
|
265
|
+
google.model({ name: "gemini-2.5-pro" }).pricing; // → undefined (prompt-length tiers; pass pricing yourself)
|
|
266
|
+
googlePricingFor("gemini-3.5-flash-lite"); // the same built-in entry
|
|
267
|
+
Object.keys(GOOGLE_MODEL_PRICING); // the priced ids
|
|
268
|
+
|
|
269
|
+
const { usage } = await google
|
|
270
|
+
.model({ name: "gemini-3.5-flash-lite" })
|
|
271
|
+
.complete([{ role: "user", content: "hi" }]);
|
|
272
|
+
console.log(totalCost(usage.cost));
|
|
227
273
|
```
|
|
228
274
|
|
|
229
|
-
|
|
275
|
+
`GOOGLE_MODEL_PRICING` comes from https://ai.google.dev/gemini-api/docs/pricing (paid tier, Standard), fetched 2026-10-17. Only models with a single flat token price are listed. Deliberately unpriced: the Pro models (priced by prompt length, split at 200k prompt tokens), models whose audio input bills higher than text (`Usage` does not split input by modality — price them with the text rate yourself only if you send no audio), and models with a scheduled price change. The hourly context-cache storage charge is not a per-token rate and is never included.
|
|
276
|
+
|
|
277
|
+
## Provider config options and input-token counts
|
|
278
|
+
|
|
279
|
+
Native body fields merge after neutral options. When both values are plain
|
|
280
|
+
objects, their immediate keys merge and caller-provided keys win; untouched
|
|
281
|
+
neutral fields remain. Arrays, scalars and `null` replace the field. This merge
|
|
282
|
+
is one level deep: a nested object explicitly supplied by the caller replaces
|
|
283
|
+
that nested value.
|
|
284
|
+
|
|
285
|
+
`providerOptions.google.body` adds native `GenerateContentConfig` fields,
|
|
286
|
+
merged after neutral generation options for both completion and streaming:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
const options = { providerOptions: { google: { body: { topP: 0.8, topK: 4 } } } };
|
|
290
|
+
await model.complete(messages, options);
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The key stays `google` for custom provider labels. Other provider keys are
|
|
294
|
+
ignored. `GoogleCallOptions` is exported. `systemInstruction`, `tools`,
|
|
295
|
+
`abortSignal` and `httpOptions` are protected and throw `InvalidRequestError`;
|
|
296
|
+
use neutral messages/tools/signal and SDK transport configuration for them.
|
|
297
|
+
|
|
298
|
+
`model.countTokens(messages, options?)` counts the converted contents, system
|
|
299
|
+
instructions and tools on both the Gemini Developer API (API key) and Vertex AI.
|
|
300
|
+
Developer API counting sends the full `generateContentRequest`, including tool
|
|
301
|
+
configuration and generation settings, as documented in the
|
|
302
|
+
[Gemini token-counting reference](https://ai.google.dev/api/tokens). It uses the
|
|
303
|
+
existing SDK transport, preserving API-key auth, custom fetch, base URL/proxy
|
|
304
|
+
settings, headers, retries and cancellation. Vertex retains its typed SDK path.
|
|
305
|
+
|
|
306
|
+
Cached-content references still throw `InvalidRequestError` with
|
|
307
|
+
`reason: "count-unsupported"`. Use `countInputTokens` for budget preflight: it
|
|
308
|
+
returns an estimate for this explicit limitation; authentication, network,
|
|
309
|
+
rate-limit and ordinary validation errors still propagate. `sdk.count(text)`
|
|
310
|
+
remains a separate offline estimate.
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
import { countInputTokens } from "@warlock.js/ai";
|
|
314
|
+
|
|
315
|
+
const messages = [
|
|
316
|
+
{ role: "system" as const, content: "Be concise." },
|
|
317
|
+
{ role: "user" as const, content: "Hello" },
|
|
318
|
+
];
|
|
319
|
+
const tools = [{ name: "lookup", description: "Look up data", execute: async () => "ok" }];
|
|
320
|
+
const { tokens, source } = await countInputTokens(model, messages, { tools });
|
|
321
|
+
// source is "native" for ordinary Developer API and Vertex requests, including tools.
|
|
322
|
+
```
|
|
230
323
|
|
|
231
324
|
## When NOT to use this skill
|
|
232
325
|
|
|
@@ -239,4 +332,42 @@ Resolution order: per-model `pricing` > SDK-level registry entry > `undefined`.
|
|
|
239
332
|
- The `pick-ai-provider` topic of the `warlock-js-ai` skill
|
|
240
333
|
- The `embed-text` topic of the `warlock-js-ai` skill
|
|
241
334
|
|
|
335
|
+
## Search grounding metadata and fees
|
|
336
|
+
|
|
337
|
+
`response.providerMetadata.google.groundingMetadata` retains the raw grounding
|
|
338
|
+
metadata for each indexed candidate: queries, chunks, supports and search entry
|
|
339
|
+
point. Streaming `done` carries the same metadata. Agent reports retain it per
|
|
340
|
+
trip. Hosted search never produces a local function call.
|
|
341
|
+
|
|
342
|
+
`usage.serverToolUse.webSearchRequests` counts unique nonempty
|
|
343
|
+
`groundingMetadata.webSearchQueries`; `googleWebSearchQueries` records the raw
|
|
344
|
+
array length, including duplicates and empty entries. Stream snapshots replace
|
|
345
|
+
prior metadata per candidate rather than accumulating repeated snapshots.
|
|
346
|
+
The [official schema](https://ai.google.dev/api/generate-content#GroundingMetadata)
|
|
347
|
+
defines the queries array.
|
|
348
|
+
|
|
349
|
+
[Google's pricing guide](https://ai.google.dev/gemini-api/docs/google-search#pricing)
|
|
350
|
+
(verified 2026-10-18) bills Gemini 3 and newer per unique nonempty query: $14 per
|
|
351
|
+
1,000 queries after the shared monthly free allowance. You can explicitly use
|
|
352
|
+
the exported `GOOGLE_SERVER_TOOL_PRICING` as `pricing.serverTools` for that
|
|
353
|
+
model family. Gemini 2.5 and older bill per grounded prompt; their query count
|
|
354
|
+
is not a billable-prompt count, so leave this fee unpriced or account for it
|
|
355
|
+
outside this per-query table. Free allowances are not subtracted locally.
|
|
356
|
+
No web-fetch/code-execution count is inferred from result parts. See
|
|
357
|
+
[cost tracking](https://warlock.js.org/v/latest/ai/recipes/cost-tracking/#hosted-tool-request-fees).
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import { GOOGLE_SERVER_TOOL_PRICING } from "@warlock.js/ai-google";
|
|
361
|
+
const model = google.model({
|
|
362
|
+
name: "gemini-3.5-flash-lite",
|
|
363
|
+
|
|
364
|
+
pricing: { ...{ input: 0.3, output: 2.5 }, serverTools: GOOGLE_SERVER_TOOL_PRICING },
|
|
365
|
+
});
|
|
366
|
+
const response = await model.complete(messages, {
|
|
367
|
+
providerTools: [{ provider: "google", definition: { googleSearch: {} } }],
|
|
368
|
+
});
|
|
369
|
+
console.log(response.usage.serverToolUse?.webSearchRequests);
|
|
370
|
+
console.log(response.providerMetadata);
|
|
371
|
+
```
|
|
372
|
+
|
|
242
373
|
|
package/llms.txt
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
|
|
7
7
|
## Skills
|
|
8
8
|
|
|
9
|
-
- [setup-google](@warlock.js/ai-google/setup-google/SKILL.md): Wire @warlock.js/ai-google — new GoogleSDK({apiKey} | {vertexai, project, location}) for Gemini API + Vertex AI. generateContent / embedContent + thoughtSignature round-trip for thinking models, batched embeddings. .model({name, vision?, reasoning?, audio?, pdf?}) with cost-truth capabilities (PDF + audio input map to Gemini inlineData), .image({name, pricing?}) for image generation via ai.image — the id picks the transport: gemini-* → generateContent + responseModalities IMAGE (usage passed through; generateImages is deprecated by Google), everything else → generateImages (Imagen, per-image, zero usage) — extended thinking via options.reasoning → thinkingConfig.thinkingBudget, usage reasoningTokens (thoughtsTokenCount) / cachedTokens (cachedContentTokenCount). Triggers: `GoogleSDK`, `google.model`, `google.embedder`, `google.image`, `imagen`, `generateImages`, `gemini image`, `nano banana`, `responseModalities`, `imageConfig`, `ai.image`, `inlineData`, `pdf input`, `audio input`, `thoughtSignature`, `responseJsonSchema`, `vertexai`, `reasoning`, `thinkingConfig`, `thinkingBudget`, `thoughtsTokenCount`, `reasoningTokens`, `cachedTokens`, `promptCaching`, `cacheControl`; "use gemini", "wire Vertex AI", "gemini embeddings", "gemini thinking tool calls", "gemini 2.5 thinking budget", "gemini cached content cost", "generate images with imagen", "generate an image with gemini", "gemini image model 404 not supported for predict", "send a pdf / audio to gemini"; import `import { GoogleSDK } from "@warlock.js/ai-google"`. Skip: the ai.image verb surface — the `generate-images` topic of the `warlock-js-ai` skill; agent loop the `run-ai-agent` topic of the `warlock-js-ai` skill; provider picking the `pick-ai-provider` topic of the `warlock-js-ai` skill; embedder usage the `embed-text` topic of the `warlock-js-ai` skill; siblings `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-ollama`; raw `@google/genai`, `@google-cloud/vertexai`, Vercel `@ai-sdk/google`.
|
|
9
|
+
- [setup-google](@warlock.js/ai-google/setup-google/SKILL.md): Wire @warlock.js/ai-google — new GoogleSDK({apiKey} | {vertexai, project, location}) for Gemini API + Vertex AI. generateContent / embedContent + thoughtSignature round-trip for thinking models, batched embeddings. .model({name, vision?, reasoning?, audio?, pdf?}) with cost-truth capabilities (PDF + audio input map to Gemini inlineData), .image({name, pricing?}) for image generation via ai.image — the id picks the transport: gemini-* → generateContent + responseModalities IMAGE (usage passed through; generateImages is deprecated by Google), everything else → generateImages (Imagen, per-image, zero usage) — extended thinking via options.reasoning → thinkingConfig.thinkingBudget, usage reasoningTokens (thoughtsTokenCount) / cachedTokens (cachedContentTokenCount). Triggers: `providerOptions`, `GoogleCallOptions`, `countTokens`, `GoogleSDK`, `google.model`, `google.embedder`, `google.image`, `imagen`, `generateImages`, `gemini image`, `nano banana`, `responseModalities`, `imageConfig`, `ai.image`, `inlineData`, `pdf input`, `audio input`, `thoughtSignature`, `responseJsonSchema`, `ai.providerTool`, `googleSearch`, `includeThoughts`, `streamChunks`, `vertexai`, `reasoning`, `thinkingConfig`, `thinkingBudget`, `thoughtsTokenCount`, `reasoningTokens`, `cachedTokens`, `inputIncludesCache`, `GOOGLE_MODEL_PRICING`, `googlePricingFor`, `ProviderOverloadedError`, `promptCaching`, `cacheControl`; "use gemini", "wire Vertex AI", "gemini embeddings", "gemini thinking tool calls", "gemini 2.5 thinking budget", "gemini cached content cost", "generate images with imagen", "generate an image with gemini", "gemini image model 404 not supported for predict", "send a pdf / audio to gemini"; import `import { GoogleSDK } from "@warlock.js/ai-google"`. Skip: the ai.image verb surface — the `generate-images` topic of the `warlock-js-ai` skill; agent loop the `run-ai-agent` topic of the `warlock-js-ai` skill; provider picking the `pick-ai-provider` topic of the `warlock-js-ai` skill; embedder usage the `embed-text` topic of the `warlock-js-ai` skill; siblings `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-ollama`; raw `@google/genai`, `@google-cloud/vertexai`, Vercel `@ai-sdk/google`.
|
package/package.json
CHANGED
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"directory": "packages/ai-google"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"@warlock.js/ai": "5.
|
|
18
|
+
"@warlock.js/ai": "5.32.0"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@google/genai": "^2.17.1",
|
|
22
|
-
"@warlock.js/logger": "5.
|
|
22
|
+
"@warlock.js/logger": "5.32.0"
|
|
23
23
|
},
|
|
24
|
-
"version": "5.
|
|
24
|
+
"version": "5.32.0",
|
|
25
25
|
"main": "./cjs/index.cjs",
|
|
26
26
|
"module": "./esm/index.mjs",
|
|
27
27
|
"types": "./esm/index.d.mts",
|