ak-gemini 2.0.1 → 2.0.3

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ak-gemini
2
2
 
3
- **Modular, type-safe wrapper for Google's Gemini AI.** Five class exports for different interaction patterns — JSON transformation, chat, stateless messages, tool-using agents, and code-writing agents — all sharing a common base.
3
+ **Modular, type-safe wrapper for Google's Gemini AI.** Seven class exports for different interaction patterns — JSON transformation, chat, stateless messages, tool-using agents, code-writing agents, document Q&A, and embeddings — all sharing a common base.
4
4
 
5
5
  ```sh
6
6
  npm install ak-gemini
@@ -17,7 +17,7 @@ export GEMINI_API_KEY=your-key
17
17
  ```
18
18
 
19
19
  ```javascript
20
- import { Transformer, Chat, Message, ToolAgent, CodeAgent } from 'ak-gemini';
20
+ import { Transformer, Chat, Message, ToolAgent, CodeAgent, RagAgent, Embedding } from 'ak-gemini';
21
21
  ```
22
22
 
23
23
  ---
@@ -176,6 +176,27 @@ for await (const event of agent.stream('Refactor the auth module')) {
176
176
  }
177
177
  ```
178
178
 
179
+ ### Embedding — Vector Embeddings
180
+
181
+ Generate vector embeddings for similarity search, clustering, and classification.
182
+
183
+ ```javascript
184
+ const embedder = new Embedding({
185
+ modelName: 'gemini-embedding-001', // default
186
+ taskType: 'RETRIEVAL_DOCUMENT'
187
+ });
188
+
189
+ // Single text
190
+ const result = await embedder.embed('Hello world');
191
+ console.log(result.values); // [0.012, -0.034, ...]
192
+
193
+ // Batch
194
+ const results = await embedder.embedBatch(['Hello', 'World']);
195
+
196
+ // Cosine similarity (pure math, no API call)
197
+ const score = embedder.similarity(results[0].values, results[1].values);
198
+ ```
199
+
179
200
  ---
180
201
 
181
202
  ## Stopping Agents
@@ -252,6 +273,43 @@ new Chat({
252
273
  });
253
274
  ```
254
275
 
276
+ ### Google Search Grounding
277
+
278
+ Ground responses in real-time web search results. Available on all classes.
279
+
280
+ ```javascript
281
+ const chat = new Chat({
282
+ enableGrounding: true,
283
+ groundingConfig: { excludeDomains: ['example.com'] }
284
+ });
285
+
286
+ const result = await chat.send('Who won the 2026 Super Bowl?');
287
+ const sources = result.usage?.groundingMetadata?.groundingChunks;
288
+ ```
289
+
290
+ **Warning**: Google Search grounding costs ~$35/1k queries.
291
+
292
+ ### Context Caching
293
+
294
+ Reduce costs by caching repeated system prompts, documents, or tool definitions.
295
+
296
+ ```javascript
297
+ const chat = new Chat({ systemPrompt: longSystemPrompt });
298
+
299
+ // Create a cache
300
+ const cache = await chat.createCache({
301
+ ttl: '3600s',
302
+ displayName: 'my-system-prompt-cache'
303
+ });
304
+
305
+ // Use the cache (subsequent calls use cached tokens at reduced cost)
306
+ await chat.useCache(cache.name);
307
+ const result = await chat.send('Hello!');
308
+
309
+ // Clean up
310
+ await chat.deleteCache(cache.name);
311
+ ```
312
+
255
313
  ### Billing Labels (Vertex AI)
256
314
 
257
315
  ```javascript
@@ -281,6 +339,9 @@ All classes accept `BaseGeminiOptions`:
281
339
  | `maxOutputTokens` | number | `50000` | Max tokens in response (`null` removes limit) |
282
340
  | `logLevel` | string | based on NODE_ENV | `'trace'`\|`'debug'`\|`'info'`\|`'warn'`\|`'error'`\|`'none'` |
283
341
  | `labels` | object | — | Billing labels (Vertex AI) |
342
+ | `enableGrounding` | boolean | `false` | Enable Google Search grounding |
343
+ | `groundingConfig` | object | — | Grounding config (excludeDomains, timeRangeFilter) |
344
+ | `cachedContent` | string | — | Cached content resource name |
284
345
 
285
346
  ### Transformer-Specific
286
347
 
@@ -293,8 +354,6 @@ All classes accept `BaseGeminiOptions`:
293
354
  | `retryDelay` | number | `1000` | Initial retry delay (ms) |
294
355
  | `responseSchema` | object | — | JSON schema for output validation |
295
356
  | `asyncValidator` | function | — | Global async validator |
296
- | `enableGrounding` | boolean | `false` | Enable Google Search grounding |
297
-
298
357
  ### ToolAgent-Specific
299
358
 
300
359
  | Option | Type | Default | Description |
@@ -322,21 +381,31 @@ All classes accept `BaseGeminiOptions`:
322
381
  | `responseSchema` | object | — | Schema for structured output |
323
382
  | `responseMimeType` | string | — | e.g. `'application/json'` |
324
383
 
384
+ ### Embedding-Specific
385
+
386
+ | Option | Type | Default | Description |
387
+ |--------|------|---------|-------------|
388
+ | `taskType` | string | — | `'RETRIEVAL_DOCUMENT'`, `'RETRIEVAL_QUERY'`, `'SEMANTIC_SIMILARITY'`, `'CLUSTERING'` |
389
+ | `title` | string | — | Document title (only with `RETRIEVAL_DOCUMENT`) |
390
+ | `outputDimensionality` | number | — | Output vector dimensions |
391
+ | `autoTruncate` | boolean | `true` | Auto-truncate long inputs |
392
+
325
393
  ---
326
394
 
327
395
  ## Exports
328
396
 
329
397
  ```javascript
330
398
  // Named exports
331
- import { Transformer, Chat, Message, ToolAgent, CodeAgent, BaseGemini, log } from 'ak-gemini';
399
+ import { Transformer, Chat, Message, ToolAgent, CodeAgent, RagAgent, Embedding, BaseGemini, log } from 'ak-gemini';
332
400
  import { extractJSON, attemptJSONRecovery } from 'ak-gemini';
333
401
 
334
402
  // Default export (namespace)
335
403
  import AI from 'ak-gemini';
336
404
  new AI.Transformer({ ... });
405
+ new AI.Embedding({ ... });
337
406
 
338
407
  // CommonJS
339
- const { Transformer, Chat } = require('ak-gemini');
408
+ const { Transformer, Chat, Embedding } = require('ak-gemini');
340
409
  ```
341
410
 
342
411
  ---
package/base.js CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import dotenv from 'dotenv';
8
- dotenv.config();
8
+ dotenv.config({ quiet: true });
9
9
  const { NODE_ENV = "unknown", LOG_LEVEL = "" } = process.env;
10
10
 
11
11
  import { GoogleGenAI, HarmCategory, HarmBlockThreshold } from '@google/genai';
@@ -43,7 +43,8 @@ const MODEL_PRICING = {
43
43
  'gemini-3-pro': { input: 2.00, output: 12.00 },
44
44
  'gemini-3-pro-preview': { input: 2.00, output: 12.00 },
45
45
  'gemini-2.0-flash': { input: 0.10, output: 0.40 },
46
- 'gemini-2.0-flash-lite': { input: 0.02, output: 0.10 }
46
+ 'gemini-2.0-flash-lite': { input: 0.02, output: 0.10 },
47
+ 'gemini-embedding-001': { input: 0.006, output: 0 }
47
48
  };
48
49
 
49
50
  export { DEFAULT_SAFETY_SETTINGS, DEFAULT_THINKING_CONFIG, THINKING_SUPPORTED_MODELS, MODEL_PRICING, DEFAULT_MAX_OUTPUT_TOKENS };
@@ -99,6 +100,13 @@ class BaseGemini {
99
100
  // ── Labels ──
100
101
  this.labels = options.labels || {};
101
102
 
103
+ // ── Grounding ──
104
+ this.enableGrounding = options.enableGrounding || false;
105
+ this.groundingConfig = options.groundingConfig || {};
106
+
107
+ // ── Caching ──
108
+ this.cachedContent = options.cachedContent || null;
109
+
102
110
  // ── Chat Config ──
103
111
  this.chatConfig = {
104
112
  temperature: 0.7,
@@ -197,14 +205,24 @@ class BaseGemini {
197
205
  * @protected
198
206
  */
199
207
  _getChatCreateOptions() {
200
- return {
208
+ const opts = {
201
209
  model: this.modelName,
202
210
  config: {
203
211
  ...this.chatConfig,
204
- ...(this.vertexai && Object.keys(this.labels).length > 0 && { labels: this.labels })
212
+ ...(this.vertexai && Object.keys(this.labels).length > 0 && { labels: this.labels }),
213
+ ...(this.cachedContent && { cachedContent: this.cachedContent })
205
214
  },
206
215
  history: []
207
216
  };
217
+
218
+ // Merge grounding into tools (preserving existing tools like functionDeclarations)
219
+ if (this.enableGrounding) {
220
+ const existingTools = opts.config.tools || [];
221
+ opts.config.tools = [...existingTools, { googleSearch: this.groundingConfig }];
222
+ log.debug('Search grounding ENABLED (WARNING: costs $35/1k queries)');
223
+ }
224
+
225
+ return opts;
208
226
  }
209
227
 
210
228
  // ── Chat Session Management ──────────────────────────────────────────────
@@ -344,7 +362,8 @@ class BaseGemini {
344
362
  promptTokens: response.usageMetadata?.promptTokenCount || 0,
345
363
  responseTokens: response.usageMetadata?.candidatesTokenCount || 0,
346
364
  totalTokens: response.usageMetadata?.totalTokenCount || 0,
347
- timestamp: Date.now()
365
+ timestamp: Date.now(),
366
+ groundingMetadata: response.candidates?.[0]?.groundingMetadata || null
348
367
  };
349
368
  }
350
369
 
@@ -367,7 +386,8 @@ class BaseGemini {
367
386
  attempts: useCumulative ? cumulative.attempts : 1,
368
387
  modelVersion: meta.modelVersion,
369
388
  requestedModel: meta.requestedModel,
370
- timestamp: meta.timestamp
389
+ timestamp: meta.timestamp,
390
+ groundingMetadata: meta.groundingMetadata || null
371
391
  };
372
392
  }
373
393
 
@@ -425,6 +445,112 @@ class BaseGemini {
425
445
  };
426
446
  }
427
447
 
448
+ // ── Context Caching ─────────────────────────────────────────────────────
449
+
450
+ /**
451
+ * Creates a cached content resource for cost reduction on repeated prompts.
452
+ * Auto-populates model and systemInstruction from this instance if not provided.
453
+ * @param {Object} [config={}] - Cache configuration
454
+ * @param {string} [config.model] - Model (defaults to this.modelName)
455
+ * @param {string} [config.ttl] - Time-to-live (e.g., '3600s')
456
+ * @param {string} [config.displayName] - Human-readable name
457
+ * @param {Array} [config.contents] - Content to cache
458
+ * @param {string} [config.systemInstruction] - System prompt to cache (defaults to this.systemPrompt)
459
+ * @param {Array} [config.tools] - Tools to cache
460
+ * @param {Object} [config.toolConfig] - Tool configuration to cache
461
+ * @returns {Promise<Object>} The created cache resource
462
+ */
463
+ async createCache(config = {}) {
464
+ const cacheConfig = {};
465
+ if (config.ttl) cacheConfig.ttl = config.ttl;
466
+ if (config.displayName) cacheConfig.displayName = config.displayName;
467
+ if (config.contents) cacheConfig.contents = config.contents;
468
+ if (config.tools) cacheConfig.tools = config.tools;
469
+ if (config.toolConfig) cacheConfig.toolConfig = config.toolConfig;
470
+
471
+ // Auto-populate systemInstruction from instance if not provided
472
+ const sysInstruction = config.systemInstruction !== undefined ? config.systemInstruction : this.systemPrompt;
473
+ if (sysInstruction) cacheConfig.systemInstruction = sysInstruction;
474
+
475
+ const cached = await this.genAIClient.caches.create({
476
+ model: config.model || this.modelName,
477
+ config: cacheConfig
478
+ });
479
+
480
+ log.debug(`Cache created: ${cached.name}`);
481
+ return cached;
482
+ }
483
+
484
+ /**
485
+ * Retrieves a cached content resource by name.
486
+ * @param {string} cacheName - Server-generated resource name
487
+ * @returns {Promise<Object>} The cached content resource
488
+ */
489
+ async getCache(cacheName) {
490
+ return await this.genAIClient.caches.get({ name: cacheName });
491
+ }
492
+
493
+ /**
494
+ * Lists all cached content resources.
495
+ * @returns {Promise<Object>} Pager of cached content resources
496
+ */
497
+ async listCaches() {
498
+ const pager = await this.genAIClient.caches.list();
499
+ const results = [];
500
+ for await (const cache of pager) {
501
+ results.push(cache);
502
+ }
503
+ return results;
504
+ }
505
+
506
+ /**
507
+ * Updates a cached content resource (TTL or expiration).
508
+ * @param {string} cacheName - Server-generated resource name
509
+ * @param {Object} [config={}] - Update config
510
+ * @param {string} [config.ttl] - New TTL (e.g., '7200s')
511
+ * @param {string} [config.expireTime] - New expiration (RFC 3339)
512
+ * @returns {Promise<Object>} The updated cache resource
513
+ */
514
+ async updateCache(cacheName, config = {}) {
515
+ return await this.genAIClient.caches.update({
516
+ name: cacheName,
517
+ config: {
518
+ ...(config.ttl && { ttl: config.ttl }),
519
+ ...(config.expireTime && { expireTime: config.expireTime })
520
+ }
521
+ });
522
+ }
523
+
524
+ /**
525
+ * Deletes a cached content resource.
526
+ * Clears this.cachedContent if it matches the deleted cache.
527
+ * @param {string} cacheName - Server-generated resource name
528
+ * @returns {Promise<void>}
529
+ */
530
+ async deleteCache(cacheName) {
531
+ await this.genAIClient.caches.delete({ name: cacheName });
532
+ log.debug(`Cache deleted: ${cacheName}`);
533
+ if (this.cachedContent === cacheName) {
534
+ this.cachedContent = null;
535
+ }
536
+ }
537
+
538
+ /**
539
+ * Sets the cached content for this instance and reinitializes the session.
540
+ * @param {string} cacheName - Server-generated cache resource name
541
+ * @returns {Promise<void>}
542
+ */
543
+ async useCache(cacheName) {
544
+ this.cachedContent = cacheName;
545
+ // When using cached content, remove systemInstruction from chatConfig
546
+ // since it's already baked into the cache — the API rejects duplicates
547
+ delete this.chatConfig.systemInstruction;
548
+ if (this.chatSession) {
549
+ await this.init(true);
550
+ }
551
+ log.debug(`Using cache: ${cacheName}`);
552
+ }
553
+
428
554
  // ── Private Helpers ──────────────────────────────────────────────────────
429
555
 
430
556
  /**
package/index.cjs CHANGED
@@ -32,6 +32,7 @@ __export(index_exports, {
32
32
  BaseGemini: () => base_default,
33
33
  Chat: () => chat_default,
34
34
  CodeAgent: () => code_agent_default,
35
+ Embedding: () => Embedding,
35
36
  HarmBlockThreshold: () => import_genai2.HarmBlockThreshold,
36
37
  HarmCategory: () => import_genai2.HarmCategory,
37
38
  Message: () => message_default,
@@ -310,7 +311,7 @@ function extractJSON(text) {
310
311
  }
311
312
 
312
313
  // base.js
313
- import_dotenv.default.config();
314
+ import_dotenv.default.config({ quiet: true });
314
315
  var { NODE_ENV = "unknown", LOG_LEVEL = "" } = process.env;
315
316
  var DEFAULT_SAFETY_SETTINGS = [
316
317
  { category: import_genai.HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: import_genai.HarmBlockThreshold.BLOCK_NONE },
@@ -335,7 +336,8 @@ var MODEL_PRICING = {
335
336
  "gemini-3-pro": { input: 2, output: 12 },
336
337
  "gemini-3-pro-preview": { input: 2, output: 12 },
337
338
  "gemini-2.0-flash": { input: 0.1, output: 0.4 },
338
- "gemini-2.0-flash-lite": { input: 0.02, output: 0.1 }
339
+ "gemini-2.0-flash-lite": { input: 0.02, output: 0.1 },
340
+ "gemini-embedding-001": { input: 6e-3, output: 0 }
339
341
  };
340
342
  var BaseGemini = class {
341
343
  /**
@@ -361,6 +363,9 @@ var BaseGemini = class {
361
363
  }
362
364
  this._configureLogLevel(options.logLevel);
363
365
  this.labels = options.labels || {};
366
+ this.enableGrounding = options.enableGrounding || false;
367
+ this.groundingConfig = options.groundingConfig || {};
368
+ this.cachedContent = options.cachedContent || null;
364
369
  this.chatConfig = {
365
370
  temperature: 0.7,
366
371
  topP: 0.95,
@@ -433,14 +438,21 @@ var BaseGemini = class {
433
438
  * @protected
434
439
  */
435
440
  _getChatCreateOptions() {
436
- return {
441
+ const opts = {
437
442
  model: this.modelName,
438
443
  config: {
439
444
  ...this.chatConfig,
440
- ...this.vertexai && Object.keys(this.labels).length > 0 && { labels: this.labels }
445
+ ...this.vertexai && Object.keys(this.labels).length > 0 && { labels: this.labels },
446
+ ...this.cachedContent && { cachedContent: this.cachedContent }
441
447
  },
442
448
  history: []
443
449
  };
450
+ if (this.enableGrounding) {
451
+ const existingTools = opts.config.tools || [];
452
+ opts.config.tools = [...existingTools, { googleSearch: this.groundingConfig }];
453
+ logger_default.debug("Search grounding ENABLED (WARNING: costs $35/1k queries)");
454
+ }
455
+ return opts;
444
456
  }
445
457
  // ── Chat Session Management ──────────────────────────────────────────────
446
458
  /**
@@ -562,7 +574,8 @@ ${contextText}
562
574
  promptTokens: response.usageMetadata?.promptTokenCount || 0,
563
575
  responseTokens: response.usageMetadata?.candidatesTokenCount || 0,
564
576
  totalTokens: response.usageMetadata?.totalTokenCount || 0,
565
- timestamp: Date.now()
577
+ timestamp: Date.now(),
578
+ groundingMetadata: response.candidates?.[0]?.groundingMetadata || null
566
579
  };
567
580
  }
568
581
  /**
@@ -582,7 +595,8 @@ ${contextText}
582
595
  attempts: useCumulative ? cumulative.attempts : 1,
583
596
  modelVersion: meta.modelVersion,
584
597
  requestedModel: meta.requestedModel,
585
- timestamp: meta.timestamp
598
+ timestamp: meta.timestamp,
599
+ groundingMetadata: meta.groundingMetadata || null
586
600
  };
587
601
  }
588
602
  // ── Token Estimation ─────────────────────────────────────────────────────
@@ -627,6 +641,99 @@ ${contextText}
627
641
  note: "Cost is for input tokens only; output cost depends on response length"
628
642
  };
629
643
  }
644
+ // ── Context Caching ─────────────────────────────────────────────────────
645
+ /**
646
+ * Creates a cached content resource for cost reduction on repeated prompts.
647
+ * Auto-populates model and systemInstruction from this instance if not provided.
648
+ * @param {Object} [config={}] - Cache configuration
649
+ * @param {string} [config.model] - Model (defaults to this.modelName)
650
+ * @param {string} [config.ttl] - Time-to-live (e.g., '3600s')
651
+ * @param {string} [config.displayName] - Human-readable name
652
+ * @param {Array} [config.contents] - Content to cache
653
+ * @param {string} [config.systemInstruction] - System prompt to cache (defaults to this.systemPrompt)
654
+ * @param {Array} [config.tools] - Tools to cache
655
+ * @param {Object} [config.toolConfig] - Tool configuration to cache
656
+ * @returns {Promise<Object>} The created cache resource
657
+ */
658
+ async createCache(config = {}) {
659
+ const cacheConfig = {};
660
+ if (config.ttl) cacheConfig.ttl = config.ttl;
661
+ if (config.displayName) cacheConfig.displayName = config.displayName;
662
+ if (config.contents) cacheConfig.contents = config.contents;
663
+ if (config.tools) cacheConfig.tools = config.tools;
664
+ if (config.toolConfig) cacheConfig.toolConfig = config.toolConfig;
665
+ const sysInstruction = config.systemInstruction !== void 0 ? config.systemInstruction : this.systemPrompt;
666
+ if (sysInstruction) cacheConfig.systemInstruction = sysInstruction;
667
+ const cached = await this.genAIClient.caches.create({
668
+ model: config.model || this.modelName,
669
+ config: cacheConfig
670
+ });
671
+ logger_default.debug(`Cache created: ${cached.name}`);
672
+ return cached;
673
+ }
674
+ /**
675
+ * Retrieves a cached content resource by name.
676
+ * @param {string} cacheName - Server-generated resource name
677
+ * @returns {Promise<Object>} The cached content resource
678
+ */
679
+ async getCache(cacheName) {
680
+ return await this.genAIClient.caches.get({ name: cacheName });
681
+ }
682
+ /**
683
+ * Lists all cached content resources.
684
+ * @returns {Promise<Object>} Pager of cached content resources
685
+ */
686
+ async listCaches() {
687
+ const pager = await this.genAIClient.caches.list();
688
+ const results = [];
689
+ for await (const cache of pager) {
690
+ results.push(cache);
691
+ }
692
+ return results;
693
+ }
694
+ /**
695
+ * Updates a cached content resource (TTL or expiration).
696
+ * @param {string} cacheName - Server-generated resource name
697
+ * @param {Object} [config={}] - Update config
698
+ * @param {string} [config.ttl] - New TTL (e.g., '7200s')
699
+ * @param {string} [config.expireTime] - New expiration (RFC 3339)
700
+ * @returns {Promise<Object>} The updated cache resource
701
+ */
702
+ async updateCache(cacheName, config = {}) {
703
+ return await this.genAIClient.caches.update({
704
+ name: cacheName,
705
+ config: {
706
+ ...config.ttl && { ttl: config.ttl },
707
+ ...config.expireTime && { expireTime: config.expireTime }
708
+ }
709
+ });
710
+ }
711
+ /**
712
+ * Deletes a cached content resource.
713
+ * Clears this.cachedContent if it matches the deleted cache.
714
+ * @param {string} cacheName - Server-generated resource name
715
+ * @returns {Promise<void>}
716
+ */
717
+ async deleteCache(cacheName) {
718
+ await this.genAIClient.caches.delete({ name: cacheName });
719
+ logger_default.debug(`Cache deleted: ${cacheName}`);
720
+ if (this.cachedContent === cacheName) {
721
+ this.cachedContent = null;
722
+ }
723
+ }
724
+ /**
725
+ * Sets the cached content for this instance and reinitializes the session.
726
+ * @param {string} cacheName - Server-generated cache resource name
727
+ * @returns {Promise<void>}
728
+ */
729
+ async useCache(cacheName) {
730
+ this.cachedContent = cacheName;
731
+ delete this.chatConfig.systemInstruction;
732
+ if (this.chatSession) {
733
+ await this.init(true);
734
+ }
735
+ logger_default.debug(`Using cache: ${cacheName}`);
736
+ }
630
737
  // ── Private Helpers ──────────────────────────────────────────────────────
631
738
  /**
632
739
  * Configures the log level based on options, env vars, or NODE_ENV.
@@ -722,20 +829,8 @@ var Transformer = class extends base_default {
722
829
  this.asyncValidator = options.asyncValidator || null;
723
830
  this.maxRetries = options.maxRetries || 3;
724
831
  this.retryDelay = options.retryDelay || 1e3;
725
- this.enableGrounding = options.enableGrounding || false;
726
- this.groundingConfig = options.groundingConfig || {};
727
832
  logger_default.debug(`Transformer keys \u2014 Source: "${this.promptKey}", Target: "${this.answerKey}", Context: "${this.contextKey}"`);
728
833
  }
729
- // ── Chat Create Options Override ──────────────────────────────────────────
730
- /** @protected */
731
- _getChatCreateOptions() {
732
- const opts = super._getChatCreateOptions();
733
- if (this.enableGrounding) {
734
- opts.config.tools = [{ googleSearch: this.groundingConfig }];
735
- logger_default.debug(`Search grounding ENABLED (WARNING: costs $35/1k queries)`);
736
- }
737
- return opts;
738
- }
739
834
  // ── Seeding ──────────────────────────────────────────────────────────────
740
835
  /**
741
836
  * Seeds the chat with transformation examples using the configured key mapping.
@@ -2221,14 +2316,152 @@ ${serialized}` });
2221
2316
  };
2222
2317
  var rag_agent_default = RagAgent;
2223
2318
 
2319
+ // embedding.js
2320
+ var Embedding = class extends base_default {
2321
+ /**
2322
+ * @param {import('./types.d.ts').EmbeddingOptions} [options={}]
2323
+ */
2324
+ constructor(options = {}) {
2325
+ if (options.modelName === void 0) {
2326
+ options = { ...options, modelName: "gemini-embedding-001" };
2327
+ }
2328
+ if (options.systemPrompt === void 0) {
2329
+ options = { ...options, systemPrompt: null };
2330
+ }
2331
+ super(options);
2332
+ this.taskType = options.taskType || null;
2333
+ this.title = options.title || null;
2334
+ this.outputDimensionality = options.outputDimensionality || null;
2335
+ this.autoTruncate = options.autoTruncate ?? true;
2336
+ logger_default.debug(`Embedding created with model: ${this.modelName}`);
2337
+ }
2338
+ /**
2339
+ * Initialize the Embedding client.
2340
+ * Override: validates API connection only, NO chat session (stateless).
2341
+ * @param {boolean} [force=false]
2342
+ * @returns {Promise<void>}
2343
+ */
2344
+ async init(force = false) {
2345
+ if (this._initialized && !force) return;
2346
+ logger_default.debug(`Initializing ${this.constructor.name} with model: ${this.modelName}...`);
2347
+ try {
2348
+ await this.genAIClient.models.list();
2349
+ logger_default.debug(`${this.constructor.name}: API connection successful.`);
2350
+ } catch (e) {
2351
+ throw new Error(`${this.constructor.name} initialization failed: ${e.message}`);
2352
+ }
2353
+ this._initialized = true;
2354
+ logger_default.debug(`${this.constructor.name}: Initialized (stateless mode).`);
2355
+ }
2356
+ /**
2357
+ * Builds the config object for embedContent calls.
2358
+ * @param {Object} [overrides={}] - Per-call config overrides
2359
+ * @returns {Object} The config object
2360
+ * @private
2361
+ */
2362
+ _buildConfig(overrides = {}) {
2363
+ const config = {};
2364
+ const taskType = overrides.taskType || this.taskType;
2365
+ const title = overrides.title || this.title;
2366
+ const dims = overrides.outputDimensionality || this.outputDimensionality;
2367
+ if (taskType) config.taskType = taskType;
2368
+ if (title) config.title = title;
2369
+ if (dims) config.outputDimensionality = dims;
2370
+ return config;
2371
+ }
2372
+ /**
2373
+ * Embed a single text string.
2374
+ * @param {string} text - The text to embed
2375
+ * @param {Object} [config={}] - Per-call config overrides
2376
+ * @param {string} [config.taskType] - Override task type
2377
+ * @param {string} [config.title] - Override title
2378
+ * @param {number} [config.outputDimensionality] - Override dimensions
2379
+
2380
+ * @returns {Promise<import('./types.d.ts').EmbeddingResult>} The embedding result
2381
+ */
2382
+ async embed(text, config = {}) {
2383
+ if (!this._initialized) await this.init();
2384
+ const result = await this.genAIClient.models.embedContent({
2385
+ model: this.modelName,
2386
+ contents: text,
2387
+ config: this._buildConfig(config)
2388
+ });
2389
+ return result.embeddings[0];
2390
+ }
2391
+ /**
2392
+ * Embed multiple text strings in a single API call.
2393
+ * @param {string[]} texts - Array of texts to embed
2394
+ * @param {Object} [config={}] - Per-call config overrides
2395
+ * @param {string} [config.taskType] - Override task type
2396
+ * @param {string} [config.title] - Override title
2397
+ * @param {number} [config.outputDimensionality] - Override dimensions
2398
+
2399
+ * @returns {Promise<import('./types.d.ts').EmbeddingResult[]>} Array of embedding results
2400
+ */
2401
+ async embedBatch(texts, config = {}) {
2402
+ if (!this._initialized) await this.init();
2403
+ const result = await this.genAIClient.models.embedContent({
2404
+ model: this.modelName,
2405
+ contents: texts,
2406
+ config: this._buildConfig(config)
2407
+ });
2408
+ return result.embeddings;
2409
+ }
2410
+ /**
2411
+ * Compute cosine similarity between two embedding vectors.
2412
+ * Pure math — no API call.
2413
+ * @param {number[]} a - First embedding vector
2414
+ * @param {number[]} b - Second embedding vector
2415
+ * @returns {number} Cosine similarity between -1 and 1
2416
+ */
2417
+ similarity(a, b) {
2418
+ if (!a || !b || a.length !== b.length) {
2419
+ throw new Error("Vectors must be non-null and have the same length");
2420
+ }
2421
+ let dot = 0;
2422
+ let magA = 0;
2423
+ let magB = 0;
2424
+ for (let i = 0; i < a.length; i++) {
2425
+ dot += a[i] * b[i];
2426
+ magA += a[i] * a[i];
2427
+ magB += b[i] * b[i];
2428
+ }
2429
+ const magnitude = Math.sqrt(magA) * Math.sqrt(magB);
2430
+ if (magnitude === 0) return 0;
2431
+ return dot / magnitude;
2432
+ }
2433
+ // ── No-ops (embeddings don't use chat sessions) ──
2434
+ /** @returns {any[]} Always returns empty array */
2435
+ getHistory() {
2436
+ return [];
2437
+ }
2438
+ /** No-op for Embedding */
2439
+ async clearHistory() {
2440
+ }
2441
+ /** No-op for Embedding */
2442
+ async seed() {
2443
+ logger_default.warn("Embedding.seed() is a no-op \u2014 embeddings do not support few-shot examples.");
2444
+ return [];
2445
+ }
2446
+ /**
2447
+ * @param {any} _nextPayload
2448
+ * @throws {Error} Embedding does not support token estimation
2449
+ * @returns {Promise<{inputTokens: number}>}
2450
+ */
2451
+ async estimate(_nextPayload) {
2452
+ throw new Error("Embedding does not support token estimation. Use embed() directly.");
2453
+ }
2454
+ };
2455
+
2224
2456
  // index.js
2225
2457
  var import_genai2 = require("@google/genai");
2226
- var index_default = { Transformer: transformer_default, Chat: chat_default, Message: message_default, ToolAgent: tool_agent_default, CodeAgent: code_agent_default, RagAgent: rag_agent_default };
2458
+ var index_default = { Transformer: transformer_default, Chat: chat_default, Message: message_default, ToolAgent: tool_agent_default, CodeAgent: code_agent_default, RagAgent: rag_agent_default, Embedding };
2227
2459
  // Annotate the CommonJS export names for ESM import in node:
2228
2460
  0 && (module.exports = {
2229
2461
  BaseGemini,
2230
2462
  Chat,
2231
2463
  CodeAgent,
2464
+ Embedding,
2232
2465
  HarmBlockThreshold,
2233
2466
  HarmCategory,
2234
2467
  Message,