@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiError } from "@google/genai";
|
|
2
|
-
import { AIError, ContextLengthExceededError, InvalidRequestError, ProviderAuthError, ProviderError, ProviderRateLimitError, ProviderTimeoutError } from "@warlock.js/ai";
|
|
2
|
+
import { AIError, ContextLengthExceededError, InvalidRequestError, ProviderAuthError, ProviderError, ProviderOverloadedError, ProviderRateLimitError, ProviderTimeoutError } from "@warlock.js/ai";
|
|
3
3
|
|
|
4
4
|
//#region ../../packages/ai-google/src/utils/wrap-google-error.ts
|
|
5
5
|
/**
|
|
@@ -28,35 +28,48 @@ function wrapGoogleError(thrown) {
|
|
|
28
28
|
const shape = toShape(thrown);
|
|
29
29
|
const context = buildContext(shape);
|
|
30
30
|
const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));
|
|
31
|
+
const billed = shape.status === 400 || shape.status === 500 ? false : void 0;
|
|
31
32
|
if (isTimeout(shape)) return new ProviderTimeoutError(message, {
|
|
32
33
|
cause: thrown,
|
|
33
|
-
context
|
|
34
|
+
context,
|
|
35
|
+
billed
|
|
34
36
|
});
|
|
35
37
|
if (shape.status === 401 || shape.status === 403 || /permission_denied|api key not valid|unauthenticated/i.test(message)) return new ProviderAuthError(message, {
|
|
36
38
|
cause: thrown,
|
|
37
|
-
context
|
|
39
|
+
context,
|
|
40
|
+
billed
|
|
38
41
|
});
|
|
39
42
|
if (shape.status === 429 || /resource_exhausted|quota/i.test(message)) return new ProviderRateLimitError(message, {
|
|
40
43
|
cause: thrown,
|
|
41
|
-
context
|
|
44
|
+
context,
|
|
45
|
+
billed
|
|
42
46
|
});
|
|
43
47
|
if (shape.status === 400) {
|
|
44
48
|
if (/token count|context length|exceeds the maximum|input is too long/i.test(message)) return new ContextLengthExceededError(message, {
|
|
45
49
|
cause: thrown,
|
|
46
|
-
context
|
|
50
|
+
context,
|
|
51
|
+
billed
|
|
47
52
|
});
|
|
48
53
|
return new InvalidRequestError(message, {
|
|
49
54
|
cause: thrown,
|
|
50
|
-
context
|
|
55
|
+
context,
|
|
56
|
+
billed
|
|
51
57
|
});
|
|
52
58
|
}
|
|
53
59
|
if (shape.status === 404 || isClientStatus(shape.status)) return new InvalidRequestError(message, {
|
|
54
60
|
cause: thrown,
|
|
55
|
-
context
|
|
61
|
+
context,
|
|
62
|
+
billed
|
|
63
|
+
});
|
|
64
|
+
if (shape.status === 503 || /\bUNAVAILABLE\b/.test(message) || /overloaded/i.test(message)) return new ProviderOverloadedError(message, {
|
|
65
|
+
cause: thrown,
|
|
66
|
+
context,
|
|
67
|
+
billed
|
|
56
68
|
});
|
|
57
69
|
return new ProviderError(message, {
|
|
58
70
|
cause: thrown,
|
|
59
|
-
context
|
|
71
|
+
context,
|
|
72
|
+
billed
|
|
60
73
|
});
|
|
61
74
|
}
|
|
62
75
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap-google-error.mjs","names":[],"sources":["../../../../../../../../packages/ai-google/src/utils/wrap-google-error.ts"],"sourcesContent":["import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n} from \"@warlock.js/ai\";\nimport { ApiError } from \"@google/genai\";\n\n/**\n * Raw-error fields the wrapper reads off a Gemini SDK error.\n * `@google/genai`'s `ApiError` exposes `status` (HTTP code) +\n * `message`; transport aborts surface as `AbortError` / `ETIMEDOUT`.\n * We duck-type so proxied / re-thrown errors still classify.\n */\ntype GoogleErrorShape = {\n status?: number;\n message?: string;\n name?: string;\n code?: string;\n};\n\n/**\n * Wrap any thrown value caught inside the Gemini adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** Gemini has no machine error `code`; the\n * signals are the HTTP `status` and the canonical status phrase Google\n * embeds in `message` (`PERMISSION_DENIED`, `RESOURCE_EXHAUSTED`,\n * `INVALID_ARGUMENT`, …). Dispatch keys on `status`, using the message\n * phrase as the tie-breaker for the two 400 sub-cases\n * (context-length vs generic) and for status-less auth/quota errors.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.ai.models.generateContent(...);\n * } catch (thrown) {\n * throw wrapGoogleError(thrown);\n * }\n */\nexport function wrapGoogleError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n\n if (isTimeout(shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context });\n }\n\n if (\n shape.status === 401 ||\n shape.status === 403 ||\n /permission_denied|api key not valid|unauthenticated/i.test(message)\n ) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.status === 429 || /resource_exhausted|quota/i.test(message)) {\n return new ProviderRateLimitError(message, { cause: thrown, context });\n }\n\n if (shape.status === 400) {\n if (/token count|context length|exceeds the maximum|input is too long/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n if (shape.status === 404 || isClientStatus(shape.status)) {\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n return new ProviderError(message, { cause: thrown, context });\n}\n\n/**\n * Read the raw error shape. The Gemini SDK's `ApiError` carries a\n * numeric `status`; flattened/proxied errors may carry it (or `code`)\n * loosely.\n */\nfunction toShape(thrown: unknown): GoogleErrorShape {\n if (thrown instanceof ApiError) {\n return { status: thrown.status, message: thrown.message, name: thrown.name };\n }\n\n if (typeof thrown === \"object\" && thrown !== null) {\n const raw = thrown as Record<string, unknown>;\n\n return {\n status: typeof raw.status === \"number\" ? raw.status : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n code: typeof raw.code === \"string\" ? raw.code : undefined,\n };\n }\n\n return {};\n}\n\n/**\n * Decide whether the error is a timeout. Gemini maps gateway timeouts\n * to HTTP 504 (`DEADLINE_EXCEEDED`); transport aborts surface as\n * `AbortError` / `ETIMEDOUT` / `ECONNABORTED`.\n */\nfunction isTimeout(shape: GoogleErrorShape): boolean {\n if (shape.status === 504) {\n return true;\n }\n\n if (shape.name === \"AbortError\" || /deadline_exceeded/i.test(shape.message ?? \"\")) {\n return true;\n }\n\n return shape.code === \"ETIMEDOUT\" || shape.code === \"ECONNABORTED\";\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/** Attach the diagnostic fields to `error.context`. */\nfunction buildContext(shape: GoogleErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.status !== undefined) {\n context.status = shape.status;\n }\n\n if (shape.name) {\n context.code = shape.name;\n }\n\n return context;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"wrap-google-error.mjs","names":[],"sources":["../../../../../../../../packages/ai-google/src/utils/wrap-google-error.ts"],"sourcesContent":["import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderOverloadedError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n} from \"@warlock.js/ai\";\nimport { ApiError } from \"@google/genai\";\n\n/**\n * Raw-error fields the wrapper reads off a Gemini SDK error.\n * `@google/genai`'s `ApiError` exposes `status` (HTTP code) +\n * `message`; transport aborts surface as `AbortError` / `ETIMEDOUT`.\n * We duck-type so proxied / re-thrown errors still classify.\n */\ntype GoogleErrorShape = {\n status?: number;\n message?: string;\n name?: string;\n code?: string;\n};\n\n/**\n * Wrap any thrown value caught inside the Gemini adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** Gemini has no machine error `code`; the\n * signals are the HTTP `status` and the canonical status phrase Google\n * embeds in `message` (`PERMISSION_DENIED`, `RESOURCE_EXHAUSTED`,\n * `INVALID_ARGUMENT`, …). Dispatch keys on `status`, using the message\n * phrase as the tie-breaker for the two 400 sub-cases\n * (context-length vs generic) and for status-less auth/quota errors.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.ai.models.generateContent(...);\n * } catch (thrown) {\n * throw wrapGoogleError(thrown);\n * }\n */\nexport function wrapGoogleError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n // \"If your request fails with a 400 or 500 error, you won't be charged\n // for the tokens used.\" — https://ai.google.dev/gemini-api/docs/billing\n // (fetched 2026-10-17). Every other failure keeps the default.\n const billed = shape.status === 400 || shape.status === 500 ? false : undefined;\n\n if (isTimeout(shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context, billed });\n }\n\n if (\n shape.status === 401 ||\n shape.status === 403 ||\n /permission_denied|api key not valid|unauthenticated/i.test(message)\n ) {\n return new ProviderAuthError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 429 || /resource_exhausted|quota/i.test(message)) {\n return new ProviderRateLimitError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 400) {\n if (/token count|context length|exceeds the maximum|input is too long/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context, billed });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 404 || isClientStatus(shape.status)) {\n return new InvalidRequestError(message, { cause: thrown, context, billed });\n }\n\n // 503 UNAVAILABLE — \"temporarily overloaded or down\"\n // (https://ai.google.dev/gemini-api/docs/api-errors).\n if (shape.status === 503 || /\\bUNAVAILABLE\\b/.test(message) || /overloaded/i.test(message)) {\n return new ProviderOverloadedError(message, { cause: thrown, context, billed });\n }\n\n return new ProviderError(message, { cause: thrown, context, billed });\n}\n\n/**\n * Read the raw error shape. The Gemini SDK's `ApiError` carries a\n * numeric `status`; flattened/proxied errors may carry it (or `code`)\n * loosely.\n */\nfunction toShape(thrown: unknown): GoogleErrorShape {\n if (thrown instanceof ApiError) {\n return { status: thrown.status, message: thrown.message, name: thrown.name };\n }\n\n if (typeof thrown === \"object\" && thrown !== null) {\n const raw = thrown as Record<string, unknown>;\n\n return {\n status: typeof raw.status === \"number\" ? raw.status : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n code: typeof raw.code === \"string\" ? raw.code : undefined,\n };\n }\n\n return {};\n}\n\n/**\n * Decide whether the error is a timeout. Gemini maps gateway timeouts\n * to HTTP 504 (`DEADLINE_EXCEEDED`); transport aborts surface as\n * `AbortError` / `ETIMEDOUT` / `ECONNABORTED`.\n */\nfunction isTimeout(shape: GoogleErrorShape): boolean {\n if (shape.status === 504) {\n return true;\n }\n\n if (shape.name === \"AbortError\" || /deadline_exceeded/i.test(shape.message ?? \"\")) {\n return true;\n }\n\n return shape.code === \"ETIMEDOUT\" || shape.code === \"ECONNABORTED\";\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/** Attach the diagnostic fields to `error.context`. */\nfunction buildContext(shape: GoogleErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.status !== undefined) {\n context.status = shape.status;\n }\n\n if (shape.name) {\n context.code = shape.name;\n }\n\n return context;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,gBAAgB,QAA0B;CACxD,IAAI,kBAAkB,SACpB,OAAO;CAGT,MAAM,QAAQ,QAAQ,MAAM;CAC5B,MAAM,UAAU,aAAa,KAAK;CAClC,MAAM,UAAU,MAAM,YAAY,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM;CAI1F,MAAM,SAAS,MAAM,WAAW,OAAO,MAAM,WAAW,MAAM,QAAQ;CAEtE,IAAI,UAAU,KAAK,GACjB,OAAO,IAAI,qBAAqB,SAAS;EAAE,OAAO;EAAQ;EAAS;CAAO,CAAC;CAG7E,IACE,MAAM,WAAW,OACjB,MAAM,WAAW,OACjB,uDAAuD,KAAK,OAAO,GAEnE,OAAO,IAAI,kBAAkB,SAAS;EAAE,OAAO;EAAQ;EAAS;CAAO,CAAC;CAG1E,IAAI,MAAM,WAAW,OAAO,4BAA4B,KAAK,OAAO,GAClE,OAAO,IAAI,uBAAuB,SAAS;EAAE,OAAO;EAAQ;EAAS;CAAO,CAAC;CAG/E,IAAI,MAAM,WAAW,KAAK;EACxB,IAAI,oEAAoE,KAAK,OAAO,GAClF,OAAO,IAAI,2BAA2B,SAAS;GAAE,OAAO;GAAQ;GAAS;EAAO,CAAC;EAGnF,OAAO,IAAI,oBAAoB,SAAS;GAAE,OAAO;GAAQ;GAAS;EAAO,CAAC;CAC5E;CAEA,IAAI,MAAM,WAAW,OAAO,eAAe,MAAM,MAAM,GACrD,OAAO,IAAI,oBAAoB,SAAS;EAAE,OAAO;EAAQ;EAAS;CAAO,CAAC;CAK5E,IAAI,MAAM,WAAW,OAAO,kBAAkB,KAAK,OAAO,KAAK,cAAc,KAAK,OAAO,GACvF,OAAO,IAAI,wBAAwB,SAAS;EAAE,OAAO;EAAQ;EAAS;CAAO,CAAC;CAGhF,OAAO,IAAI,cAAc,SAAS;EAAE,OAAO;EAAQ;EAAS;CAAO,CAAC;AACtE;;;;;;AAOA,SAAS,QAAQ,QAAmC;CAClD,IAAI,kBAAkB,UACpB,OAAO;EAAE,QAAQ,OAAO;EAAQ,SAAS,OAAO;EAAS,MAAM,OAAO;CAAK;CAG7E,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM;EACjD,MAAM,MAAM;EAEZ,OAAO;GACL,QAAQ,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;GACtD,SAAS,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;GACzD,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;GAChD,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;EAClD;CACF;CAEA,OAAO,CAAC;AACV;;;;;;AAOA,SAAS,UAAU,OAAkC;CACnD,IAAI,MAAM,WAAW,KACnB,OAAO;CAGT,IAAI,MAAM,SAAS,gBAAgB,qBAAqB,KAAK,MAAM,WAAW,EAAE,GAC9E,OAAO;CAGT,OAAO,MAAM,SAAS,eAAe,MAAM,SAAS;AACtD;;AAGA,SAAS,eAAe,QAAqC;CAC3D,OAAO,OAAO,WAAW,YAAY,UAAU,OAAO,SAAS;AACjE;;AAGA,SAAS,aAAa,OAAkD;CACtE,MAAM,UAAmC,CAAC;CAE1C,IAAI,MAAM,WAAW,QACnB,QAAQ,SAAS,MAAM;CAGzB,IAAI,MAAM,MACR,QAAQ,OAAO,MAAM;CAGvB,OAAO;AACT"}
|
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,70 @@ 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));
|
|
273
|
+
```
|
|
274
|
+
|
|
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);
|
|
227
291
|
```
|
|
228
292
|
|
|
229
|
-
|
|
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?)` calls the native counting endpoint with
|
|
299
|
+
the same converted contents. In Vertex mode it also counts the same system
|
|
300
|
+
instruction and tools as generation and forwards cancellation. In Developer API
|
|
301
|
+
mode, the installed Google SDK cannot count system instructions or tools through
|
|
302
|
+
its typed count endpoint: requests containing them fail explicitly before a
|
|
303
|
+
network call. Cached-content references also fail explicitly, since this SDK
|
|
304
|
+
count endpoint cannot reproduce them. The adapter never silently counts only a
|
|
305
|
+
subset of a prompt. `sdk.count(text)` remains a separate offline estimate.
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
if (model.countTokens) {
|
|
309
|
+
const inputTokens = await model.countTokens(messages, options);
|
|
310
|
+
}
|
|
311
|
+
```
|
|
230
312
|
|
|
231
313
|
## When NOT to use this skill
|
|
232
314
|
|
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.31.0"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@google/genai": "^2.17.1",
|
|
22
|
-
"@warlock.js/logger": "5.
|
|
22
|
+
"@warlock.js/logger": "5.31.0"
|
|
23
23
|
},
|
|
24
|
-
"version": "5.
|
|
24
|
+
"version": "5.31.0",
|
|
25
25
|
"main": "./cjs/index.cjs",
|
|
26
26
|
"module": "./esm/index.mjs",
|
|
27
27
|
"types": "./esm/index.d.mts",
|