ak-gemini 2.4.1 → 2.6.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 ADDED
@@ -0,0 +1,111 @@
1
+ # Changelog
2
+
3
+ ## 2.6.0
4
+
5
+ > **Upgrading from 2.5.0?** [UPGRADING.md](./UPGRADING.md) walks through only what you
6
+ > need to change, with before/after examples.
7
+
8
+ Feature request from a downstream consumer (`smarterchild/agent`, Vertex AI),
9
+ paired with ak-claude 0.2.0. **Minor** bump — cached-token billing changes
10
+ reported cost (see "Behavior changes" below).
11
+
12
+ ### Behavior changes (read before upgrading)
13
+ - **Cached tokens are now billed at the discounted cached rate.**
14
+ `usageMetadata.cachedContentTokenCount` is surfaced as `usage.cachedTokens` and
15
+ factored into `estimatedCost`. **Gemini INCLUDES cached tokens in
16
+ `promptTokenCount`**, so they are *subtracted* from the prompt count and
17
+ re-billed at `cachedInput` — they are a subset, not an addition. (ak-claude is
18
+ the opposite: Anthropic excludes cache tokens from `input_tokens` and they are
19
+ added on top. The asymmetry is intentional; do not "unify" it.) Reported cost
20
+ for cache-heavy calls drops by up to ~10×. Models with no published cached rate
21
+ bill cached tokens at the full input rate — an overestimate, never an
22
+ underestimate.
23
+ - **Pro models switch to their >200k-context tier above 200,000 prompt tokens.**
24
+ `gemini-2.5-pro` and `gemini-3.1-pro-preview` previously billed every call at
25
+ the ≤200k rate. Long-context calls now report roughly 2× the input cost they
26
+ did before, which is what Google actually charges.
27
+ - **`gemini-2.5-flash` output pricing corrected from $2.50 to $3.00 per M.**
28
+ Reported cost for that model rises accordingly.
29
+ - **`gemini-flash-latest` now resolves to `gemini-3.6-flash`** (was
30
+ `gemini-3.5-flash`) and **`gemini-flash-lite-latest` to `gemini-3.5-flash-lite`**
31
+ for pricing. Alias resolution is logged at debug — prefer the model id the API
32
+ echoes in `modelVersion`, which `_estimatedCost` already does.
33
+ - **`getLastUsage()` now reports non-zero `thoughtsTokens` on `Chat`,
34
+ `ToolAgent`, `CodeAgent`, `RagAgent`, and `Transformer`.** These classes seeded
35
+ `_cumulativeUsage` without the thinking-token count, so `getLastUsage()`
36
+ reported `0` (and undercounted cost) even when the response carried thoughts.
37
+ `result.usage` was already correct.
38
+
39
+ ### Added
40
+ - **`effort: 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'`** on every
41
+ class — the cross-package option name (identical to ak-claude). Maps to
42
+ `thinkingConfig.thinkingLevel` and wins over an explicit `thinkingLevel` /
43
+ `thinkingBudget` (the two are mutually exclusive on the wire). `xhigh` and
44
+ `max` are ak-claude-only levels and clamp to `high`. An unrecognized level
45
+ throws at construction.
46
+ - **`usage.cachedTokens`** on every `send()`/`chat()`/`generate()` result and on
47
+ `getLastUsage()`.
48
+ - **Pricing for new models:** `gemini-3.6-flash` ($1.50/$7.50, cached $0.15) and
49
+ `gemini-3.5-flash-lite` ($0.30/$2.50, cached $0.03).
50
+ - **`cachedInput` rates** on every model where Google publishes one, plus
51
+ `inputAbove200k` / `outputAbove200k` / `cachedInputAbove200k` on the tiered Pro
52
+ models.
53
+ - **New exports:** `MODEL_PRICING_AS_OF` (the date the table was last verified
54
+ against Google's pricing page) and `EFFORT_LEVELS`. `resolvePricing()` now
55
+ returns `asOf` and `tierThreshold`.
56
+ - `computeCost()` takes two new optional trailing args:
57
+ `computeCost(modelId, prompt, response, thoughtsTokens?, cachedTokens?)`.
58
+
59
+ ### Notes
60
+ - `null` from `resolvePricing()` / `usage.estimatedCost` means the model's
61
+ pricing is **unknown** — it does not mean the call was free.
62
+ - Cache **storage** ($1.00/1M tokens/hour on Flash tiers, $4.50 on Pro) is
63
+ time-based and is **not** modelled by `estimatedCost` — only per-token charges
64
+ are.
65
+ - Image-output tokens on Nano Banana models ($60/M or $120/M) are still not
66
+ modelled.
67
+
68
+ ## 2.5.0
69
+
70
+ Fixes from a downstream consumer's adversarial review. This is a **minor** bump —
71
+ it includes small but real behavior changes (see "Behavior changes" below).
72
+
73
+ ### Behavior changes (read before upgrading)
74
+ - **`Message` structured output:** passing `responseSchema` without
75
+ `responseMimeType` now auto-defaults `responseMimeType: 'application/json'`
76
+ (the SDK requires it and does not add it). Previously such a call returned a
77
+ 400 or unparseable prose. Explicit `responseMimeType` is still honored.
78
+ - **`init()` no longer validates connectivity by default.** `Message`,
79
+ `Embedding`, and `ImageGenerator` previously called `models.list()`
80
+ unconditionally during `init()`. This is now gated behind `healthCheck: true`
81
+ (default `false`), matching `BaseGemini`. If you relied on `init()` as a
82
+ startup credentials gate, pass `healthCheck: true`. `models.list()` is a
83
+ different IAM surface than `generateContent` and added a per-instance
84
+ round-trip.
85
+
86
+ ### Added
87
+ - **`usage.estimatedCost`** — every `send()`/`generate()` result and
88
+ `getLastUsage()` now include an estimated USD cost from `MODEL_PRICING`
89
+ (`null` when the model is unpriced). Thinking ("thoughts") tokens are billed at
90
+ the output rate and are now included in cost and exposed as
91
+ `usage.thoughtsTokens`.
92
+ - **Concurrency-safe per-call usage.** `Message.send()` and
93
+ `ImageGenerator.generate()` return `result.usage` computed synchronously from
94
+ that call's own response. `getLastUsage()` still reflects the instance's last
95
+ call and is unsafe to read across concurrent sends on a shared instance — use
96
+ `result.usage`.
97
+ - **Pricing helpers exported:** `MODEL_PRICING`, `MODEL_ALIASES`,
98
+ `resolvePricing()`, `computeCost()`. `resolvePricing` follows `-latest`
99
+ aliases and peels version-build suffixes (e.g. `gemini-2.5-flash-001` →
100
+ `gemini-2.5-flash`).
101
+ - **`validateSchema()`** JSON-Schema validator exported (for cross-package
102
+ symmetry; Gemini enforces schemas natively).
103
+
104
+ ### Fixed
105
+ - `estimateCost()` now uses `resolvePricing()` and returns `null` cost fields for
106
+ unknown models (was `0`), consistent with `usage.estimatedCost`.
107
+ - `estimatedCost` no longer returns `null` for a priced model when the API echoes
108
+ a version-suffixed build id in `modelVersion`.
109
+
110
+ ### Dependencies
111
+ - `@google/genai` `^2.10.0` → `^2.12.0` (no breaking changes to the used surface).
package/GUIDE.md CHANGED
@@ -147,6 +147,8 @@ console.log(result.data);
147
147
 
148
148
  Key difference from `Chat`: `result.data` contains the parsed JSON object. `result.text` contains the raw string.
149
149
 
150
+ > **Note:** `responseSchema` requires `responseMimeType: 'application/json'`. If you pass `responseSchema` without a `responseMimeType`, ak-gemini now auto-defaults it to `'application/json'` (logged at debug). Passing an explicit `responseMimeType` is still honored.
151
+
150
152
  ### When to Use Message
151
153
 
152
154
  - Classification, tagging, or labeling
@@ -1037,10 +1039,18 @@ const usage = instance.getLastUsage();
1037
1039
  // attempts: 1, // 1 = first try, 2+ = retries needed
1038
1040
  // modelVersion: 'gemini-2.5-flash-001', // actual model that responded
1039
1041
  // requestedModel: 'gemini-2.5-flash', // model you requested
1040
- // timestamp: 1710000000000
1042
+ // timestamp: 1710000000000,
1043
+ // thoughtsTokens: 0, // thinking tokens, billed at output rate (in totalTokens + cost)
1044
+ // estimatedCost: 0.00123 // USD from MODEL_PRICING; null if model unpriced
1041
1045
  // }
1042
1046
  ```
1043
1047
 
1048
+ > **Thinking tokens:** for thinking-enabled models, `thoughtsTokens` are billed at the output rate and are included in both `totalTokens` and `estimatedCost`. `responseTokens` is candidate (visible) output only.
1049
+
1050
+ > **Concurrency:** `getLastUsage()` reflects the **instance's last completed call** and mutates on every `send()` — it is **not** safe to read across concurrent sends on a shared instance. The **stateless** classes (`Message`, `ImageGenerator`) return a per-call **`result.usage`** computed synchronously from that call's own response, which *is* concurrency-safe — use it when sharing one instance across concurrent calls. The stateful classes (`Chat`, `Transformer`, `RagAgent`, `ToolAgent`, `CodeAgent`) maintain history/rounds and are not designed to be shared across concurrent calls; their `result.usage` is derived from instance state.
1051
+
1052
+ > **`estimatedCost` / `MODEL_ALIASES` caveat:** `estimatedCost` resolves `-latest` aliases and version-suffixed builds via `MODEL_PRICING`/`MODEL_ALIASES`. Alias→price mappings are pinned as of 2026-07 (e.g. `gemini-flash-latest` → `gemini-3.5-flash`); when Google rotates what a `-latest` alias points to, `estimatedCost` for that alias may be wrong until the table is updated. Pin explicit model ids for billing-grade accuracy.
1053
+
1044
1054
  ### Cost Estimation
1045
1055
 
1046
1056
  Estimate cost *before* sending:
package/README.md CHANGED
@@ -86,8 +86,15 @@ const msg = new Message({
86
86
 
87
87
  const result = await msg.send('Alice works at Acme in New York.');
88
88
  // result.data → { entities: ['Alice', 'Acme', 'New York'] }
89
+ // result.usage → { promptTokens, responseTokens, thoughtsTokens, totalTokens, estimatedCost, ... }
89
90
  ```
90
91
 
92
+ > **`responseSchema` requires `responseMimeType: 'application/json'`.** As of 2.5.0, passing `responseSchema` without a `responseMimeType` auto-defaults it to `'application/json'` — you can omit the line above. An explicit `responseMimeType` is still honored.
93
+
94
+ > **Connectivity check is opt-in.** `Message`, `Embedding`, and `ImageGenerator` do **not** ping the API during `init()` unless you pass `healthCheck: true` (default `false`, as of 2.5.0). The first `send()`/`embed()`/`generate()` surfaces auth/connectivity errors on its own.
95
+
96
+ > **Per-call usage under concurrency.** Read `result.usage` (computed from that call's own response) rather than `getLastUsage()` when sharing a `Message`/`ImageGenerator` instance across concurrent `send()`s — `getLastUsage()` reflects only the instance's last completed call.
97
+
91
98
  ### ToolAgent — Agent with User-Provided Tools
92
99
 
93
100
  Provide tool declarations and an executor function. The agent manages the tool-use loop automatically.
@@ -278,9 +285,29 @@ const cost = await instance.estimateCost({ some: 'payload' });
278
285
 
279
286
  ```javascript
280
287
  const usage = instance.getLastUsage();
281
- // { promptTokens, responseTokens, totalTokens, attempts, modelVersion, requestedModel, timestamp }
288
+ // { promptTokens, responseTokens, thoughtsTokens, cachedTokens, totalTokens,
289
+ // attempts, modelVersion, requestedModel, timestamp, estimatedCost }
282
290
  ```
283
291
 
292
+ `estimatedCost` is USD from `MODEL_PRICING`. **`null` means the model's pricing is
293
+ unknown — not that the call was free.**
294
+
295
+ **`cachedTokens` is a subset of `promptTokens`, not an addition.** Gemini includes
296
+ `cachedContentTokenCount` inside `promptTokenCount`, so `computeCost()` subtracts
297
+ them and re-bills at the discounted `cachedInput` rate. (`ak-claude` is the
298
+ opposite — Anthropic excludes cache tokens from `input_tokens` and they are added
299
+ on top.) Models with no published cached rate bill cached tokens at the full input
300
+ rate: an overestimate, never an underestimate.
301
+
302
+ Two things are deliberately **not** modelled: cache **storage** (time-based,
303
+ $1.00/1M tokens/hour on Flash tiers and $4.50 on Pro) and image-output tokens on
304
+ the Nano Banana models ($60/M or $120/M).
305
+
306
+ Pro models are tiered — `gemini-2.5-pro` and `gemini-3.1-pro-preview` switch to a
307
+ higher rate above 200,000 prompt tokens. `resolvePricing()` returns `tierThreshold`
308
+ and `asOf` (the date the table was last verified against Google's pricing page,
309
+ also exported as `MODEL_PRICING_AS_OF`).
310
+
284
311
  ### Few-Shot Seeding
285
312
 
286
313
  ```javascript
@@ -291,13 +318,30 @@ await instance.seed([
291
318
 
292
319
  ### Thinking Configuration
293
320
 
321
+ Use `effort` — it is the same option name in `ak-claude`, so one config object
322
+ works against both packages:
323
+
294
324
  ```javascript
295
325
  new Chat({
296
- modelName: 'gemini-2.5-flash',
297
- thinkingConfig: { thinkingBudget: 1024 }
326
+ modelName: 'gemini-3.5-flash',
327
+ effort: 'high' // 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
298
328
  });
329
+ // wire: thinkingConfig: { thinkingLevel: 'high' }
299
330
  ```
300
331
 
332
+ `xhigh` and `max` are ak-claude-only levels and clamp to `high` here. An
333
+ unrecognized level throws at construction.
334
+
335
+ The raw SDK shape still works:
336
+
337
+ ```javascript
338
+ new Chat({ modelName: 'gemini-2.5-flash', thinkingConfig: { thinkingBudget: 1024 } });
339
+ ```
340
+
341
+ `thinkingLevel` and `thinkingBudget` are mutually exclusive on the wire — when
342
+ `effort` is set (or you pass `thinkingLevel` yourself), `thinkingBudget` is
343
+ dropped. `effort` wins over both.
344
+
301
345
  ### Google Search Grounding
302
346
 
303
347
  Ground responses in real-time web search results. Available on all classes.
@@ -383,7 +427,8 @@ All classes accept `BaseGeminiOptions`:
383
427
  | `project` | string | env var | GCP project ID |
384
428
  | `location` | string | `'global'` | GCP region |
385
429
  | `chatConfig` | object | — | Gemini chat config overrides |
386
- | `thinkingConfig` | object | — | Thinking features config |
430
+ | `thinkingConfig` | object | — | Thinking features config (`thinkingBudget` or `thinkingLevel`) |
431
+ | `effort` | string | — | `'minimal'`\|`'low'`\|`'medium'`\|`'high'`\|`'xhigh'`\|`'max'`. Maps to `thinkingConfig.thinkingLevel`; wins over it |
387
432
  | `maxOutputTokens` | number | `50000` | Max tokens in response (`null` removes limit) |
388
433
  | `logLevel` | string | based on NODE_ENV | `'trace'`\|`'debug'`\|`'info'`\|`'warn'`\|`'error'`\|`'none'` |
389
434
  | `labels` | object | — | Billing labels (Vertex AI) |
@@ -471,6 +516,13 @@ All tests use real Gemini API calls (no mocks). Rate limiting (429 errors) can c
471
516
 
472
517
  ---
473
518
 
519
+ ## Upgrading
520
+
521
+ **Upgrading from 2.5.0 to 2.6.0?** See [UPGRADING.md](./UPGRADING.md). 2.6.0 adds the
522
+ `effort` option and corrects cost reporting — cached tokens now bill at the discounted
523
+ rate, Pro models are tiered above 200k, and `thoughtsTokens` is no longer dropped from
524
+ `getLastUsage()`. Full detail in [CHANGELOG.md](./CHANGELOG.md).
525
+
474
526
  ## Migration from v1.x
475
527
 
476
528
  See [MIGRATION.md](./MIGRATION.md) for a detailed guide on upgrading from v1.x to v2.0.
package/UPGRADING.md ADDED
@@ -0,0 +1,111 @@
1
+ # Upgrading ak-gemini
2
+
3
+ Per-version upgrade notes. For the full list of changes see [CHANGELOG.md](./CHANGELOG.md). For the v1.x → v2.0 rewrite, see [MIGRATION.md](./MIGRATION.md).
4
+
5
+ ---
6
+
7
+ ## 2.5.0 → 2.6.0
8
+
9
+ **Minor bump. No API surface is removed and no call signature breaks.** What changes is what `estimatedCost` reports — in most cases it goes *down*, because cached tokens and context tiers are now modelled correctly.
10
+
11
+ Companion release to **ak-claude 0.2.0**; the two packages keep matching option names.
12
+
13
+ ### At a glance
14
+
15
+ | If you… | Then… |
16
+ |---|---|
17
+ | don't use context caching and stay under 200k tokens | numbers are unchanged, except `gemini-2.5-flash` — see #3 |
18
+ | use context caching (`cachedContent` / `useCache()`) | your reported cost drops — cached tokens now bill at the discounted rate, see #1 |
19
+ | send >200k-token prompts to a Pro model | your reported cost rises to the correct tier rate — see #2 |
20
+ | use `gemini-2.5-flash` | output rate corrected $2.50 → $3.00 per M — see #3 |
21
+ | use `gemini-flash-latest` or `gemini-flash-lite-latest` | these aliases now resolve to newer models — see #4 |
22
+ | read `getLastUsage()` on Chat / ToolAgent / CodeAgent / RagAgent / Transformer | `thoughtsTokens` is now non-zero when thinking ran — see #5 |
23
+ | want to control reasoning depth | new `effort` option — see #6 |
24
+
25
+ ---
26
+
27
+ ### 1. Cached tokens are billed at the cached rate
28
+
29
+ **This is the change most likely to move your numbers.**
30
+
31
+ Gemini's `promptTokenCount` **includes** `cachedContentTokenCount`. Before 2.6.0, ak-gemini billed the whole prompt count at the full input rate, so cached tokens were charged as if they were fresh — a large overestimate on cache-heavy workloads.
32
+
33
+ `computeCost()` now subtracts cached tokens from the prompt count and re-bills them at `cachedInput` (roughly a 10× discount):
34
+
35
+ ```js
36
+ // promptTokens: 10_000, of which cachedTokens: 8_000, on gemini-3.5-flash
37
+ // 2.5.0: 10_000 × $1.50/M = $0.0150
38
+ // 2.6.0: 2_000 × $1.50/M + 8_000 × $0.15/M = $0.0042
39
+ ```
40
+
41
+ `usage.cachedTokens` is now exposed on both `getLastUsage()` and per-call `result.usage`.
42
+
43
+ > **`cachedTokens` is a SUBSET of `promptTokens`, not an addition.** Do not sum them. This is the opposite of **ak-claude**, where Anthropic *excludes* cache tokens from `input_tokens` and they are added on top. If you have code that normalizes usage across both packages, this asymmetry is the thing to get right.
44
+
45
+ Models with no published `cachedInput` rate fall back to the full input rate — an overestimate, never an underestimate.
46
+
47
+ **Still not modelled:** cache *storage*, which is time-based ($1.00 per 1M tokens per hour on Flash tiers, $4.50 on Pro). `estimatedCost` covers per-token charges only.
48
+
49
+ ### 2. Context-tier pricing above 200k tokens
50
+
51
+ Pro models switch to a higher rate above 200,000 prompt tokens. Previously ak-gemini always billed the ≤200k tier, understating cost on long-context calls.
52
+
53
+ ```js
54
+ resolvePricing('gemini-2.5-pro')
55
+ // { input: 1.25, output: 10.00, cachedInput: 0.125,
56
+ // inputAbove200k: 2.50, outputAbove200k: 15.00, cachedInputAbove200k: 0.25,
57
+ // tierThreshold: 200000, asOf: '2026-07-28' }
58
+ ```
59
+
60
+ `computeCost()` picks the tier from `promptTokens` automatically. Populated for `gemini-2.5-pro` and `gemini-3.1-pro-preview`.
61
+
62
+ ### 3. `gemini-2.5-flash` output rate corrected
63
+
64
+ $2.50 → **$3.00** per M output tokens. The old value was wrong against Google's published pricing. If you track cost over time, expect a step change here.
65
+
66
+ ### 4. Alias repointing
67
+
68
+ | Alias | 2.5.0 resolved to | 2.6.0 resolves to |
69
+ |---|---|---|
70
+ | `gemini-flash-latest` | `gemini-3.5-flash` | `gemini-3.6-flash` |
71
+ | `gemini-flash-lite-latest` | `gemini-3.1-flash-lite` | `gemini-3.5-flash-lite` |
72
+
73
+ Aliases are a pricing-table convenience and go stale whenever Google rotates what they point at. ak-gemini already prefers the model id the API **echoes** (`response.modelVersion`) over the alias you requested, and now logs each alias resolution at debug level. Pin a concrete model id if you need cost reporting you can audit.
74
+
75
+ New pricing entries: `gemini-3.6-flash` ($1.50 / $7.50) and `gemini-3.5-flash-lite` ($0.30 / $2.50).
76
+
77
+ ### 5. `thoughtsTokens` now appears in `getLastUsage()`
78
+
79
+ `Chat`, `ToolAgent`, `CodeAgent`, `RagAgent`, and `Transformer` seeded their cumulative usage without `thoughtsTokens`, so `getLastUsage()` reported `0` and **omitted thinking tokens from `estimatedCost`** even when the response carried them. Per-call `result.usage` was already correct.
80
+
81
+ If you read cost from `getLastUsage()` on a thinking-enabled model, your reported cost will rise — it was under-reporting before. Thinking tokens bill at the output rate.
82
+
83
+ ### 6. New `effort` option
84
+
85
+ The cross-package reasoning-depth knob — same option name in ak-claude.
86
+
87
+ ```js
88
+ new Chat({ modelName: 'gemini-3.5-flash', effort: 'high' })
89
+ // → thinkingConfig: { thinkingLevel: 'high' }
90
+ ```
91
+
92
+ Accepts `'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'`. Gemini's wire field only goes up to `high`, so **`xhigh` and `max` clamp to `high`** — they exist so the same config object works against both packages. An unrecognized value **throws at construction**.
93
+
94
+ `effort` wins over an explicit `thinkingLevel` or `thinkingBudget`; `thinkingLevel` and `thinkingBudget` are mutually exclusive on the wire, so setting `effort` deletes any budget. `thinkingConfig: null` still disables thinking outright and takes precedence over `effort`.
95
+
96
+ `thinkingConfig: { thinkingBudget: N }` continues to work unchanged.
97
+
98
+ ### New exports
99
+
100
+ ```js
101
+ import {
102
+ MODEL_PRICING_AS_OF, // date the pricing table was last verified
103
+ EFFORT_LEVELS, // ['minimal','low','medium','high','xhigh','max']
104
+ resolvePricing, // now returns `tierThreshold` and `asOf`
105
+ computeCost // now takes (model, prompt, response, thoughts?, cached?)
106
+ } from 'ak-gemini';
107
+ ```
108
+
109
+ `computeCost()`'s new `cachedTokens` argument is optional and trailing — existing 3- and 4-argument calls behave as before, except for the tier selection in #2.
110
+
111
+ > **A `null` from `resolvePricing()` or `estimatedCost` means UNKNOWN, not free.** Unpriced models (Gemma, anything not in the table) return `null`. Don't coerce it to `0` in a cost rollup.