viho-llm 0.1.5 → 0.1.6

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,10 @@
1
- # viho-llm
1
+ <p align="center">
2
+ <img src="https://static-small.vincentqiao.com/viho/logo.png" alt="viho logo" width="200"/>
3
+ </p>
2
4
 
3
- Utility library for working with Google Gemini AI, providing common tools and helpers for AI interactions.
5
+ <h1 align="center">viho-llm</h1>
6
+
7
+ <p align="center">Utility library for working with Google Gemini AI, providing common tools and helpers for AI interactions.</p>
4
8
 
5
9
  ## Installation
6
10
 
@@ -10,17 +14,26 @@ npm install viho-llm
10
14
 
11
15
  ## Prerequisites
12
16
 
13
- You need to have a Google AI API key. Get one from [Google AI Studio](https://makersuite.google.com/app/apikey).
17
+ This library supports two ways to access Google Gemini AI:
18
+
19
+ 1. **Google AI Studio (GeminiAPI)** - For personal development and prototyping
20
+ - Get an API key from [Google AI Studio](https://makersuite.google.com/app/apikey)
21
+
22
+ 2. **Vertex AI (GeminiVertex)** - For enterprise applications with advanced features
23
+ - Requires a Google Cloud project with Vertex AI enabled
24
+ - Supports context caching for cost optimization
14
25
 
15
26
  ## Usage
16
27
 
17
- ### Basic Example
28
+ ### Basic Example with GeminiAPI
29
+
30
+ Using Google AI Studio API Key (recommended for development):
18
31
 
19
32
  ```javascript
20
- import { Gemini } from 'viho-llm';
33
+ import { GeminiAPI } from 'viho-llm';
21
34
 
22
- // Initialize Gemini client
23
- const gemini = Gemini({
35
+ // Initialize Gemini client with API Key
36
+ const gemini = GeminiAPI({
24
37
  apiKey: 'your-google-api-key',
25
38
  modelName: 'gemini-pro',
26
39
  });
@@ -38,17 +51,38 @@ const response = await gemini.chat({
38
51
  console.log(response);
39
52
  ```
40
53
 
41
- ### Streaming Example
54
+ ### Basic Example with GeminiVertex
55
+
56
+ Using Vertex AI (recommended for production):
42
57
 
43
58
  ```javascript
44
- import { Gemini } from 'viho-llm';
59
+ import { GeminiVertex } from 'viho-llm';
45
60
 
46
- // Initialize Gemini client
47
- const gemini = Gemini({
48
- apiKey: 'your-google-api-key',
61
+ // Initialize Gemini client with Vertex AI
62
+ const gemini = GeminiVertex({
63
+ projectId: 'your-gcp-project-id',
64
+ location: 'us-east1',
49
65
  modelName: 'gemini-pro',
50
66
  });
51
67
 
68
+ // Send a chat message
69
+ const response = await gemini.chat({
70
+ contents: [
71
+ {
72
+ role: 'user',
73
+ parts: [{ text: 'Hello, how are you?' }],
74
+ },
75
+ ],
76
+ });
77
+
78
+ console.log(response);
79
+ ```
80
+
81
+ ### Streaming Example
82
+
83
+ Both GeminiAPI and GeminiVertex support streaming responses:
84
+
85
+ ```javascript
52
86
  // Send a chat message with streaming
53
87
  await gemini.chatWithStreaming(
54
88
  {
@@ -79,11 +113,44 @@ await gemini.chatWithStreaming(
79
113
  );
80
114
  ```
81
115
 
116
+ ### Context Caching Example (Vertex AI Only)
117
+
118
+ GeminiVertex supports context caching to reduce costs and latency when using large contexts:
119
+
120
+ ```javascript
121
+ import { GeminiVertex } from 'viho-llm';
122
+
123
+ const gemini = GeminiVertex({
124
+ projectId: 'your-gcp-project-id',
125
+ location: 'us-east1',
126
+ modelName: 'gemini-1.5-flash-002',
127
+ });
128
+
129
+ // Add a new cache
130
+ const cache = await gemini.cacheAdd({
131
+ gsPath: 'gs://your-bucket/large-document.pdf',
132
+ systemPrompt: 'You are an expert at analyzing technical documents.',
133
+ cacheName: 'my-document-cache',
134
+ cacheTTL: '3600s', // 1 hour
135
+ });
136
+
137
+ console.log('Cache created:', cache.name);
138
+
139
+ // List all caches
140
+ const caches = await gemini.cacheList();
141
+ console.log('Available caches:', caches);
142
+
143
+ // Update cache TTL
144
+ await gemini.cacheUpdate(cache.name, {
145
+ ttl: '7200s', // Extend to 2 hours
146
+ });
147
+ ```
148
+
82
149
  ## API Reference
83
150
 
84
- ### `Gemini(options)`
151
+ ### `GeminiAPI(options)`
85
152
 
86
- Creates a new Gemini client instance.
153
+ Creates a new Gemini client instance using Google AI Studio API.
87
154
 
88
155
  #### Parameters
89
156
 
@@ -172,6 +239,98 @@ await gemini.chatWithStreaming(
172
239
  );
173
240
  ```
174
241
 
242
+ ---
243
+
244
+ ### `GeminiVertex(options)`
245
+
246
+ Creates a new Gemini client instance using Vertex AI. Includes all features of GeminiAPI plus context caching support.
247
+
248
+ #### Parameters
249
+
250
+ - `options` (Object) - Configuration options
251
+ - `projectId` (string) **required** - Your Google Cloud project ID
252
+ - `location` (string) **required** - GCP region (e.g., 'us-east1', 'us-central1')
253
+ - `modelName` (string) **required** - Model name (e.g., 'gemini-1.5-flash-002', 'gemini-1.5-pro-002')
254
+
255
+ #### Returns
256
+
257
+ Returns a Gemini client object with the following methods:
258
+
259
+ ##### `client.chat(chatOptions)`
260
+
261
+ Same as GeminiAPI.chat(). See above for details.
262
+
263
+ ##### `client.chatWithStreaming(chatOptions, callbackOptions)`
264
+
265
+ Same as GeminiAPI.chatWithStreaming(). See above for details.
266
+
267
+ ##### `client.cacheAdd(cacheOptions)`
268
+
269
+ Creates a new context cache for frequently used content.
270
+
271
+ **Parameters:**
272
+
273
+ - `cacheOptions` (Object)
274
+ - `gsPath` (string) **required** - Google Cloud Storage path (e.g., 'gs://bucket/file.pdf')
275
+ - `systemPrompt` (string) **required** - System instruction for the cached context
276
+ - `cacheName` (string) **required** - Display name for the cache
277
+ - `cacheTTL` (string) **required** - Time-to-live (e.g., '3600s' for 1 hour)
278
+
279
+ **Returns:**
280
+
281
+ - (Promise\<Object\>) - Cache object with name and metadata
282
+
283
+ **Example:**
284
+
285
+ ```javascript
286
+ const cache = await gemini.cacheAdd({
287
+ gsPath: 'gs://my-bucket/documentation.pdf',
288
+ systemPrompt: 'You are a helpful documentation assistant.',
289
+ cacheName: 'docs-cache',
290
+ cacheTTL: '3600s',
291
+ });
292
+ ```
293
+
294
+ ##### `client.cacheList()`
295
+
296
+ Lists all available caches in the project.
297
+
298
+ **Parameters:** None
299
+
300
+ **Returns:**
301
+
302
+ - (Promise\<Array\>) - Array of cache objects with `name` and `displayName` properties
303
+
304
+ **Example:**
305
+
306
+ ```javascript
307
+ const caches = await gemini.cacheList();
308
+ console.log(caches);
309
+ // [{ name: 'projects/.../cachedContents/...', displayName: 'docs-cache' }]
310
+ ```
311
+
312
+ ##### `client.cacheUpdate(cacheName, cacheOptions)`
313
+
314
+ Updates an existing cache configuration.
315
+
316
+ **Parameters:**
317
+
318
+ - `cacheName` (string) **required** - The cache name to update
319
+ - `cacheOptions` (Object) **required** - Update configuration
320
+ - `ttl` (string) - New time-to-live value (e.g., '7200s')
321
+
322
+ **Returns:**
323
+
324
+ - (Promise\<Object\>) - Updated cache object
325
+
326
+ **Example:**
327
+
328
+ ```javascript
329
+ await gemini.cacheUpdate('projects/.../cachedContents/abc123', {
330
+ ttl: '7200s', // Extend to 2 hours
331
+ });
332
+ ```
333
+
175
334
  ## License
176
335
 
177
336
  MIT
package/index.js CHANGED
@@ -36,10 +36,16 @@ const chat = async (client, modelName, chatOptions) => {
36
36
  }
37
37
 
38
38
  try {
39
- const response = await client.models.generateContent({
40
- model: modelName,
41
- contents: chatOptions.contents,
42
- });
39
+ // options
40
+ const options = Object.assign(
41
+ {
42
+ model: modelName,
43
+ },
44
+ chatOptions,
45
+ );
46
+
47
+ // gen
48
+ const response = await client.models.generateContent(options);
43
49
  if (!response || !response.text) {
44
50
  logger$2.error(methodName, 'invalid response');
45
51
  return;
@@ -92,11 +98,19 @@ const chatWithStreaming = async (client, modelName, chatOptions, callbackOptions
92
98
  const firstContentCallback = callbackOptions.firstContentCallback;
93
99
 
94
100
  try {
101
+ // begin
95
102
  if (beginCallback) beginCallback();
96
- const response = await client.models.generateContentStream({
97
- model: modelName,
98
- contents: chatOptions.contents,
99
- });
103
+
104
+ // options
105
+ const options = Object.assign(
106
+ {
107
+ model: modelName,
108
+ },
109
+ chatOptions,
110
+ );
111
+
112
+ // gen
113
+ const response = await client.models.generateContentStream(options);
100
114
 
101
115
  // go
102
116
  let firstContent = true;
@@ -131,6 +145,14 @@ const cacheAdd = async (client, modelName, cacheOptions) => {
131
145
  const methodName = 'cacheAdd';
132
146
 
133
147
  // check
148
+ if (!client) {
149
+ logger$2.error(methodName, 'need client');
150
+ return;
151
+ }
152
+ if (!modelName) {
153
+ logger$2.error(methodName, 'need modelName');
154
+ return;
155
+ }
134
156
  if (!cacheOptions) {
135
157
  logger$2.error(methodName, 'need cacheOptions');
136
158
  return;
@@ -175,6 +197,71 @@ const cacheAdd = async (client, modelName, cacheOptions) => {
175
197
  }
176
198
  };
177
199
 
200
+ /**
201
+ * cacheList
202
+ * @param {*} client
203
+ * @returns
204
+ */
205
+ const cacheList = async (client) => {
206
+ const methodName = 'cacheList';
207
+
208
+ // check
209
+ if (!client) {
210
+ logger$2.error(methodName, 'need client');
211
+ return;
212
+ }
213
+
214
+ // cache list
215
+ try {
216
+ const cacheList = await client.caches.list();
217
+ const cacheObjs = cacheList?.pageInternal?.map((contentCache) => ({
218
+ name: contentCache.name,
219
+ displayName: contentCache.displayName,
220
+ }));
221
+
222
+ return cacheObjs;
223
+ } catch (error) {
224
+ logger$2.error(methodName, 'error', error);
225
+ }
226
+ };
227
+
228
+ /**
229
+ * cacheUpdate
230
+ * @param {*} client
231
+ * @param {*} cacheName
232
+ * @param {*} cacheOptions
233
+ * @returns
234
+ */
235
+ const cacheUpdate = async (client, cacheName, cacheOptions) => {
236
+ const methodName = 'cacheUpdate';
237
+
238
+ // check
239
+ if (!client) {
240
+ logger$2.error(methodName, 'need client');
241
+ return;
242
+ }
243
+ if (!cacheName) {
244
+ logger$2.error(methodName, 'need cacheName');
245
+ return;
246
+ }
247
+ if (!cacheOptions) {
248
+ logger$2.error(methodName, 'need cacheOptions');
249
+ return;
250
+ }
251
+
252
+ // cache update
253
+ try {
254
+ const res = await client.caches.update({
255
+ name: cacheName,
256
+ config: cacheOptions,
257
+ });
258
+
259
+ return res;
260
+ } catch (error) {
261
+ logger$2.error(methodName, 'error', error);
262
+ }
263
+ };
264
+
178
265
  // gemini
179
266
  const logger$1 = qiao_log_js.Logger('gemini-api.js');
180
267
 
@@ -267,6 +354,12 @@ const GeminiVertex = (options) => {
267
354
  gemini.cacheAdd = async (cacheOptions) => {
268
355
  return await cacheAdd(gemini.client, options.modelName, cacheOptions);
269
356
  };
357
+ gemini.cacheList = async () => {
358
+ return await cacheList(gemini.client);
359
+ };
360
+ gemini.cacheUpdate = async (cacheName, cacheOptions) => {
361
+ return await cacheUpdate(gemini.client, cacheName, cacheOptions);
362
+ };
270
363
 
271
364
  // r
272
365
  return gemini;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viho-llm",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Utility library for working with Google Gemini AI, providing common tools and helpers for AI interactions",
5
5
  "keywords": [
6
6
  "llm",
@@ -61,5 +61,5 @@
61
61
  }
62
62
  }
63
63
  },
64
- "gitHead": "b37b523239fe354f991017387e95a6314e994981"
64
+ "gitHead": "d77d2ba692eac3cd5262f55ade7cf74b84172385"
65
65
  }
@@ -37,10 +37,16 @@ export const chat = async (client, modelName, chatOptions) => {
37
37
  }
38
38
 
39
39
  try {
40
- const response = await client.models.generateContent({
41
- model: modelName,
42
- contents: chatOptions.contents,
43
- });
40
+ // options
41
+ const options = Object.assign(
42
+ {
43
+ model: modelName,
44
+ },
45
+ chatOptions,
46
+ );
47
+
48
+ // gen
49
+ const response = await client.models.generateContent(options);
44
50
  if (!response || !response.text) {
45
51
  logger.error(methodName, 'invalid response');
46
52
  return;
@@ -93,11 +99,19 @@ export const chatWithStreaming = async (client, modelName, chatOptions, callback
93
99
  const firstContentCallback = callbackOptions.firstContentCallback;
94
100
 
95
101
  try {
102
+ // begin
96
103
  if (beginCallback) beginCallback();
97
- const response = await client.models.generateContentStream({
98
- model: modelName,
99
- contents: chatOptions.contents,
100
- });
104
+
105
+ // options
106
+ const options = Object.assign(
107
+ {
108
+ model: modelName,
109
+ },
110
+ chatOptions,
111
+ );
112
+
113
+ // gen
114
+ const response = await client.models.generateContentStream(options);
101
115
 
102
116
  // go
103
117
  let firstContent = true;
@@ -132,6 +146,14 @@ export const cacheAdd = async (client, modelName, cacheOptions) => {
132
146
  const methodName = 'cacheAdd';
133
147
 
134
148
  // check
149
+ if (!client) {
150
+ logger.error(methodName, 'need client');
151
+ return;
152
+ }
153
+ if (!modelName) {
154
+ logger.error(methodName, 'need modelName');
155
+ return;
156
+ }
135
157
  if (!cacheOptions) {
136
158
  logger.error(methodName, 'need cacheOptions');
137
159
  return;
@@ -175,3 +197,68 @@ export const cacheAdd = async (client, modelName, cacheOptions) => {
175
197
  logger.error(methodName, 'error', error);
176
198
  }
177
199
  };
200
+
201
+ /**
202
+ * cacheList
203
+ * @param {*} client
204
+ * @returns
205
+ */
206
+ export const cacheList = async (client) => {
207
+ const methodName = 'cacheList';
208
+
209
+ // check
210
+ if (!client) {
211
+ logger.error(methodName, 'need client');
212
+ return;
213
+ }
214
+
215
+ // cache list
216
+ try {
217
+ const cacheList = await client.caches.list();
218
+ const cacheObjs = cacheList?.pageInternal?.map((contentCache) => ({
219
+ name: contentCache.name,
220
+ displayName: contentCache.displayName,
221
+ }));
222
+
223
+ return cacheObjs;
224
+ } catch (error) {
225
+ logger.error(methodName, 'error', error);
226
+ }
227
+ };
228
+
229
+ /**
230
+ * cacheUpdate
231
+ * @param {*} client
232
+ * @param {*} cacheName
233
+ * @param {*} cacheOptions
234
+ * @returns
235
+ */
236
+ export const cacheUpdate = async (client, cacheName, cacheOptions) => {
237
+ const methodName = 'cacheUpdate';
238
+
239
+ // check
240
+ if (!client) {
241
+ logger.error(methodName, 'need client');
242
+ return;
243
+ }
244
+ if (!cacheName) {
245
+ logger.error(methodName, 'need cacheName');
246
+ return;
247
+ }
248
+ if (!cacheOptions) {
249
+ logger.error(methodName, 'need cacheOptions');
250
+ return;
251
+ }
252
+
253
+ // cache update
254
+ try {
255
+ const res = await client.caches.update({
256
+ name: cacheName,
257
+ config: cacheOptions,
258
+ });
259
+
260
+ return res;
261
+ } catch (error) {
262
+ logger.error(methodName, 'error', error);
263
+ }
264
+ };
@@ -2,7 +2,7 @@
2
2
  import { GoogleGenAI } from '@google/genai';
3
3
 
4
4
  // util
5
- import { chat, chatWithStreaming, cacheAdd } from './gemini-util.js';
5
+ import { chat, chatWithStreaming, cacheAdd, cacheList, cacheUpdate } from './gemini-util.js';
6
6
 
7
7
  // Logger
8
8
  import { Logger } from 'qiao.log.js';
@@ -54,6 +54,12 @@ export const GeminiVertex = (options) => {
54
54
  gemini.cacheAdd = async (cacheOptions) => {
55
55
  return await cacheAdd(gemini.client, options.modelName, cacheOptions);
56
56
  };
57
+ gemini.cacheList = async () => {
58
+ return await cacheList(gemini.client);
59
+ };
60
+ gemini.cacheUpdate = async (cacheName, cacheOptions) => {
61
+ return await cacheUpdate(gemini.client, cacheName, cacheOptions);
62
+ };
57
63
 
58
64
  // r
59
65
  return gemini;