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/index.cjs CHANGED
@@ -32,19 +32,26 @@ __export(index_exports, {
32
32
  BaseGemini: () => base_default,
33
33
  Chat: () => chat_default,
34
34
  CodeAgent: () => code_agent_default,
35
+ EFFORT_LEVELS: () => EFFORT_LEVELS,
35
36
  Embedding: () => Embedding,
36
37
  HarmBlockThreshold: () => import_genai2.HarmBlockThreshold,
37
38
  HarmCategory: () => import_genai2.HarmCategory,
38
39
  ImageGenerator: () => ImageGenerator,
40
+ MODEL_ALIASES: () => MODEL_ALIASES,
41
+ MODEL_PRICING: () => MODEL_PRICING,
42
+ MODEL_PRICING_AS_OF: () => MODEL_PRICING_AS_OF,
39
43
  Message: () => message_default,
40
44
  RagAgent: () => rag_agent_default,
41
45
  ThinkingLevel: () => import_genai2.ThinkingLevel,
42
46
  ToolAgent: () => tool_agent_default,
43
47
  Transformer: () => transformer_default,
44
48
  attemptJSONRecovery: () => attemptJSONRecovery,
49
+ computeCost: () => computeCost,
45
50
  default: () => index_default,
46
51
  extractJSON: () => extractJSON,
47
- log: () => logger_default
52
+ log: () => logger_default,
53
+ resolvePricing: () => resolvePricing,
54
+ validateSchema: () => validateSchema
48
55
  });
49
56
  module.exports = __toCommonJS(index_exports);
50
57
 
@@ -255,6 +262,79 @@ function findCompleteJSONStructures(text) {
255
262
  }
256
263
  return results;
257
264
  }
265
+ function validateSchema(data, schema, path2 = "$") {
266
+ const errors = [];
267
+ if (!schema || typeof schema !== "object") return errors;
268
+ if (data === null && schema.nullable === true) return errors;
269
+ if (Array.isArray(schema.enum)) {
270
+ const target = JSON.stringify(data);
271
+ const ok = schema.enum.some((v) => v === data || JSON.stringify(v) === target);
272
+ if (!ok) errors.push(`${path2}: value ${JSON.stringify(data)} is not one of allowed enum values ${JSON.stringify(schema.enum)}`);
273
+ }
274
+ if (schema.type !== void 0) {
275
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type];
276
+ if (schema.nullable === true) types.push("null");
277
+ if (!types.some((t) => matchesType(data, t))) {
278
+ errors.push(`${path2}: expected type ${types.join("|")} but got ${describeType(data)}`);
279
+ return errors;
280
+ }
281
+ }
282
+ const isObject = data !== null && typeof data === "object" && !Array.isArray(data);
283
+ if (isObject && (schema.properties || schema.required || schema.additionalProperties === false)) {
284
+ const props = schema.properties || {};
285
+ if (Array.isArray(schema.required)) {
286
+ for (const key of schema.required) {
287
+ if (!Object.hasOwn(data, key)) errors.push(`${path2}: missing required property "${key}"`);
288
+ }
289
+ }
290
+ if (schema.additionalProperties === false) {
291
+ for (const key of Object.keys(data)) {
292
+ if (!Object.hasOwn(props, key)) errors.push(`${path2}: unexpected property "${key}" (additionalProperties is false)`);
293
+ }
294
+ }
295
+ for (const [key, subSchema] of Object.entries(props)) {
296
+ if (Object.hasOwn(data, key)) {
297
+ errors.push(...validateSchema(
298
+ data[key],
299
+ /** @type {any} */
300
+ subSchema,
301
+ `${path2}.${key}`
302
+ ));
303
+ }
304
+ }
305
+ }
306
+ if (Array.isArray(data) && schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
307
+ data.forEach((item, i) => {
308
+ errors.push(...validateSchema(item, schema.items, `${path2}[${i}]`));
309
+ });
310
+ }
311
+ return errors;
312
+ }
313
+ function matchesType(value, type) {
314
+ switch (type) {
315
+ case "string":
316
+ return typeof value === "string";
317
+ case "number":
318
+ return typeof value === "number" && !Number.isNaN(value);
319
+ case "integer":
320
+ return typeof value === "number" && Number.isInteger(value);
321
+ case "boolean":
322
+ return typeof value === "boolean";
323
+ case "object":
324
+ return value !== null && typeof value === "object" && !Array.isArray(value);
325
+ case "array":
326
+ return Array.isArray(value);
327
+ case "null":
328
+ return value === null;
329
+ default:
330
+ return true;
331
+ }
332
+ }
333
+ function describeType(value) {
334
+ if (value === null) return "null";
335
+ if (Array.isArray(value)) return "array";
336
+ return typeof value;
337
+ }
258
338
  function extractJSON(text) {
259
339
  if (!text || typeof text !== "string") {
260
340
  throw new Error("No text provided for JSON extraction");
@@ -322,6 +402,15 @@ var DEFAULT_THINKING_CONFIG = {
322
402
  thinkingBudget: 0
323
403
  };
324
404
  var DEFAULT_MAX_OUTPUT_TOKENS = 5e4;
405
+ var EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh", "max"];
406
+ var EFFORT_TO_THINKING_LEVEL = {
407
+ minimal: "minimal",
408
+ low: "low",
409
+ medium: "medium",
410
+ high: "high",
411
+ xhigh: "high",
412
+ max: "high"
413
+ };
325
414
  var THINKING_SUPPORTED_MODELS = [
326
415
  /^gemini-3(\.\d+)?-pro(-preview)?$/,
327
416
  /^gemini-3(\.\d+)?-flash(-preview)?$/,
@@ -331,26 +420,42 @@ var THINKING_SUPPORTED_MODELS = [
331
420
  /^gemini-2\.5-flash-lite(-preview)?$/,
332
421
  /^gemini-2\.0-flash$/
333
422
  ];
423
+ var MODEL_PRICING_AS_OF = "2026-07-28";
424
+ var TIER_THRESHOLD = 2e5;
334
425
  var MODEL_PRICING = {
335
426
  // Gemini 3.x stable
336
- "gemini-3.5-flash": { input: 1.5, output: 9 },
337
- "gemini-3.1-flash-lite": { input: 0.25, output: 1.5 },
427
+ "gemini-3.6-flash": { input: 1.5, output: 7.5, cachedInput: 0.15 },
428
+ "gemini-3.5-flash": { input: 1.5, output: 9, cachedInput: 0.15 },
429
+ "gemini-3.5-flash-lite": { input: 0.3, output: 2.5, cachedInput: 0.03 },
430
+ "gemini-3.1-flash-lite": { input: 0.25, output: 1.5, cachedInput: 0.025 },
338
431
  // Gemini 3.x preview
339
- "gemini-3.1-pro-preview": { input: 2, output: 12 },
340
- // ≤200k tier
432
+ "gemini-3.1-pro-preview": {
433
+ input: 2,
434
+ output: 12,
435
+ cachedInput: 0.2,
436
+ inputAbove200k: 4,
437
+ outputAbove200k: 18,
438
+ cachedInputAbove200k: 0.4
439
+ },
341
440
  "gemini-3-pro-preview": { input: 2, output: 12 },
342
441
  // ≤200k tier; launch rate (superseded by 3.1, off the pricing page)
343
442
  "gemini-3-flash-preview": { input: 0.5, output: 3 },
344
- "gemini-3.1-flash-lite-preview": { input: 0.25, output: 1.5 },
443
+ "gemini-3.1-flash-lite-preview": { input: 0.25, output: 1.5, cachedInput: 0.025 },
345
444
  "gemini-3.1-flash-image-preview": { input: 0.5, output: 3 },
346
445
  // text-only; image-output is $60/M
347
446
  "gemini-3-pro-image-preview": { input: 2, output: 12 },
348
447
  // text-only; image-output is $120/M
349
448
  // Gemini 2.5 stable
350
- "gemini-2.5-flash": { input: 0.3, output: 2.5 },
351
- "gemini-2.5-flash-lite": { input: 0.1, output: 0.4 },
352
- "gemini-2.5-pro": { input: 1.25, output: 10 },
353
- // ≤200k tier
449
+ "gemini-2.5-flash": { input: 0.3, output: 3, cachedInput: 0.05 },
450
+ "gemini-2.5-flash-lite": { input: 0.1, output: 0.4, cachedInput: 0.01 },
451
+ "gemini-2.5-pro": {
452
+ input: 1.25,
453
+ output: 10,
454
+ cachedInput: 0.125,
455
+ inputAbove200k: 2.5,
456
+ outputAbove200k: 15,
457
+ cachedInputAbove200k: 0.25
458
+ },
354
459
  "gemini-2.5-flash-image": { input: 0.3, output: 0 },
355
460
  // image-output is ~$0.039/image (1290 tokens)
356
461
  // Deprecated but kept for back-compat (shut down June 2026)
@@ -359,6 +464,50 @@ var MODEL_PRICING = {
359
464
  // Embeddings
360
465
  "gemini-embedding-001": { input: 0.15, output: 0 }
361
466
  };
467
+ var MODEL_ALIASES = {
468
+ "gemini-flash-latest": "gemini-3.6-flash",
469
+ "gemini-pro-latest": "gemini-3.1-pro-preview",
470
+ "gemini-flash-lite-latest": "gemini-3.5-flash-lite"
471
+ };
472
+ function resolvePricing(modelId) {
473
+ const entry = _lookupPricing(modelId);
474
+ if (!entry) return null;
475
+ return { ...entry, tierThreshold: TIER_THRESHOLD, asOf: MODEL_PRICING_AS_OF };
476
+ }
477
+ function _lookupPricing(modelId) {
478
+ if (!modelId) return null;
479
+ const tryKey = (id) => {
480
+ if (MODEL_PRICING[id]) return MODEL_PRICING[id];
481
+ const alias = MODEL_ALIASES[id];
482
+ if (alias && MODEL_PRICING[alias]) {
483
+ logger_default.debug(`Pricing: alias "${id}" resolved to "${alias}". If Google has rotated this alias the rate may be wrong \u2014 prefer the echoed modelVersion.`);
484
+ return MODEL_PRICING[alias];
485
+ }
486
+ return null;
487
+ };
488
+ let hit = tryKey(modelId);
489
+ if (hit) return hit;
490
+ let stripped = modelId;
491
+ while (true) {
492
+ const next = stripped.replace(/-\d{2,}$/, "");
493
+ if (next === stripped) break;
494
+ stripped = next;
495
+ hit = tryKey(stripped);
496
+ if (hit) return hit;
497
+ }
498
+ return null;
499
+ }
500
+ function computeCost(modelId, promptTokens, responseTokens, thoughtsTokens = 0, cachedTokens = 0) {
501
+ const pricing = resolvePricing(modelId);
502
+ if (!pricing) return null;
503
+ const above = (promptTokens || 0) > TIER_THRESHOLD;
504
+ const inputRate = above && pricing.inputAbove200k || pricing.input;
505
+ const outputRate = above && pricing.outputAbove200k || pricing.output;
506
+ const cachedRate = above ? pricing.cachedInputAbove200k ?? pricing.cachedInput ?? inputRate : pricing.cachedInput ?? inputRate;
507
+ const cached = Math.max(0, Math.min(cachedTokens || 0, promptTokens || 0));
508
+ const billablePrompt = Math.max(0, (promptTokens || 0) - cached);
509
+ return billablePrompt / 1e6 * inputRate + cached / 1e6 * cachedRate + ((responseTokens || 0) + (thoughtsTokens || 0)) / 1e6 * outputRate;
510
+ }
362
511
  var BaseGemini = class {
363
512
  /**
364
513
  * @param {BaseGeminiOptions} [options={}]
@@ -422,6 +571,7 @@ var BaseGemini = class {
422
571
  } else {
423
572
  this.chatConfig.maxOutputTokens = DEFAULT_MAX_OUTPUT_TOKENS;
424
573
  }
574
+ this.effort = this._normalizeEffort(options.effort);
425
575
  this._configureThinking(options.thinkingConfig);
426
576
  const clientOptions = this.vertexai ? {
427
577
  vertexai: true,
@@ -436,6 +586,8 @@ var BaseGemini = class {
436
586
  this._cumulativeUsage = {
437
587
  promptTokens: 0,
438
588
  responseTokens: 0,
589
+ thoughtsTokens: 0,
590
+ cachedTokens: 0,
439
591
  totalTokens: 0,
440
592
  attempts: 0
441
593
  };
@@ -453,16 +605,25 @@ var BaseGemini = class {
453
605
  logger_default.debug(`Initializing ${this.constructor.name} chat session with model: ${this.modelName}...`);
454
606
  const chatOptions = this._getChatCreateOptions();
455
607
  this.chatSession = this.genAIClient.chats.create(chatOptions);
456
- if (this.healthCheck) {
457
- try {
458
- await this._withRetry(() => this.genAIClient.models.list());
459
- logger_default.debug(`${this.constructor.name}: API connection successful.`);
460
- } catch (e) {
461
- throw new Error(`${this.constructor.name} initialization failed: ${e.message}`);
462
- }
463
- }
608
+ await this._healthCheckPing();
464
609
  logger_default.debug(`${this.constructor.name}: Chat session initialized.`);
465
610
  }
611
+ /**
612
+ * Opt-in connectivity check via `models.list()` — runs only when
613
+ * `healthCheck: true`. Fails fast (no exponential backoff): a readiness probe
614
+ * should surface a bad config immediately, not after ~30s of 429 retries.
615
+ * @returns {Promise<void>}
616
+ * @protected
617
+ */
618
+ async _healthCheckPing() {
619
+ if (!this.healthCheck) return;
620
+ try {
621
+ await this.genAIClient.models.list();
622
+ logger_default.debug(`${this.constructor.name}: API connection successful.`);
623
+ } catch (e) {
624
+ throw new Error(`${this.constructor.name} initialization failed: ${e.message}`);
625
+ }
626
+ }
466
627
  /**
467
628
  * Builds the options object for `genAIClient.chats.create()`.
468
629
  * Override in subclasses to add tools, grounding, etc.
@@ -523,7 +684,7 @@ var BaseGemini = class {
523
684
  }
524
685
  this.chatSession = this._createChatSession([]);
525
686
  this.lastResponseMetadata = null;
526
- this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 0 };
687
+ this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, thoughtsTokens: 0, cachedTokens: 0, totalTokens: 0, attempts: 0 };
527
688
  logger_default.debug(`${this.constructor.name}: Conversation history cleared.`);
528
689
  }
529
690
  // ── Few-Shot Seeding ─────────────────────────────────────────────────────
@@ -609,12 +770,19 @@ ${contextText}
609
770
  */
610
771
  _captureMetadata(response) {
611
772
  const modelStatus = response?.modelStatus || null;
773
+ const promptTokens = response.usageMetadata?.promptTokenCount || 0;
774
+ const responseTokens = response.usageMetadata?.candidatesTokenCount || 0;
775
+ const thoughtsTokens = response.usageMetadata?.thoughtsTokenCount || 0;
776
+ const cachedTokens = response.usageMetadata?.cachedContentTokenCount || 0;
612
777
  this.lastResponseMetadata = {
613
778
  modelVersion: response.modelVersion || null,
614
779
  requestedModel: this.modelName,
615
- promptTokens: response.usageMetadata?.promptTokenCount || 0,
616
- responseTokens: response.usageMetadata?.candidatesTokenCount || 0,
617
- totalTokens: response.usageMetadata?.totalTokenCount || 0,
780
+ promptTokens,
781
+ responseTokens,
782
+ thoughtsTokens,
783
+ cachedTokens,
784
+ // totalTokenCount includes thoughts; fall back to summing the parts.
785
+ totalTokens: response.usageMetadata?.totalTokenCount || promptTokens + responseTokens + thoughtsTokens,
618
786
  timestamp: Date.now(),
619
787
  groundingMetadata: response.candidates?.[0]?.groundingMetadata || null,
620
788
  modelStatus
@@ -632,18 +800,73 @@ ${contextText}
632
800
  getLastUsage() {
633
801
  if (!this.lastResponseMetadata) return null;
634
802
  const meta = this.lastResponseMetadata;
635
- const cumulative = this._cumulativeUsage || { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 1 };
803
+ const cumulative = this._cumulativeUsage || { promptTokens: 0, responseTokens: 0, totalTokens: 0, thoughtsTokens: 0, cachedTokens: 0, attempts: 1 };
636
804
  const useCumulative = cumulative.attempts > 0;
805
+ const promptTokens = useCumulative ? cumulative.promptTokens : meta.promptTokens;
806
+ const responseTokens = useCumulative ? cumulative.responseTokens : meta.responseTokens;
807
+ const thoughtsTokens = useCumulative ? cumulative.thoughtsTokens || 0 : meta.thoughtsTokens || 0;
808
+ const totalTokens = useCumulative ? cumulative.totalTokens : meta.totalTokens;
809
+ const cachedTokens = useCumulative && cumulative.cachedTokens !== void 0 ? cumulative.cachedTokens : meta.cachedTokens || 0;
637
810
  return {
638
- promptTokens: useCumulative ? cumulative.promptTokens : meta.promptTokens,
639
- responseTokens: useCumulative ? cumulative.responseTokens : meta.responseTokens,
640
- totalTokens: useCumulative ? cumulative.totalTokens : meta.totalTokens,
811
+ promptTokens,
812
+ responseTokens,
813
+ thoughtsTokens,
814
+ cachedTokens,
815
+ totalTokens,
641
816
  attempts: useCumulative ? cumulative.attempts : 1,
642
817
  modelVersion: meta.modelVersion,
643
818
  requestedModel: meta.requestedModel,
644
819
  timestamp: meta.timestamp,
645
820
  groundingMetadata: meta.groundingMetadata || null,
646
- modelStatus: meta.modelStatus || null
821
+ modelStatus: meta.modelStatus || null,
822
+ estimatedCost: this._estimatedCost(meta.modelVersion, promptTokens, responseTokens, thoughtsTokens, cachedTokens)
823
+ };
824
+ }
825
+ /**
826
+ * Estimated USD cost, preferring the model id the API echoed (`modelVersion`)
827
+ * and falling back to the requested model when that build isn't priced.
828
+ * @param {string|null|undefined} modelVersion
829
+ * @param {number} promptTokens
830
+ * @param {number} responseTokens
831
+ * @param {number} [thoughtsTokens=0]
832
+ * @param {number} [cachedTokens=0] - Cached-content tokens (a SUBSET of promptTokens)
833
+ * @returns {number|null}
834
+ * @protected
835
+ */
836
+ _estimatedCost(modelVersion, promptTokens, responseTokens, thoughtsTokens = 0, cachedTokens = 0) {
837
+ return computeCost(modelVersion, promptTokens, responseTokens, thoughtsTokens, cachedTokens) ?? computeCost(this.modelName, promptTokens, responseTokens, thoughtsTokens, cachedTokens);
838
+ }
839
+ /**
840
+ * Builds a usage object directly from a single API response, WITHOUT reading
841
+ * mutable instance state (`lastResponseMetadata`/`_cumulativeUsage`). Safe to
842
+ * call under concurrent send() calls on a shared instance — unlike
843
+ * getLastUsage(), which reflects whichever call most recently mutated the
844
+ * instance and can cross-talk between concurrent sends.
845
+ * @param {Object} response - A single generateContent() response
846
+ * @param {number} [attempts=1] - Attempts this call consumed
847
+ * @returns {UsageData}
848
+ * @protected
849
+ */
850
+ _usageFromResponse(response, attempts = 1) {
851
+ const promptTokens = response?.usageMetadata?.promptTokenCount || 0;
852
+ const responseTokens = response?.usageMetadata?.candidatesTokenCount || 0;
853
+ const thoughtsTokens = response?.usageMetadata?.thoughtsTokenCount || 0;
854
+ const cachedTokens = response?.usageMetadata?.cachedContentTokenCount || 0;
855
+ const totalTokens = response?.usageMetadata?.totalTokenCount || promptTokens + responseTokens + thoughtsTokens;
856
+ const modelVersion = response?.modelVersion || null;
857
+ return {
858
+ promptTokens,
859
+ responseTokens,
860
+ thoughtsTokens,
861
+ cachedTokens,
862
+ totalTokens,
863
+ attempts,
864
+ modelVersion,
865
+ requestedModel: this.modelName,
866
+ timestamp: Date.now(),
867
+ groundingMetadata: response?.candidates?.[0]?.groundingMetadata || null,
868
+ modelStatus: response?.modelStatus || null,
869
+ estimatedCost: this._estimatedCost(modelVersion, promptTokens, responseTokens, thoughtsTokens, cachedTokens)
647
870
  };
648
871
  }
649
872
  // ── Token Estimation ─────────────────────────────────────────────────────
@@ -679,13 +902,13 @@ ${contextText}
679
902
  */
680
903
  async estimateCost(nextPayload) {
681
904
  const tokenInfo = await this.estimate(nextPayload);
682
- const pricing = MODEL_PRICING[this.modelName] || { input: 0, output: 0 };
905
+ const pricing = resolvePricing(this.modelName);
683
906
  return {
684
907
  inputTokens: tokenInfo.inputTokens,
685
908
  model: this.modelName,
686
909
  pricing,
687
- estimatedInputCost: tokenInfo.inputTokens / 1e6 * pricing.input,
688
- note: "Cost is for input tokens only; output cost depends on response length"
910
+ estimatedInputCost: pricing ? tokenInfo.inputTokens / 1e6 * pricing.input : null,
911
+ note: pricing ? "Cost is for input tokens only; output cost depends on response length" : `No pricing known for model "${this.modelName}"; estimatedInputCost is null`
689
912
  };
690
913
  }
691
914
  // ── Context Caching ─────────────────────────────────────────────────────
@@ -848,23 +1071,49 @@ ${contextText}
848
1071
  */
849
1072
  _configureThinking(thinkingConfig) {
850
1073
  const modelSupportsThinking = THINKING_SUPPORTED_MODELS.some((p) => p.test(this.modelName));
851
- if (thinkingConfig === void 0) return;
1074
+ if (thinkingConfig === void 0 && this.effort === null) return;
852
1075
  if (thinkingConfig === null) {
853
1076
  delete this.chatConfig.thinkingConfig;
854
1077
  logger_default.debug(`thinkingConfig set to null - removed from configuration`);
855
1078
  return;
856
1079
  }
857
1080
  if (!modelSupportsThinking) {
858
- logger_default.warn(`Model ${this.modelName} does not support thinking features. Ignoring thinkingConfig.`);
1081
+ logger_default.warn(`Model ${this.modelName} does not support thinking features. Ignoring thinkingConfig/effort.`);
859
1082
  return;
860
1083
  }
861
- const config = { ...DEFAULT_THINKING_CONFIG, ...thinkingConfig };
862
- if (thinkingConfig.thinkingLevel !== void 0) {
1084
+ const config = { ...DEFAULT_THINKING_CONFIG, ...thinkingConfig || {} };
1085
+ if (this.effort !== null) {
1086
+ const level = EFFORT_TO_THINKING_LEVEL[this.effort];
1087
+ if (level !== this.effort) {
1088
+ logger_default.debug(`Effort '${this.effort}' has no Gemini equivalent; using thinkingLevel '${level}'.`);
1089
+ }
1090
+ config.thinkingLevel = level;
1091
+ }
1092
+ if (config.thinkingLevel !== void 0) {
863
1093
  delete config.thinkingBudget;
864
1094
  }
865
1095
  this.chatConfig.thinkingConfig = config;
866
1096
  logger_default.debug(`Thinking config applied: ${JSON.stringify(config)}`);
867
1097
  }
1098
+ /**
1099
+ * Validates the cross-package `effort` option.
1100
+ * Throws rather than warns: this runs in the constructor before log levels are
1101
+ * finalized, so a warning could be swallowed.
1102
+ * @param {string|null|undefined} effort
1103
+ * @returns {'minimal'|'low'|'medium'|'high'|'xhigh'|'max'|null}
1104
+ * @protected
1105
+ */
1106
+ _normalizeEffort(effort) {
1107
+ if (effort === void 0 || effort === null) return null;
1108
+ const level = String(effort).toLowerCase();
1109
+ if (!EFFORT_LEVELS.includes(level)) {
1110
+ throw new Error(`Invalid effort "${effort}". Expected one of: ${EFFORT_LEVELS.join(", ")}.`);
1111
+ }
1112
+ return (
1113
+ /** @type {any} */
1114
+ level
1115
+ );
1116
+ }
868
1117
  };
869
1118
  var base_default = BaseGemini;
870
1119
 
@@ -996,7 +1245,7 @@ var Transformer = class extends base_default {
996
1245
  let lastPayload = this._preparePayload(payload);
997
1246
  const messageOptions = {};
998
1247
  if (opts.labels) messageOptions.labels = opts.labels;
999
- this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 0 };
1248
+ this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, thoughtsTokens: 0, cachedTokens: 0, totalTokens: 0, attempts: 0 };
1000
1249
  let lastError = null;
1001
1250
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
1002
1251
  try {
@@ -1004,6 +1253,8 @@ var Transformer = class extends base_default {
1004
1253
  if (this.lastResponseMetadata) {
1005
1254
  this._cumulativeUsage.promptTokens += this.lastResponseMetadata.promptTokens || 0;
1006
1255
  this._cumulativeUsage.responseTokens += this.lastResponseMetadata.responseTokens || 0;
1256
+ this._cumulativeUsage.thoughtsTokens += this.lastResponseMetadata.thoughtsTokens || 0;
1257
+ this._cumulativeUsage.cachedTokens += this.lastResponseMetadata.cachedTokens || 0;
1007
1258
  this._cumulativeUsage.totalTokens += this.lastResponseMetadata.totalTokens || 0;
1008
1259
  this._cumulativeUsage.attempts = attempt + 1;
1009
1260
  }
@@ -1139,6 +1390,8 @@ Respond with JSON only \u2013 no comments or explanations.
1139
1390
  this._cumulativeUsage = {
1140
1391
  promptTokens: this.lastResponseMetadata.promptTokens,
1141
1392
  responseTokens: this.lastResponseMetadata.responseTokens,
1393
+ thoughtsTokens: this.lastResponseMetadata.thoughtsTokens,
1394
+ cachedTokens: this.lastResponseMetadata.cachedTokens,
1142
1395
  totalTokens: this.lastResponseMetadata.totalTokens,
1143
1396
  attempts: 1
1144
1397
  };
@@ -1164,7 +1417,7 @@ Respond with JSON only \u2013 no comments or explanations.
1164
1417
  const exampleHistory = history.slice(0, this.exampleCount || 0);
1165
1418
  this.chatSession = this._createChatSession(exampleHistory);
1166
1419
  this.lastResponseMetadata = null;
1167
- this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, totalTokens: 0, attempts: 0 };
1420
+ this._cumulativeUsage = { promptTokens: 0, responseTokens: 0, thoughtsTokens: 0, cachedTokens: 0, totalTokens: 0, attempts: 0 };
1168
1421
  logger_default.debug(`Conversation cleared. Preserved ${exampleHistory.length} example items.`);
1169
1422
  }
1170
1423
  /**
@@ -1263,6 +1516,8 @@ var Chat = class extends base_default {
1263
1516
  this._cumulativeUsage = {
1264
1517
  promptTokens: this.lastResponseMetadata.promptTokens,
1265
1518
  responseTokens: this.lastResponseMetadata.responseTokens,
1519
+ thoughtsTokens: this.lastResponseMetadata.thoughtsTokens,
1520
+ cachedTokens: this.lastResponseMetadata.cachedTokens,
1266
1521
  totalTokens: this.lastResponseMetadata.totalTokens,
1267
1522
  attempts: 1
1268
1523
  };
@@ -1310,6 +1565,9 @@ var Message = class extends base_default {
1310
1565
  }
1311
1566
  if (options.responseMimeType) {
1312
1567
  this.chatConfig.responseMimeType = options.responseMimeType;
1568
+ } else if (options.responseSchema) {
1569
+ this.chatConfig.responseMimeType = "application/json";
1570
+ logger_default.debug("responseSchema set without responseMimeType \u2014 defaulting responseMimeType to 'application/json'.");
1313
1571
  }
1314
1572
  this._isStructured = !!(options.responseSchema || options.responseMimeType === "application/json");
1315
1573
  logger_default.debug(`Message created (structured=${this._isStructured})`);
@@ -1323,12 +1581,7 @@ var Message = class extends base_default {
1323
1581
  async init(force = false) {
1324
1582
  if (this._initialized && !force) return;
1325
1583
  logger_default.debug(`Initializing ${this.constructor.name} with model: ${this.modelName}...`);
1326
- try {
1327
- await this.genAIClient.models.list();
1328
- logger_default.debug(`${this.constructor.name}: API connection successful.`);
1329
- } catch (e) {
1330
- throw new Error(`${this.constructor.name} initialization failed: ${e.message}`);
1331
- }
1584
+ await this._healthCheckPing();
1332
1585
  this._initialized = true;
1333
1586
  logger_default.debug(`${this.constructor.name}: Initialized (stateless mode).`);
1334
1587
  }
@@ -1354,10 +1607,13 @@ var Message = class extends base_default {
1354
1607
  ...this.vertexai && Object.keys(mergedLabels).length > 0 && { labels: mergedLabels }
1355
1608
  }
1356
1609
  }));
1610
+ const usage = this._usageFromResponse(result);
1357
1611
  this._captureMetadata(result);
1358
1612
  this._cumulativeUsage = {
1359
1613
  promptTokens: this.lastResponseMetadata.promptTokens,
1360
1614
  responseTokens: this.lastResponseMetadata.responseTokens,
1615
+ thoughtsTokens: this.lastResponseMetadata.thoughtsTokens,
1616
+ cachedTokens: this.lastResponseMetadata.cachedTokens,
1361
1617
  totalTokens: this.lastResponseMetadata.totalTokens,
1362
1618
  attempts: 1
1363
1619
  };
@@ -1367,7 +1623,7 @@ var Message = class extends base_default {
1367
1623
  const text = result.text || "";
1368
1624
  const response = {
1369
1625
  text,
1370
- usage: this.getLastUsage()
1626
+ usage
1371
1627
  };
1372
1628
  if (this._isStructured) {
1373
1629
  try {
@@ -1515,6 +1771,8 @@ var ToolAgent = class extends base_default {
1515
1771
  this._cumulativeUsage = {
1516
1772
  promptTokens: this.lastResponseMetadata.promptTokens,
1517
1773
  responseTokens: this.lastResponseMetadata.responseTokens,
1774
+ thoughtsTokens: this.lastResponseMetadata.thoughtsTokens,
1775
+ cachedTokens: this.lastResponseMetadata.cachedTokens,
1518
1776
  totalTokens: this.lastResponseMetadata.totalTokens,
1519
1777
  attempts: 1
1520
1778
  };
@@ -2526,6 +2784,8 @@ ${this.envOverview}`;
2526
2784
  this._cumulativeUsage = {
2527
2785
  promptTokens: this.lastResponseMetadata.promptTokens,
2528
2786
  responseTokens: this.lastResponseMetadata.responseTokens,
2787
+ thoughtsTokens: this.lastResponseMetadata.thoughtsTokens,
2788
+ cachedTokens: this.lastResponseMetadata.cachedTokens,
2529
2789
  totalTokens: this.lastResponseMetadata.totalTokens,
2530
2790
  attempts: 1
2531
2791
  };
@@ -2844,6 +3104,8 @@ ${serialized}` });
2844
3104
  this._cumulativeUsage = {
2845
3105
  promptTokens: this.lastResponseMetadata.promptTokens,
2846
3106
  responseTokens: this.lastResponseMetadata.responseTokens,
3107
+ thoughtsTokens: this.lastResponseMetadata.thoughtsTokens,
3108
+ cachedTokens: this.lastResponseMetadata.cachedTokens,
2847
3109
  totalTokens: this.lastResponseMetadata.totalTokens,
2848
3110
  attempts: 1
2849
3111
  };
@@ -2981,12 +3243,7 @@ var Embedding = class extends base_default {
2981
3243
  async init(force = false) {
2982
3244
  if (this._initialized && !force) return;
2983
3245
  logger_default.debug(`Initializing ${this.constructor.name} with model: ${this.modelName}...`);
2984
- try {
2985
- await this.genAIClient.models.list();
2986
- logger_default.debug(`${this.constructor.name}: API connection successful.`);
2987
- } catch (e) {
2988
- throw new Error(`${this.constructor.name} initialization failed: ${e.message}`);
2989
- }
3246
+ await this._healthCheckPing();
2990
3247
  this._initialized = true;
2991
3248
  logger_default.debug(`${this.constructor.name}: Initialized (stateless mode).`);
2992
3249
  }
@@ -3118,12 +3375,7 @@ var ImageGenerator = class extends base_default {
3118
3375
  async init(force = false) {
3119
3376
  if (this._initialized && !force) return;
3120
3377
  logger_default.debug(`Initializing ${this.constructor.name} with model: ${this.modelName}...`);
3121
- try {
3122
- await this.genAIClient.models.list();
3123
- logger_default.debug(`${this.constructor.name}: API connection successful.`);
3124
- } catch (e) {
3125
- throw new Error(`${this.constructor.name} initialization failed: ${e.message}`);
3126
- }
3378
+ await this._healthCheckPing();
3127
3379
  this._initialized = true;
3128
3380
  }
3129
3381
  /**
@@ -3165,10 +3417,13 @@ var ImageGenerator = class extends base_default {
3165
3417
  contents: [{ role: "user", parts }],
3166
3418
  config: this._buildConfig(opts)
3167
3419
  }));
3420
+ const usage = this._usageFromResponse(result);
3168
3421
  this._captureMetadata(result);
3169
3422
  this._cumulativeUsage = {
3170
3423
  promptTokens: this.lastResponseMetadata.promptTokens,
3171
3424
  responseTokens: this.lastResponseMetadata.responseTokens,
3425
+ thoughtsTokens: this.lastResponseMetadata.thoughtsTokens,
3426
+ cachedTokens: this.lastResponseMetadata.cachedTokens,
3172
3427
  totalTokens: this.lastResponseMetadata.totalTokens,
3173
3428
  attempts: 1
3174
3429
  };
@@ -3188,7 +3443,7 @@ var ImageGenerator = class extends base_default {
3188
3443
  if (images.length === 0) {
3189
3444
  logger_default.warn("ImageGenerator: no images returned. Check prompt or safety filters.");
3190
3445
  }
3191
- return { images, text: text || null, usage: this.getLastUsage() };
3446
+ return { images, text: text || null, usage };
3192
3447
  }
3193
3448
  /**
3194
3449
  * Convenience: write one or all images to disk.
@@ -3244,16 +3499,23 @@ var index_default = { Transformer: transformer_default, Chat: chat_default, Mess
3244
3499
  BaseGemini,
3245
3500
  Chat,
3246
3501
  CodeAgent,
3502
+ EFFORT_LEVELS,
3247
3503
  Embedding,
3248
3504
  HarmBlockThreshold,
3249
3505
  HarmCategory,
3250
3506
  ImageGenerator,
3507
+ MODEL_ALIASES,
3508
+ MODEL_PRICING,
3509
+ MODEL_PRICING_AS_OF,
3251
3510
  Message,
3252
3511
  RagAgent,
3253
3512
  ThinkingLevel,
3254
3513
  ToolAgent,
3255
3514
  Transformer,
3256
3515
  attemptJSONRecovery,
3516
+ computeCost,
3257
3517
  extractJSON,
3258
- log
3518
+ log,
3519
+ resolvePricing,
3520
+ validateSchema
3259
3521
  });
package/index.js CHANGED
@@ -29,9 +29,10 @@ export { default as RagAgent } from './rag-agent.js';
29
29
  export { default as Embedding } from './embedding.js';
30
30
  export { default as ImageGenerator } from './image-generator.js';
31
31
  export { default as BaseGemini } from './base.js';
32
+ export { MODEL_PRICING, MODEL_PRICING_AS_OF, MODEL_ALIASES, EFFORT_LEVELS, resolvePricing, computeCost } from './base.js';
32
33
  export { default as log } from './logger.js';
33
34
  export { ThinkingLevel, HarmCategory, HarmBlockThreshold } from '@google/genai';
34
- export { extractJSON, attemptJSONRecovery } from './json-helpers.js';
35
+ export { extractJSON, attemptJSONRecovery, validateSchema } from './json-helpers.js';
35
36
 
36
37
  // ── Default Export (namespace object) ──
37
38