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 +111 -0
- package/GUIDE.md +11 -1
- package/README.md +56 -4
- package/UPGRADING.md +111 -0
- package/base.js +292 -34
- package/chat.js +2 -0
- package/code-agent.js +2 -0
- package/embedding.js +4 -6
- package/image-generator.js +10 -7
- package/index.cjs +319 -57
- package/index.js +2 -1
- package/json-helpers.js +105 -0
- package/message.js +20 -7
- package/package.json +5 -3
- package/rag-agent.js +2 -0
- package/tool-agent.js +2 -0
- package/transformer.js +6 -2
- package/types.d.ts +87 -4
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.
|
|
337
|
-
"gemini-3.
|
|
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": {
|
|
340
|
-
|
|
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:
|
|
351
|
-
"gemini-2.5-flash-lite": { input: 0.1, output: 0.4 },
|
|
352
|
-
"gemini-2.5-pro": {
|
|
353
|
-
|
|
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
|
-
|
|
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
|
|
616
|
-
responseTokens
|
|
617
|
-
|
|
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
|
|
639
|
-
responseTokens
|
|
640
|
-
|
|
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 =
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|