@warlock.js/ai-google 4.1.1
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/cjs/index.cjs +767 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/config.type.d.mts +83 -0
- package/esm/config.type.d.mts.map +1 -0
- package/esm/embedder.mjs +103 -0
- package/esm/embedder.mjs.map +1 -0
- package/esm/index.d.mts +3 -0
- package/esm/index.mjs +3 -0
- package/esm/known-vision-models.mjs +39 -0
- package/esm/known-vision-models.mjs.map +1 -0
- package/esm/model.mjs +277 -0
- package/esm/model.mjs.map +1 -0
- package/esm/sdk.d.mts +62 -0
- package/esm/sdk.d.mts.map +1 -0
- package/esm/sdk.mjs +78 -0
- package/esm/sdk.mjs.map +1 -0
- package/esm/utils/index.mjs +6 -0
- package/esm/utils/map-finish-reason.mjs +34 -0
- package/esm/utils/map-finish-reason.mjs.map +1 -0
- package/esm/utils/to-google-contents.mjs +120 -0
- package/esm/utils/to-google-contents.mjs.map +1 -0
- package/esm/utils/to-google-tools.mjs +41 -0
- package/esm/utils/to-google-tools.mjs.map +1 -0
- package/esm/utils/wrap-google-error.mjs +108 -0
- package/esm/utils/wrap-google-error.mjs.map +1 -0
- package/llms-full.txt +154 -0
- package/llms.txt +9 -0
- package/package.json +39 -0
- package/skills/README.md +9 -0
- package/skills/setup-google/SKILL.md +144 -0
package/llms-full.txt
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Warlock AI Google — full skills
|
|
2
|
+
|
|
3
|
+
> Package: `@warlock.js/ai-google`
|
|
4
|
+
|
|
5
|
+
> Generated artifact. Concatenates every SKILL.md and reference file under `@warlock.js/ai-google/skills/`. Re-run `node scripts/generate-llms.mjs` after any change.
|
|
6
|
+
|
|
7
|
+
## setup-google `@warlock.js/ai-google/setup-google/SKILL.md`
|
|
8
|
+
|
|
9
|
+
---
|
|
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. Triggers: `GoogleSDK`, `google.model`, `google.embedder`, `thoughtSignature`, `responseJsonSchema`, `vertexai`; "use gemini", "wire Vertex AI", "gemini embeddings", "gemini thinking tool calls"; import `import { GoogleSDK } from "@warlock.js/ai-google"`. Skip: agent loop `@warlock.js/ai/run-ai-agent/SKILL.md`; provider picking `@warlock.js/ai/pick-ai-provider/SKILL.md`; embedder usage `@warlock.js/ai/embed-text/SKILL.md`; 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
|
+
---
|
|
13
|
+
|
|
14
|
+
# `@warlock.js/ai-google`
|
|
15
|
+
|
|
16
|
+
Provider adapter that turns Google Gemini into a vendor-neutral `ModelContract`, plus a Gemini embedder. Mirrors the openai / anthropic / bedrock adapters.
|
|
17
|
+
|
|
18
|
+
## Construction
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { GoogleSDK } from "@warlock.js/ai-google";
|
|
22
|
+
|
|
23
|
+
// Gemini API (API key):
|
|
24
|
+
const google = new GoogleSDK({ apiKey: process.env.GEMINI_API_KEY! });
|
|
25
|
+
|
|
26
|
+
// Vertex AI:
|
|
27
|
+
const vertex = new GoogleSDK({
|
|
28
|
+
vertexai: true,
|
|
29
|
+
project: "my-project",
|
|
30
|
+
location: "us-central1",
|
|
31
|
+
provider: "vertex",
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`GoogleSDK` is a class with a long-lived `GoogleGenAI` client. The two adapter-only keys (`provider`, `pricing`) are stripped; everything else is forwarded verbatim to `new GoogleGenAI(...)` (so `apiVersion`, `httpOptions`, `vertexai`, `project`, `location` all pass through). `provider` defaults to `"google"`.
|
|
36
|
+
|
|
37
|
+
## Producing a model
|
|
38
|
+
|
|
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 })
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Capabilities — what's auto-set
|
|
46
|
+
|
|
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
|
+
|
|
52
|
+
Explicit config always wins.
|
|
53
|
+
|
|
54
|
+
## System prompt
|
|
55
|
+
|
|
56
|
+
Gemini content roles must be `"user"` or `"model"` — there is no `"system"` role. The adapter hoists every neutral `role: "system"` message into the separate `config.systemInstruction` string. Transparent to the agent.
|
|
57
|
+
|
|
58
|
+
## Roles & tool calls
|
|
59
|
+
|
|
60
|
+
- Neutral `assistant` → Gemini `"model"`; `user` stays `"user"`.
|
|
61
|
+
- Assistant tool calls → `"model"` content: optional leading `{ text }` + one `{ functionCall: { name, args } }` part per call (`id` deliberately omitted — see below).
|
|
62
|
+
- 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> }`.
|
|
63
|
+
|
|
64
|
+
**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.
|
|
65
|
+
|
|
66
|
+
**Gemini reports `finishReason: STOP` even when it called a function** — the adapter overrides to `"tool_calls"` when the response carries function calls.
|
|
67
|
+
|
|
68
|
+
**`thoughtSignature` round-trip (thinking models).** Gemini 2.5 thinking models attach an opaque `thoughtSignature` to each `functionCall` part and **400 the next request** if it's not echoed back. The adapter captures the signature into `ModelToolCallRequest.providerMetadata.thoughtSignature` and replays it on the echoed `functionCall` part. Fully automatic.
|
|
69
|
+
|
|
70
|
+
## Structured output
|
|
71
|
+
|
|
72
|
+
Object-root `responseSchema` + `structuredOutput`-capable → `config.responseMimeType = "application/json"` + `config.responseJsonSchema = <schema>` (Gemini takes a **raw JSON Schema** directly, not its typed `Schema`).
|
|
73
|
+
|
|
74
|
+
## Multipart messages (vision)
|
|
75
|
+
|
|
76
|
+
- `{ type: "text" }` → `{ text }`
|
|
77
|
+
- `{ type: "image", source: { base64, mediaType } }` → `{ inlineData: { mimeType, data } }`
|
|
78
|
+
- `{ type: "image", source: { url } }` → **throws `InvalidRequestError`**. `generateContent` does not fetch arbitrary remote URLs (only Files API / GCS URIs). Resolve images to base64 first.
|
|
79
|
+
|
|
80
|
+
## Streaming
|
|
81
|
+
|
|
82
|
+
`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`.
|
|
83
|
+
|
|
84
|
+
## Finish-reason mapping
|
|
85
|
+
|
|
86
|
+
`STOP` → `stop` · `MAX_TOKENS` → `length` · `SAFETY` / `RECITATION` / `BLOCKLIST` / `PROHIBITED_CONTENT` / `MALFORMED_FUNCTION_CALL` / unknown / null → `error`. `tool_calls` is derived from function-call presence.
|
|
87
|
+
|
|
88
|
+
## Embeddings
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const embedder = google.embedder({ name: "gemini-embedding-001" });
|
|
92
|
+
const { vector } = await embedder.embed("Hello world");
|
|
93
|
+
const { vectors } = await embedder.embedMany(["a", "b"]); // single batched call
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`embedContent` accepts an array natively, so `embedMany` is **one request** (unlike Bedrock/Titan). Pass `dimensions` to forward Gemini's `outputDimensionality` truncation hint.
|
|
97
|
+
|
|
98
|
+
**Gemini's embed endpoint returns no token usage**, so `usage` is always `{ promptTokens: 0, totalTokens: 0 }` (honest absence).
|
|
99
|
+
|
|
100
|
+
## Errors
|
|
101
|
+
|
|
102
|
+
Wrapped into the typed `@warlock.js/ai` `AIError` hierarchy. Gemini has no machine error code — dispatch keys on HTTP `status` + canonical status phrase in the message:
|
|
103
|
+
|
|
104
|
+
- 401/403 / `PERMISSION_DENIED` / "API key not valid" → `ProviderAuthError`
|
|
105
|
+
- 429 / `RESOURCE_EXHAUSTED` → `ProviderRateLimitError`
|
|
106
|
+
- 400 with token/context phrasing → `ContextLengthExceededError`, else `InvalidRequestError`
|
|
107
|
+
- 404 / any other 4xx → `InvalidRequestError`
|
|
108
|
+
- 504 / `DEADLINE_EXCEEDED` / `ETIMEDOUT` / `ECONNABORTED` / `AbortError` → `ProviderTimeoutError`
|
|
109
|
+
- anything else (5xx and status-less) → `ProviderError`
|
|
110
|
+
|
|
111
|
+
Timeout is checked first, so an aborted/`AbortError` request always classifies as `ProviderTimeoutError` regardless of status. `AIError` instances pass through unwrapped (no double-wrap).
|
|
112
|
+
|
|
113
|
+
## Token counting
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
await google.count("some text") // approximate heuristic, offline
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`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.
|
|
120
|
+
|
|
121
|
+
## Token usage
|
|
122
|
+
|
|
123
|
+
`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`. Embeddings have no usage (see above).
|
|
124
|
+
|
|
125
|
+
## Pricing
|
|
126
|
+
|
|
127
|
+
Optional USD pricing flows in two ways, resolved per `model()` call:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
// SDK-level registry, keyed by model name:
|
|
131
|
+
const google = new GoogleSDK({
|
|
132
|
+
apiKey,
|
|
133
|
+
pricing: { "gemini-2.5-flash": { input: 0.3, output: 2.5 } },
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
google.model({ name: "gemini-2.5-flash" }); // → inherits the registry entry
|
|
137
|
+
google.model({ name: "gemini-2.5-flash", pricing: {...} }); // → per-model pricing wins
|
|
138
|
+
google.model({ name: "gemini-2.5-pro" }); // → no entry → pricing undefined (no cost computed)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Resolution order: per-model `pricing` > SDK-level registry entry > `undefined`. The resolved `ModelPricing` is attached to the produced model so the agent runtime can compute cost from usage.
|
|
142
|
+
|
|
143
|
+
## When NOT to use this skill
|
|
144
|
+
|
|
145
|
+
- Direct `@google/genai` calls without going through `@warlock.js/ai` agents.
|
|
146
|
+
- OpenAI / Anthropic / Bedrock / Ollama models — those have their own adapter packages.
|
|
147
|
+
|
|
148
|
+
## See also
|
|
149
|
+
|
|
150
|
+
- [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md)
|
|
151
|
+
- [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md)
|
|
152
|
+
- [`@warlock.js/ai/embed-text/SKILL.md`](@warlock.js/ai/embed-text/SKILL.md)
|
|
153
|
+
|
|
154
|
+
|
package/llms.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Warlock AI Google
|
|
2
|
+
|
|
3
|
+
> Package: `@warlock.js/ai-google`
|
|
4
|
+
|
|
5
|
+
> Google Gemini adapter for @warlock.js/ai
|
|
6
|
+
|
|
7
|
+
## Skills
|
|
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. Triggers: `GoogleSDK`, `google.model`, `google.embedder`, `thoughtSignature`, `responseJsonSchema`, `vertexai`; "use gemini", "wire Vertex AI", "gemini embeddings", "gemini thinking tool calls"; import `import { GoogleSDK } from "@warlock.js/ai-google"`. Skip: agent loop `@warlock.js/ai/run-ai-agent/SKILL.md`; provider picking `@warlock.js/ai/pick-ai-provider/SKILL.md`; embedder usage `@warlock.js/ai/embed-text/SKILL.md`; 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
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@warlock.js/ai-google",
|
|
3
|
+
"description": "Google Gemini adapter for @warlock.js/ai",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"warlock",
|
|
6
|
+
"ai",
|
|
7
|
+
"google",
|
|
8
|
+
"gemini"
|
|
9
|
+
],
|
|
10
|
+
"author": "Hasan Zohdy",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/warlockjs/ai-google"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@google/genai": "^2.4.0",
|
|
18
|
+
"@warlock.js/logger": "*"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"@warlock.js/ai": "*"
|
|
22
|
+
},
|
|
23
|
+
"version": "4.1.1",
|
|
24
|
+
"main": "./cjs/index.cjs",
|
|
25
|
+
"module": "./esm/index.mjs",
|
|
26
|
+
"types": "./esm/index.d.mts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": {
|
|
30
|
+
"types": "./esm/index.d.mts",
|
|
31
|
+
"default": "./esm/index.mjs"
|
|
32
|
+
},
|
|
33
|
+
"require": {
|
|
34
|
+
"types": "./esm/index.d.mts",
|
|
35
|
+
"default": "./cjs/index.cjs"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/skills/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# `@warlock.js/ai-google` — skills index
|
|
2
|
+
|
|
3
|
+
Per-task skills. All cross-references use the form `@warlock.js/<pkg>/<skill>/SKILL.md`.
|
|
4
|
+
|
|
5
|
+
## Skills
|
|
6
|
+
|
|
7
|
+
### [`setup-google/`](./setup-google/SKILL.md)
|
|
8
|
+
|
|
9
|
+
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. Load when wiring a Gemini-backed model into a @warlock.js agent or running on Vertex AI.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup-google
|
|
3
|
+
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. Triggers: `GoogleSDK`, `google.model`, `google.embedder`, `thoughtSignature`, `responseJsonSchema`, `vertexai`; "use gemini", "wire Vertex AI", "gemini embeddings", "gemini thinking tool calls"; import `import { GoogleSDK } from "@warlock.js/ai-google"`. Skip: agent loop `@warlock.js/ai/run-ai-agent/SKILL.md`; provider picking `@warlock.js/ai/pick-ai-provider/SKILL.md`; embedder usage `@warlock.js/ai/embed-text/SKILL.md`; 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`.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `@warlock.js/ai-google`
|
|
7
|
+
|
|
8
|
+
Provider adapter that turns Google Gemini into a vendor-neutral `ModelContract`, plus a Gemini embedder. Mirrors the openai / anthropic / bedrock adapters.
|
|
9
|
+
|
|
10
|
+
## Construction
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { GoogleSDK } from "@warlock.js/ai-google";
|
|
14
|
+
|
|
15
|
+
// Gemini API (API key):
|
|
16
|
+
const google = new GoogleSDK({ apiKey: process.env.GEMINI_API_KEY! });
|
|
17
|
+
|
|
18
|
+
// Vertex AI:
|
|
19
|
+
const vertex = new GoogleSDK({
|
|
20
|
+
vertexai: true,
|
|
21
|
+
project: "my-project",
|
|
22
|
+
location: "us-central1",
|
|
23
|
+
provider: "vertex",
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`GoogleSDK` is a class with a long-lived `GoogleGenAI` client. The two adapter-only keys (`provider`, `pricing`) are stripped; everything else is forwarded verbatim to `new GoogleGenAI(...)` (so `apiVersion`, `httpOptions`, `vertexai`, `project`, `location` all pass through). `provider` defaults to `"google"`.
|
|
28
|
+
|
|
29
|
+
## Producing a model
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
google.model({ name: "gemini-2.5-flash" })
|
|
33
|
+
google.model({ name: "gemini-2.5-pro", temperature: 0.2 })
|
|
34
|
+
google.model({ name: "gemini-1.5-flash", maxTokens: 2048 })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Capabilities — what's auto-set
|
|
38
|
+
|
|
39
|
+
| Flag | Default |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `structuredOutput` | `true` (via `responseMimeType` + `responseJsonSchema`) |
|
|
42
|
+
| `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`). |
|
|
43
|
+
|
|
44
|
+
Explicit config always wins.
|
|
45
|
+
|
|
46
|
+
## System prompt
|
|
47
|
+
|
|
48
|
+
Gemini content roles must be `"user"` or `"model"` — there is no `"system"` role. The adapter hoists every neutral `role: "system"` message into the separate `config.systemInstruction` string. Transparent to the agent.
|
|
49
|
+
|
|
50
|
+
## Roles & tool calls
|
|
51
|
+
|
|
52
|
+
- Neutral `assistant` → Gemini `"model"`; `user` stays `"user"`.
|
|
53
|
+
- Assistant tool calls → `"model"` content: optional leading `{ text }` + one `{ functionCall: { name, args } }` part per call (`id` deliberately omitted — see below).
|
|
54
|
+
- 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> }`.
|
|
55
|
+
|
|
56
|
+
**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.
|
|
57
|
+
|
|
58
|
+
**Gemini reports `finishReason: STOP` even when it called a function** — the adapter overrides to `"tool_calls"` when the response carries function calls.
|
|
59
|
+
|
|
60
|
+
**`thoughtSignature` round-trip (thinking models).** Gemini 2.5 thinking models attach an opaque `thoughtSignature` to each `functionCall` part and **400 the next request** if it's not echoed back. The adapter captures the signature into `ModelToolCallRequest.providerMetadata.thoughtSignature` and replays it on the echoed `functionCall` part. Fully automatic.
|
|
61
|
+
|
|
62
|
+
## Structured output
|
|
63
|
+
|
|
64
|
+
Object-root `responseSchema` + `structuredOutput`-capable → `config.responseMimeType = "application/json"` + `config.responseJsonSchema = <schema>` (Gemini takes a **raw JSON Schema** directly, not its typed `Schema`).
|
|
65
|
+
|
|
66
|
+
## Multipart messages (vision)
|
|
67
|
+
|
|
68
|
+
- `{ type: "text" }` → `{ text }`
|
|
69
|
+
- `{ type: "image", source: { base64, mediaType } }` → `{ inlineData: { mimeType, data } }`
|
|
70
|
+
- `{ type: "image", source: { url } }` → **throws `InvalidRequestError`**. `generateContent` does not fetch arbitrary remote URLs (only Files API / GCS URIs). Resolve images to base64 first.
|
|
71
|
+
|
|
72
|
+
## Streaming
|
|
73
|
+
|
|
74
|
+
`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`.
|
|
75
|
+
|
|
76
|
+
## Finish-reason mapping
|
|
77
|
+
|
|
78
|
+
`STOP` → `stop` · `MAX_TOKENS` → `length` · `SAFETY` / `RECITATION` / `BLOCKLIST` / `PROHIBITED_CONTENT` / `MALFORMED_FUNCTION_CALL` / unknown / null → `error`. `tool_calls` is derived from function-call presence.
|
|
79
|
+
|
|
80
|
+
## Embeddings
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const embedder = google.embedder({ name: "gemini-embedding-001" });
|
|
84
|
+
const { vector } = await embedder.embed("Hello world");
|
|
85
|
+
const { vectors } = await embedder.embedMany(["a", "b"]); // single batched call
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`embedContent` accepts an array natively, so `embedMany` is **one request** (unlike Bedrock/Titan). Pass `dimensions` to forward Gemini's `outputDimensionality` truncation hint.
|
|
89
|
+
|
|
90
|
+
**Gemini's embed endpoint returns no token usage**, so `usage` is always `{ promptTokens: 0, totalTokens: 0 }` (honest absence).
|
|
91
|
+
|
|
92
|
+
## Errors
|
|
93
|
+
|
|
94
|
+
Wrapped into the typed `@warlock.js/ai` `AIError` hierarchy. Gemini has no machine error code — dispatch keys on HTTP `status` + canonical status phrase in the message:
|
|
95
|
+
|
|
96
|
+
- 401/403 / `PERMISSION_DENIED` / "API key not valid" → `ProviderAuthError`
|
|
97
|
+
- 429 / `RESOURCE_EXHAUSTED` → `ProviderRateLimitError`
|
|
98
|
+
- 400 with token/context phrasing → `ContextLengthExceededError`, else `InvalidRequestError`
|
|
99
|
+
- 404 / any other 4xx → `InvalidRequestError`
|
|
100
|
+
- 504 / `DEADLINE_EXCEEDED` / `ETIMEDOUT` / `ECONNABORTED` / `AbortError` → `ProviderTimeoutError`
|
|
101
|
+
- anything else (5xx and status-less) → `ProviderError`
|
|
102
|
+
|
|
103
|
+
Timeout is checked first, so an aborted/`AbortError` request always classifies as `ProviderTimeoutError` regardless of status. `AIError` instances pass through unwrapped (no double-wrap).
|
|
104
|
+
|
|
105
|
+
## Token counting
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await google.count("some text") // approximate heuristic, offline
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`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.
|
|
112
|
+
|
|
113
|
+
## Token usage
|
|
114
|
+
|
|
115
|
+
`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`. Embeddings have no usage (see above).
|
|
116
|
+
|
|
117
|
+
## Pricing
|
|
118
|
+
|
|
119
|
+
Optional USD pricing flows in two ways, resolved per `model()` call:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// SDK-level registry, keyed by model name:
|
|
123
|
+
const google = new GoogleSDK({
|
|
124
|
+
apiKey,
|
|
125
|
+
pricing: { "gemini-2.5-flash": { input: 0.3, output: 2.5 } },
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
google.model({ name: "gemini-2.5-flash" }); // → inherits the registry entry
|
|
129
|
+
google.model({ name: "gemini-2.5-flash", pricing: {...} }); // → per-model pricing wins
|
|
130
|
+
google.model({ name: "gemini-2.5-pro" }); // → no entry → pricing undefined (no cost computed)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Resolution order: per-model `pricing` > SDK-level registry entry > `undefined`. The resolved `ModelPricing` is attached to the produced model so the agent runtime can compute cost from usage.
|
|
134
|
+
|
|
135
|
+
## When NOT to use this skill
|
|
136
|
+
|
|
137
|
+
- Direct `@google/genai` calls without going through `@warlock.js/ai` agents.
|
|
138
|
+
- OpenAI / Anthropic / Bedrock / Ollama models — those have their own adapter packages.
|
|
139
|
+
|
|
140
|
+
## See also
|
|
141
|
+
|
|
142
|
+
- [`@warlock.js/ai/run-ai-agent/SKILL.md`](@warlock.js/ai/run-ai-agent/SKILL.md)
|
|
143
|
+
- [`@warlock.js/ai/pick-ai-provider/SKILL.md`](@warlock.js/ai/pick-ai-provider/SKILL.md)
|
|
144
|
+
- [`@warlock.js/ai/embed-text/SKILL.md`](@warlock.js/ai/embed-text/SKILL.md)
|