ak-gemini 2.3.0 → 2.4.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/base.js CHANGED
@@ -37,11 +37,13 @@ const THINKING_SUPPORTED_MODELS = [
37
37
  ];
38
38
 
39
39
  /**
40
- * Model pricing per million tokens (Paid Tier Standard, base rate, as of May 2026).
40
+ * Model pricing per million tokens (Paid Tier Standard, base rate, as of July 2026).
41
41
  * Source: https://ai.google.dev/gemini-api/docs/pricing
42
42
  *
43
43
  * NOTES:
44
44
  * - Pro models use tiered pricing (≤200k vs >200k context). Listed rate is ≤200k base tier.
45
+ * - Gemma models (e.g. Gemma 4) are intentionally excluded — they are open models with
46
+ * no paid per-token tier on the Gemini API (Vertex deployments bill by compute).
45
47
  * - Image-output tokens on Nano Banana models bill at $60/M (1.5 Flash Image) or $120/M (3 Pro Image).
46
48
  * Only text-input/text-output rates are modelled here; image-output cost is NOT included in estimateCost().
47
49
  * - Audio input is more expensive on most models — listed rate covers text/image/video input.
@@ -52,6 +54,7 @@ const MODEL_PRICING = {
52
54
  'gemini-3.1-flash-lite': { input: 0.25, output: 1.50 },
53
55
  // Gemini 3.x preview
54
56
  'gemini-3.1-pro-preview': { input: 2.00, output: 12.00 }, // ≤200k tier
57
+ 'gemini-3-pro-preview': { input: 2.00, output: 12.00 }, // ≤200k tier; launch rate (superseded by 3.1, off the pricing page)
55
58
  'gemini-3-flash-preview': { input: 0.50, output: 3.00 },
56
59
  'gemini-3.1-flash-lite-preview': { input: 0.25, output: 1.50 },
57
60
  'gemini-3.1-flash-image-preview': { input: 0.50, output: 3.00 }, // text-only; image-output is $60/M
@@ -155,6 +158,13 @@ class BaseGemini {
155
158
  if (this.serviceTier) this.chatConfig['serviceTier'] = this.serviceTier;
156
159
  if (this.includeServerSideToolInvocations) this.chatConfig['includeServerSideToolInvocations'] = true;
157
160
 
161
+ // ── Sampling Shortcuts ──
162
+ // Top-level sugar for chatConfig sampling knobs; wins over chatConfig
163
+ // (same precedence as the maxOutputTokens promotion below)
164
+ for (const key of ['temperature', 'topP', 'topK']) {
165
+ if (options[key] !== undefined) this.chatConfig[key] = options[key];
166
+ }
167
+
158
168
  // Apply systemPrompt to chatConfig
159
169
  if (this.systemPrompt) {
160
170
  this.chatConfig.systemInstruction = this.systemPrompt;
@@ -321,6 +331,7 @@ class BaseGemini {
321
331
  * @param {string} [opts.contextKey='CONTEXT'] - Key for optional context
322
332
  * @param {string} [opts.explanationKey='EXPLANATION'] - Key for optional explanations
323
333
  * @param {string} [opts.systemPromptKey='SYSTEM'] - Key for system prompt overrides in examples
334
+ * @param {'json'|'text'} [opts.format='json'] - Model-turn format: 'json' wraps answers in a {data} envelope (Transformer protocol); 'text' stores ANSWER verbatim (prose agents like Chat)
324
335
  * @returns {Promise<Array>} The updated chat history
325
336
  */
326
337
  async seed(examples, opts = {}) {
@@ -336,6 +347,7 @@ class BaseGemini {
336
347
  const contextKey = opts.contextKey || 'CONTEXT';
337
348
  const explanationKey = opts.explanationKey || 'EXPLANATION';
338
349
  const systemPromptKey = opts.systemPromptKey || 'SYSTEM';
350
+ const format = opts.format || 'json';
339
351
 
340
352
  // Check for system prompt override in examples
341
353
  const instructionExample = examples.find(ex => ex[systemPromptKey]);
@@ -367,9 +379,15 @@ class BaseGemini {
367
379
  userText += promptText;
368
380
  }
369
381
 
370
- if (answerValue) modelResponse.data = answerValue;
371
- if (explanationValue) modelResponse.explanation = explanationValue;
372
- const modelText = JSON.stringify(modelResponse, null, 2);
382
+ let modelText;
383
+ if (format === 'text') {
384
+ modelText = isJSON(answerValue) ? JSON.stringify(answerValue, null, 2) : String(answerValue || '');
385
+ if (explanationValue) log.warn('seed(): EXPLANATION has no representation in text format; ignored.');
386
+ } else {
387
+ if (answerValue) modelResponse.data = answerValue;
388
+ if (explanationValue) modelResponse.explanation = explanationValue;
389
+ modelText = JSON.stringify(modelResponse, null, 2);
390
+ }
373
391
 
374
392
  if (userText.trim().length && modelText.trim().length > 0) {
375
393
  historyToAdd.push({ role: 'user', parts: [{ text: userText.trim() }] });
package/chat.js CHANGED
@@ -47,6 +47,19 @@ class Chat extends BaseGemini {
47
47
  log.debug(`Chat created with model: ${this.modelName}`);
48
48
  }
49
49
 
50
+ /**
51
+ * Seeds the conversation with example pairs stored as plain prose turns.
52
+ * Chat is a prose agent — model turns are stored verbatim, not wrapped in
53
+ * Transformer's {data} JSON envelope.
54
+ *
55
+ * @param {import('./types').TransformationExample[]} [examples]
56
+ * @param {import('./types').SeedOptions} [opts={}]
57
+ * @returns {Promise<Array>} The updated chat history
58
+ */
59
+ async seed(examples, opts = {}) {
60
+ return super.seed(examples, { format: 'text', ...opts });
61
+ }
62
+
50
63
  /**
51
64
  * Send a text message and get a response. Adds to conversation history.
52
65
  *
package/index.cjs CHANGED
@@ -338,6 +338,8 @@ var MODEL_PRICING = {
338
338
  // Gemini 3.x preview
339
339
  "gemini-3.1-pro-preview": { input: 2, output: 12 },
340
340
  // ≤200k tier
341
+ "gemini-3-pro-preview": { input: 2, output: 12 },
342
+ // ≤200k tier; launch rate (superseded by 3.1, off the pricing page)
341
343
  "gemini-3-flash-preview": { input: 0.5, output: 3 },
342
344
  "gemini-3.1-flash-lite-preview": { input: 0.25, output: 1.5 },
343
345
  "gemini-3.1-flash-image-preview": { input: 0.5, output: 3 },
@@ -398,6 +400,9 @@ var BaseGemini = class {
398
400
  };
399
401
  if (this.serviceTier) this.chatConfig["serviceTier"] = this.serviceTier;
400
402
  if (this.includeServerSideToolInvocations) this.chatConfig["includeServerSideToolInvocations"] = true;
403
+ for (const key of ["temperature", "topP", "topK"]) {
404
+ if (options[key] !== void 0) this.chatConfig[key] = options[key];
405
+ }
401
406
  if (this.systemPrompt) {
402
407
  this.chatConfig.systemInstruction = this.systemPrompt;
403
408
  } else if (this.systemPrompt === null && options.systemPrompt === void 0) {
@@ -531,6 +536,7 @@ var BaseGemini = class {
531
536
  * @param {string} [opts.contextKey='CONTEXT'] - Key for optional context
532
537
  * @param {string} [opts.explanationKey='EXPLANATION'] - Key for optional explanations
533
538
  * @param {string} [opts.systemPromptKey='SYSTEM'] - Key for system prompt overrides in examples
539
+ * @param {'json'|'text'} [opts.format='json'] - Model-turn format: 'json' wraps answers in a {data} envelope (Transformer protocol); 'text' stores ANSWER verbatim (prose agents like Chat)
534
540
  * @returns {Promise<Array>} The updated chat history
535
541
  */
536
542
  async seed(examples, opts = {}) {
@@ -544,6 +550,7 @@ var BaseGemini = class {
544
550
  const contextKey = opts.contextKey || "CONTEXT";
545
551
  const explanationKey = opts.explanationKey || "EXPLANATION";
546
552
  const systemPromptKey = opts.systemPromptKey || "SYSTEM";
553
+ const format = opts.format || "json";
547
554
  const instructionExample = examples.find((ex) => ex[systemPromptKey]);
548
555
  if (instructionExample) {
549
556
  logger_default.debug(`Found system prompt in examples; reinitializing chat.`);
@@ -572,9 +579,15 @@ ${contextText}
572
579
  let promptText = isJSON(promptValue) ? JSON.stringify(promptValue, null, 2) : promptValue;
573
580
  userText += promptText;
574
581
  }
575
- if (answerValue) modelResponse.data = answerValue;
576
- if (explanationValue) modelResponse.explanation = explanationValue;
577
- const modelText = JSON.stringify(modelResponse, null, 2);
582
+ let modelText;
583
+ if (format === "text") {
584
+ modelText = isJSON(answerValue) ? JSON.stringify(answerValue, null, 2) : String(answerValue || "");
585
+ if (explanationValue) logger_default.warn("seed(): EXPLANATION has no representation in text format; ignored.");
586
+ } else {
587
+ if (answerValue) modelResponse.data = answerValue;
588
+ if (explanationValue) modelResponse.explanation = explanationValue;
589
+ modelText = JSON.stringify(modelResponse, null, 2);
590
+ }
578
591
  if (userText.trim().length && modelText.trim().length > 0) {
579
592
  historyToAdd.push({ role: "user", parts: [{ text: userText.trim() }] });
580
593
  historyToAdd.push({ role: "model", parts: [{ text: modelText.trim() }] });
@@ -938,7 +951,8 @@ var Transformer = class extends base_default {
938
951
  answerKey: this.answerKey,
939
952
  contextKey: this.contextKey,
940
953
  explanationKey: this.explanationKey,
941
- systemPromptKey: this.systemPromptKey
954
+ systemPromptKey: this.systemPromptKey,
955
+ format: "json"
942
956
  });
943
957
  }
944
958
  // ── Primary Send Method ──────────────────────────────────────────────────
@@ -1216,6 +1230,18 @@ var Chat = class extends base_default {
1216
1230
  super(options);
1217
1231
  logger_default.debug(`Chat created with model: ${this.modelName}`);
1218
1232
  }
1233
+ /**
1234
+ * Seeds the conversation with example pairs stored as plain prose turns.
1235
+ * Chat is a prose agent — model turns are stored verbatim, not wrapped in
1236
+ * Transformer's {data} JSON envelope.
1237
+ *
1238
+ * @param {import('./types').TransformationExample[]} [examples]
1239
+ * @param {import('./types').SeedOptions} [opts={}]
1240
+ * @returns {Promise<Array>} The updated chat history
1241
+ */
1242
+ async seed(examples, opts = {}) {
1243
+ return super.seed(examples, { format: "text", ...opts });
1244
+ }
1219
1245
  /**
1220
1246
  * Send a text message and get a response. Adds to conversation history.
1221
1247
  *
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "ak-gemini",
3
3
  "author": "ak@mixpanel.com",
4
4
  "description": "AK's Generative AI Helper for doing... everything",
5
- "version": "2.3.0",
5
+ "version": "2.4.1",
6
6
  "main": "index.js",
7
7
  "files": [
8
8
  "index.js",
@@ -65,7 +65,7 @@
65
65
  ],
66
66
  "license": "ISC",
67
67
  "dependencies": {
68
- "@google/genai": "^2.6.0",
68
+ "@google/genai": "^2.10.0",
69
69
  "dotenv": "^17.3.1",
70
70
  "pino": "^10.3.1",
71
71
  "pino-pretty": "^13.1.3"
package/transformer.js CHANGED
@@ -141,7 +141,8 @@ class Transformer extends BaseGemini {
141
141
  answerKey: this.answerKey,
142
142
  contextKey: this.contextKey,
143
143
  explanationKey: this.explanationKey,
144
- systemPromptKey: this.systemPromptKey
144
+ systemPromptKey: this.systemPromptKey,
145
+ format: 'json'
145
146
  });
146
147
  }
147
148
 
package/types.d.ts CHANGED
@@ -147,6 +147,12 @@ export interface BaseGeminiOptions {
147
147
  thinkingConfig?: ThinkingConfig | null;
148
148
  /** Maximum output tokens (default: 50000, null removes limit) */
149
149
  maxOutputTokens?: number | null;
150
+ /** Sampling temperature (shorthand for chatConfig.temperature; wins over chatConfig) */
151
+ temperature?: number;
152
+ /** Nucleus sampling (shorthand for chatConfig.topP; wins over chatConfig) */
153
+ topP?: number;
154
+ /** Top-k sampling (shorthand for chatConfig.topK; wins over chatConfig) */
155
+ topK?: number;
150
156
  /** Log level (default: based on NODE_ENV) */
151
157
  logLevel?: LogLevel;
152
158
 
@@ -563,6 +569,8 @@ export interface SeedOptions {
563
569
  contextKey?: string;
564
570
  explanationKey?: string;
565
571
  systemPromptKey?: string;
572
+ /** Model-turn format: 'json' wraps answers in a {data} envelope (Transformer protocol); 'text' stores ANSWER verbatim (prose agents like Chat). Default: 'json' */
573
+ format?: 'json' | 'text';
566
574
  }
567
575
 
568
576
  // ── Class Declarations ───────────────────────────────────────────────────────