ak-gemini 1.0.53 → 1.0.55

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.
Files changed (4) hide show
  1. package/index.cjs +29 -5
  2. package/index.js +43 -8
  3. package/package.json +15 -4
  4. package/types.d.ts +83 -0
package/index.cjs CHANGED
@@ -56,10 +56,14 @@ var logger_default = logger;
56
56
  // index.js
57
57
  var import_meta = {};
58
58
  import_dotenv.default.config();
59
- var { NODE_ENV = "unknown", GEMINI_API_KEY } = process.env;
59
+ var { NODE_ENV = "unknown", GEMINI_API_KEY, LOG_LEVEL = "" } = process.env;
60
60
  if (NODE_ENV === "dev") logger_default.level = "debug";
61
61
  if (NODE_ENV === "test") logger_default.level = "warn";
62
62
  if (NODE_ENV.startsWith("prod")) logger_default.level = "error";
63
+ if (LOG_LEVEL) {
64
+ logger_default.level = LOG_LEVEL;
65
+ logger_default.debug(`Setting log level to ${LOG_LEVEL}`);
66
+ }
63
67
  var DEFAULT_SAFETY_SETTINGS = [
64
68
  { category: import_genai.HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: import_genai.HarmBlockThreshold.BLOCK_NONE },
65
69
  { category: import_genai.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: import_genai.HarmBlockThreshold.BLOCK_NONE }
@@ -161,6 +165,12 @@ async function initChat(force = false) {
161
165
  config: this.chatConfig,
162
166
  history: []
163
167
  });
168
+ try {
169
+ await this.genAIClient.models.list();
170
+ logger_default.debug("Gemini API connection successful.");
171
+ } catch (e) {
172
+ throw new Error(`Gemini chat initialization failed: ${e.message}`);
173
+ }
164
174
  logger_default.debug("Gemini chat session initialized.");
165
175
  }
166
176
  async function seedWithExamples(examples) {
@@ -173,14 +183,22 @@ async function seedWithExamples(examples) {
173
183
  } catch (err) {
174
184
  throw new Error(`Could not load examples from file: ${this.examplesFile}. Please check the file path and format.`);
175
185
  }
186
+ } else if (this.exampleData) {
187
+ logger_default.debug(`Using example data provided in options.`);
188
+ if (Array.isArray(this.exampleData)) {
189
+ examples = this.exampleData;
190
+ } else {
191
+ throw new Error(`Invalid example data provided. Expected an array of examples.`);
192
+ }
176
193
  } else {
177
194
  logger_default.debug("No examples provided and no examples file specified. Skipping seeding.");
178
195
  return;
179
196
  }
180
197
  }
181
- if (examples?.slice().pop()[this.systemInstructionsKey]) {
198
+ const instructionExample = examples.find((ex) => ex[this.systemInstructionsKey]);
199
+ if (instructionExample) {
182
200
  logger_default.debug(`Found system instructions in examples; reinitializing chat with new instructions.`);
183
- this.systemInstructions = examples.slice().pop()[this.systemInstructionsKey];
201
+ this.systemInstructions = instructionExample[this.systemInstructionsKey];
184
202
  this.chatConfig.systemInstruction = this.systemInstructions;
185
203
  await this.init(true);
186
204
  }
@@ -213,14 +231,16 @@ ${contextText}
213
231
  }
214
232
  }
215
233
  const currentHistory = this?.chat?.getHistory() || [];
234
+ logger_default.debug(`Adding ${historyToAdd.length} examples to chat history (${currentHistory.length} current examples)...`);
216
235
  this.chat = await this.genAIClient.chats.create({
217
236
  model: this.modelName,
218
237
  // @ts-ignore
219
238
  config: this.chatConfig,
220
239
  history: [...currentHistory, ...historyToAdd]
221
240
  });
222
- logger_default.debug("Transformation examples seeded successfully.");
223
- return this.chat.getHistory();
241
+ const newHistory = this.chat.getHistory();
242
+ logger_default.debug(`Created new chat session with ${newHistory.length} examples.`);
243
+ return newHistory;
224
244
  }
225
245
  async function rawMessage(sourcePayload) {
226
246
  if (!this.chat) {
@@ -254,6 +274,10 @@ async function prepareAndValidateMessage(sourcePayload, options = {}, validatorF
254
274
  lastPayload = JSON.stringify(sourcePayload, null, 2);
255
275
  } else if (typeof sourcePayload === "string") {
256
276
  lastPayload = sourcePayload;
277
+ } else if (typeof sourcePayload === "boolean" || typeof sourcePayload === "number") {
278
+ lastPayload = sourcePayload.toString();
279
+ } else if (sourcePayload === null || sourcePayload === void 0) {
280
+ lastPayload = JSON.stringify({});
257
281
  } else {
258
282
  throw new Error("Invalid source payload. Must be a JSON object or string.");
259
283
  }
package/index.js CHANGED
@@ -18,7 +18,7 @@
18
18
  //env
19
19
  import dotenv from 'dotenv';
20
20
  dotenv.config();
21
- const { NODE_ENV = "unknown", GEMINI_API_KEY } = process.env;
21
+ const { NODE_ENV = "unknown", GEMINI_API_KEY, LOG_LEVEL = "" } = process.env;
22
22
 
23
23
 
24
24
 
@@ -33,6 +33,11 @@ if (NODE_ENV === 'dev') log.level = 'debug';
33
33
  if (NODE_ENV === 'test') log.level = 'warn';
34
34
  if (NODE_ENV.startsWith('prod')) log.level = 'error';
35
35
 
36
+ if (LOG_LEVEL) {
37
+ log.level = LOG_LEVEL;
38
+ log.debug(`Setting log level to ${LOG_LEVEL}`);
39
+ }
40
+
36
41
 
37
42
 
38
43
  // defaults
@@ -196,6 +201,15 @@ async function initChat(force = false) {
196
201
  history: [],
197
202
  });
198
203
 
204
+ try {
205
+ await this.genAIClient.models.list();
206
+ log.debug("Gemini API connection successful.");
207
+ } catch (e) {
208
+ throw new Error(`Gemini chat initialization failed: ${e.message}`);
209
+ }
210
+
211
+
212
+
199
213
  log.debug("Gemini chat session initialized.");
200
214
  }
201
215
 
@@ -218,15 +232,27 @@ async function seedWithExamples(examples) {
218
232
  catch (err) {
219
233
  throw new Error(`Could not load examples from file: ${this.examplesFile}. Please check the file path and format.`);
220
234
  }
221
- } else {
235
+ }
236
+
237
+ else if (this.exampleData) {
238
+ log.debug(`Using example data provided in options.`);
239
+ if (Array.isArray(this.exampleData)) {
240
+ examples = this.exampleData;
241
+ } else {
242
+ throw new Error(`Invalid example data provided. Expected an array of examples.`);
243
+ }
244
+ }
245
+
246
+ else {
222
247
  log.debug("No examples provided and no examples file specified. Skipping seeding.");
223
248
  return;
224
249
  }
225
250
  }
226
251
 
227
- if (examples?.slice().pop()[this.systemInstructionsKey]) {
252
+ const instructionExample = examples.find(ex => ex[this.systemInstructionsKey]);
253
+ if (instructionExample) {
228
254
  log.debug(`Found system instructions in examples; reinitializing chat with new instructions.`);
229
- this.systemInstructions = examples.slice().pop()[this.systemInstructionsKey];
255
+ this.systemInstructions = instructionExample[this.systemInstructionsKey];
230
256
  this.chatConfig.systemInstruction = this.systemInstructions;
231
257
  await this.init(true); // Reinitialize chat with new system instructions
232
258
  }
@@ -266,8 +292,9 @@ async function seedWithExamples(examples) {
266
292
 
267
293
  }
268
294
 
269
- const currentHistory = this?.chat?.getHistory() || [];
270
295
 
296
+ const currentHistory = this?.chat?.getHistory() || [];
297
+ log.debug(`Adding ${historyToAdd.length} examples to chat history (${currentHistory.length} current examples)...`);
271
298
  this.chat = await this.genAIClient.chats.create({
272
299
  model: this.modelName,
273
300
  // @ts-ignore
@@ -275,9 +302,10 @@ async function seedWithExamples(examples) {
275
302
  history: [...currentHistory, ...historyToAdd],
276
303
  });
277
304
 
278
- log.debug("Transformation examples seeded successfully.");
279
305
 
280
- return this.chat.getHistory(); // Return the updated chat history for reference
306
+ const newHistory = this.chat.getHistory();
307
+ log.debug(`Created new chat session with ${newHistory.length} examples.`);
308
+ return newHistory;
281
309
  }
282
310
 
283
311
  /**
@@ -345,7 +373,14 @@ async function prepareAndValidateMessage(sourcePayload, options = {}, validatorF
345
373
  lastPayload = JSON.stringify(sourcePayload, null, 2);
346
374
  } else if (typeof sourcePayload === 'string') {
347
375
  lastPayload = sourcePayload;
348
- } else {
376
+ }
377
+ else if (typeof sourcePayload === 'boolean' || typeof sourcePayload === 'number') {
378
+ lastPayload = sourcePayload.toString();
379
+ }
380
+ else if (sourcePayload === null || sourcePayload === undefined) {
381
+ lastPayload = JSON.stringify({}); // Convert null/undefined to empty object
382
+ }
383
+ else {
349
384
  throw new Error("Invalid source payload. Must be a JSON object or string.");
350
385
  }
351
386
 
package/package.json CHANGED
@@ -2,19 +2,25 @@
2
2
  "name": "ak-gemini",
3
3
  "author": "ak@mixpanel.com",
4
4
  "description": "AK's Generative AI Helper for doing... transforms",
5
- "version": "1.0.53",
5
+ "version": "1.0.55",
6
6
  "main": "index.js",
7
7
  "files": [
8
8
  "index.js",
9
9
  "index.cjs",
10
- "types.ts",
10
+ "types.d.ts",
11
11
  "logger.js"
12
12
  ],
13
13
  "types": "types.d.ts",
14
14
  "exports": {
15
15
  ".": {
16
- "import": "./index.js",
17
- "require": "./index.cjs"
16
+ "import": {
17
+ "types": "./types.d.ts",
18
+ "default": "./index.js"
19
+ },
20
+ "require": {
21
+ "types": "./types.d.ts",
22
+ "default": "./index.cjs"
23
+ }
18
24
  }
19
25
  },
20
26
  "repository": {
@@ -36,6 +42,11 @@
36
42
  "update-deps": "npx npm-check-updates -u && npm install",
37
43
  "prune": "rm -rf tmp/*",
38
44
  "test": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js",
45
+ "test:all": "./scripts/test-all.sh",
46
+ "test:integration": "./scripts/test-local.sh",
47
+ "test:unit": "npm test -- tests/module.test.js",
48
+ "test:function": "npm test -- tests/function.integration.test.js",
49
+ "test:http": "npm test -- tests/function.http.test.js",
39
50
  "build:cjs": "esbuild index.js --bundle --platform=node --format=cjs --outfile=index.cjs --external:@google/genai --external:ak-tools --external:dotenv --external:pino-pretty --external:pino"
40
51
  },
41
52
  "type": "module",
package/types.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ import type { GoogleGenAI } from '@google/genai';
2
+
3
+ export interface SafetySetting {
4
+ category: string; // The harm category
5
+ threshold: string; // The blocking threshold
6
+ }
7
+
8
+ export interface ChatConfig {
9
+ responseMimeType?: string; // MIME type for responses
10
+ temperature?: number; // Controls randomness (0.0 to 1.0)
11
+ topP?: number; // Controls diversity via nucleus sampling
12
+ topK?: number; // Controls diversity by limiting top-k tokens
13
+ systemInstruction?: string; // System instruction for the model
14
+ safetySettings?: SafetySetting[]; // Safety settings array
15
+ responseSchema?: Object; // Schema for validating model responses
16
+ [key: string]: any; // Additional properties for flexibility
17
+ }
18
+
19
+ export interface AITransformerContext {
20
+ modelName?: string;
21
+ systemInstructions?: string;
22
+ chatConfig?: ChatConfig;
23
+ genAI?: any;
24
+ chat?: any;
25
+ examplesFile?: string | null;
26
+ exampleData?: TransformationExample[] | null;
27
+ promptKey?: string;
28
+ answerKey?: string;
29
+ contextKey?: string;
30
+ explanationKey?: string;
31
+ systemInstructionsKey?: string;
32
+ maxRetries?: number;
33
+ retryDelay?: number;
34
+ init?: () => Promise<void>; // Initialization function
35
+ seed?: () => Promise<void>; // Function to seed the transformer with examples
36
+ message?: (payload: Record<string, unknown>) => Promise<Record<string, unknown>>; // Function to send messages to the model
37
+ rebuild?: () => Promise<Record<string, unknown>; // Function to rebuild the transformer
38
+ rawMessage?: (payload: Record<string, unknown>) => Promise<Record<string, unknown>>; // Function to send raw messages to the model
39
+ genAIClient?: GoogleGenAI; // Google GenAI client instance
40
+ onlyJSON?: boolean; // If true, only JSON responses are allowed
41
+
42
+ }
43
+
44
+ export interface TransformationExample {
45
+ CONTEXT?: Record<string, unknown>; // optional context for the transformation
46
+ PROMPT?: Record<string, unknown>; // what the user provides as input
47
+ ANSWER?: Record<string, unknown>; // what the model should return as output
48
+ }
49
+
50
+ export interface ExampleFileContent {
51
+ examples: TransformationExample[];
52
+ }
53
+
54
+ export interface AITransformerOptions {
55
+ // ? https://ai.google.dev/gemini-api/docs/models
56
+ modelName?: string; // The Gemini model to use
57
+ systemInstructions?: string; // Custom system instructions for the model
58
+ chatConfig?: ChatConfig; // Configuration object for the chat session
59
+ examplesFile?: string; // Path to JSON file containing transformation examples
60
+ exampleData?: TransformationExample[]; // Inline examples to seed the transformer
61
+ sourceKey?: string; // Key name for source data in examples
62
+ targetKey?: string; // Key name for target data in examples
63
+ contextKey?: string; // Key name for context data in examples
64
+ explanationKey?: string; // Key name for explanation data in examples
65
+ systemInstructionsKey?: string; // Key for system instructions in examples
66
+ maxRetries?: number; // Maximum retry attempts for auto-retry functionality
67
+ retryDelay?: number; // Initial retry delay in milliseconds
68
+ // ? https://ai.google.dev/gemini-api/docs/structured-output
69
+ responseSchema?: Object; // Schema for validating model responses
70
+ apiKey?: string; // API key for Google GenAI
71
+ onlyJSON?: boolean; // If true, only JSON responses are allowed
72
+ asyncValidator?: AsyncValidatorFunction; // Optional async validator function for response validation
73
+ promptKey?: string; // Key for the prompt in examples
74
+ answerKey?: string; // Key for the answer in examples
75
+ contextKey?: string; // Key for the context in examples
76
+ explanationKey?: string; // Key for the explanation in examples
77
+ }
78
+
79
+ // Async validator function type
80
+ export type AsyncValidatorFunction = (payload: Record<string, unknown>) => Promise<unknown>;
81
+
82
+
83
+ export declare class AITransformer implements AITransformerContext {}