ak-gemini 1.0.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.
Files changed (6) hide show
  1. package/README.md +212 -0
  2. package/index.cjs +41668 -0
  3. package/index.js +451 -0
  4. package/logger.js +16 -0
  5. package/package.json +62 -0
  6. package/types.ts +65 -0
package/index.js ADDED
@@ -0,0 +1,451 @@
1
+ /**
2
+ * @fileoverview
3
+ * Generic AI transformation module that can be configured for different use cases.
4
+ * Supports various models, system instructions, chat configurations, and example datasets.
5
+ */
6
+
7
+ /**
8
+ * @typedef {import('./types').SafetySetting} SafetySetting
9
+ * @typedef {import('./types').ChatConfig} ChatConfig
10
+ * @typedef {import('./types').TransformationExample} TransformationExample
11
+ * @typedef {import('./types').ExampleFileContent} ExampleFileContent
12
+ * @typedef {import('./types').AITransformerOptions} AITransformerOptions
13
+ * @typedef {import('./types').AsyncValidatorFunction} AsyncValidatorFunction
14
+ * @typedef {import('./types').AITransformerContext} ExportedAPI
15
+ *
16
+ */
17
+
18
+ //env
19
+ import dotenv from 'dotenv';
20
+ dotenv.config();
21
+ const { NODE_ENV = "unknown", GEMINI_API_KEY } = process.env;
22
+
23
+
24
+
25
+ //deps
26
+ import { GoogleGenAI, HarmCategory, HarmBlockThreshold } from '@google/genai';
27
+ import u from 'ak-tools';
28
+ import path from 'path';
29
+ import log from './logger.js';
30
+
31
+
32
+
33
+
34
+ // defaults
35
+ const DEFAULT_SAFETY_SETTINGS = [
36
+ { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
37
+ { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE }
38
+ ];
39
+
40
+ const DEFAULT_SYSTEM_INSTRUCTIONS = `
41
+ You are an expert JSON transformation engine. Your task is to accurately convert data payloads from one format to another.
42
+
43
+ You will be provided with example transformations (Source JSON -> Target JSON).
44
+
45
+ Learn the mapping rules from these examples.
46
+
47
+ When presented with new Source JSON, apply the learned transformation rules to produce a new Target JSON payload.
48
+
49
+ Always respond ONLY with a valid JSON object that strictly adheres to the expected output format.
50
+
51
+ Do not include any additional text, explanations, or formatting before or after the JSON object.
52
+ `;
53
+
54
+ const DEFAULT_CHAT_CONFIG = {
55
+ responseMimeType: 'application/json',
56
+ temperature: 0.2,
57
+ topP: 0.95,
58
+ topK: 64,
59
+ systemInstruction: DEFAULT_SYSTEM_INSTRUCTIONS,
60
+ safetySettings: DEFAULT_SAFETY_SETTINGS
61
+ };
62
+
63
+ /**
64
+ * main export class for AI Transformer
65
+ * @class AITransformer
66
+ * @description A class that provides methods to initialize, seed, transform, and manage AI-based transformations using Google Gemini API.
67
+ * @implements {ExportedAPI}
68
+ */
69
+ // @ts-ignore
70
+ export default class AITransformer {
71
+ /**
72
+ * @param {AITransformerOptions} [options={}] - Configuration options for the transformer
73
+ *
74
+ */
75
+ constructor(options = {}) {
76
+ this.modelName = "";
77
+ this.promptKey = "";
78
+ this.answerKey = "";
79
+ this.contextKey = "";
80
+ this.maxRetries = 3;
81
+ this.retryDelay = 1000;
82
+ this.systemInstructions = "";
83
+ this.chatConfig = {};
84
+ this.apiKey = GEMINI_API_KEY;
85
+ AITransformFactory.call(this, options);
86
+
87
+ //external API
88
+ this.init = initChat.bind(this);
89
+ this.seed = seedWithExamples.bind(this);
90
+ this.message = transformJSON.bind(this);
91
+ this.rebuild = rebuildPayload.bind(this);
92
+ this.reset = resetChat.bind(this);
93
+ this.getHistory = getChatHistory.bind(this);
94
+ this.transformWithValidation = transformWithValidation.bind(this);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * factory function to create an AI Transformer instance
100
+ * @param {AITransformerOptions} [options={}] - Configuration options for the transformer
101
+ * @returns {void} - An instance of AITransformer with initialized properties and methods
102
+ */
103
+ function AITransformFactory(options = {}) {
104
+ // ? https://ai.google.dev/gemini-api/docs/models
105
+ this.modelName = options.modelName || 'gemini-2.0-flash';
106
+ this.systemInstructions = options.systemInstructions || DEFAULT_SYSTEM_INSTRUCTIONS;
107
+
108
+ this.apiKey = options.apiKey || GEMINI_API_KEY;
109
+ if (!this.apiKey) throw new Error("Missing Gemini API key. Provide via options.apiKey or GEMINI_API_KEY env var.");
110
+ // Build chat config, making sure systemInstruction uses the custom instructions
111
+ this.chatConfig = {
112
+ ...DEFAULT_CHAT_CONFIG,
113
+ ...options.chatConfig,
114
+ systemInstruction: this.systemInstructions
115
+ };
116
+
117
+ // response schema is optional, but if provided, it should be a valid JSON schema
118
+ if (options.responseSchema) {
119
+ this.chatConfig.responseSchema = options.responseSchema;
120
+ }
121
+
122
+ // examples file is optional, but if provided, it should contain valid PROMPT and ANSWER keys
123
+ this.examplesFile = options.examplesFile || null;
124
+ this.exampleData = options.exampleData || null; // can be used instead of examplesFile
125
+
126
+ // Use configurable keys with fallbacks
127
+ this.promptKey = options.sourceKey || 'PROMPT';
128
+ this.answerKey = options.targetKey || 'ANSWER';
129
+ this.contextKey = options.contextKey || 'CONTEXT'; // Now configurable
130
+
131
+ // Retry configuration
132
+ this.maxRetries = options.maxRetries || 3;
133
+ this.retryDelay = options.retryDelay || 1000;
134
+
135
+ if (this.promptKey === this.answerKey) {
136
+ throw new Error("Source and target keys cannot be the same. Please provide distinct keys.");
137
+ }
138
+
139
+ log.debug(`Creating AI Transformer with model: ${this.modelName}`);
140
+ log.debug(`Using keys - Source: "${this.promptKey}", Target: "${this.answerKey}", Context: "${this.contextKey}"`);
141
+
142
+ this.genAIClient = new GoogleGenAI({ apiKey: this.apiKey });
143
+ this.chat = null;
144
+ }
145
+
146
+ /**
147
+ * Initializes the chat session with the specified model and configurations.
148
+ * @this {ExportedAPI}
149
+ * @returns {Promise<void>}
150
+ */
151
+ async function initChat() {
152
+ if (this.chat) return;
153
+
154
+ log.debug(`Initializing Gemini chat session with model: ${this.modelName}...`);
155
+
156
+ this.chat = await this.genAIClient.chats.create({
157
+ model: this.modelName,
158
+ // @ts-ignore
159
+ config: this.chatConfig,
160
+ history: [],
161
+ });
162
+
163
+ log.debug("Gemini chat session initialized.");
164
+ }
165
+
166
+ /**
167
+ * Seeds the chat session with example transformations.
168
+ * @this {ExportedAPI}
169
+ * @param {TransformationExample[]} [examples] - An array of transformation examples.
170
+ * @returns {Promise<void>}
171
+ */
172
+ async function seedWithExamples(examples) {
173
+ await this.init();
174
+
175
+ if (!examples || !Array.isArray(examples) || examples.length === 0) {
176
+ if (this.examplesFile) {
177
+ log.debug(`No examples provided, loading from file: ${this.examplesFile}`);
178
+ examples = await u.load(path.resolve(this.examplesFile), true);
179
+ } else {
180
+ log.debug("No examples provided and no examples file specified. Skipping seeding.");
181
+ return;
182
+ }
183
+ }
184
+
185
+ log.debug(`Seeding chat with ${examples.length} transformation examples...`);
186
+ const historyToAdd = [];
187
+
188
+ for (const example of examples) {
189
+ // Use the configurable keys from constructor
190
+ const contextValue = example[this.contextKey] || "";
191
+ const promptValue = example[this.promptKey] || "";
192
+ const answerValue = example[this.answerKey] || "";
193
+
194
+ // Add context as user message with special formatting to make it part of the example flow
195
+ if (contextValue) {
196
+ let contextText = u.isJSON(contextValue) ? JSON.stringify(contextValue, null, 2) : contextValue;
197
+ // Prefix context to make it clear it's contextual information
198
+ historyToAdd.push({
199
+ role: 'user',
200
+ parts: [{ text: `Context: ${contextText}` }]
201
+ });
202
+ // Add a brief model acknowledgment
203
+ historyToAdd.push({
204
+ role: 'model',
205
+ parts: [{ text: "I understand the context." }]
206
+ });
207
+ }
208
+
209
+ if (promptValue) {
210
+ let promptText = u.isJSON(promptValue) ? JSON.stringify(promptValue, null, 2) : promptValue;
211
+ historyToAdd.push({ role: 'user', parts: [{ text: promptText }] });
212
+ }
213
+
214
+ if (answerValue) {
215
+ let answerText = u.isJSON(answerValue) ? JSON.stringify(answerValue, null, 2) : answerValue;
216
+ historyToAdd.push({ role: 'model', parts: [{ text: answerText }] });
217
+ }
218
+ }
219
+
220
+ const currentHistory = this.chat.getHistory();
221
+
222
+ this.chat = await this.genAIClient.chats.create({
223
+ model: this.modelName,
224
+ // @ts-ignore
225
+ config: this.chatConfig,
226
+ history: [...currentHistory, ...historyToAdd],
227
+ });
228
+
229
+ log.debug("Transformation examples seeded successfully.");
230
+ }
231
+
232
+ /**
233
+ * Transforms a source JSON payload into a target JSON payload
234
+ * @param {Object} sourcePayload - The source payload (as a JavaScript object).
235
+ * @returns {Promise<Object>} - The transformed target payload (as a JavaScript object).
236
+ * @throws {Error} If the transformation fails or returns invalid JSON.
237
+ */
238
+ async function transformJSON(sourcePayload) {
239
+ if (!this.chat) {
240
+ throw new Error("Chat session not initialized. Call initChat() or seedWithExamples() first.");
241
+ }
242
+
243
+ let result;
244
+ let actualPayload;
245
+ if (sourcePayload && u.isJSON(sourcePayload)) actualPayload = JSON.stringify(sourcePayload, null, 2);
246
+ else if (typeof sourcePayload === 'string') actualPayload = sourcePayload;
247
+ else throw new Error("Invalid source payload. Must be a JSON object or a valid JSON string.");
248
+
249
+ try {
250
+ result = await this.chat.sendMessage({ message: actualPayload });
251
+ } catch (error) {
252
+ log.error("Error with Gemini API:", error);
253
+ throw new Error(`Transformation failed: ${error.message}`);
254
+ }
255
+
256
+ try {
257
+ const modelResponse = result.text;
258
+ const parsedResponse = JSON.parse(modelResponse);
259
+ return parsedResponse;
260
+ } catch (parseError) {
261
+ log.error("Error parsing Gemini response:", parseError);
262
+ throw new Error(`Invalid JSON response from Gemini: ${parseError.message}`);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Transforms payload with automatic validation and retry logic
268
+ * @param {Object} sourcePayload - The source payload to transform
269
+ * @param {AsyncValidatorFunction} validatorFn - Async function that validates the transformed payload
270
+ * @param {Object} [options] - Options for the validation process
271
+ * @param {number} [options.maxRetries] - Override default max retries
272
+ * @param {number} [options.retryDelay] - Override default retry delay
273
+ * @returns {Promise<Object>} - The validated transformed payload
274
+ * @throws {Error} If transformation or validation fails after all retries
275
+ */
276
+ async function transformWithValidation(sourcePayload, validatorFn, options = {}) {
277
+ const maxRetries = options.maxRetries ?? this.maxRetries;
278
+ const retryDelay = options.retryDelay ?? this.retryDelay;
279
+
280
+ let lastPayload = null;
281
+ let lastError = null;
282
+
283
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
284
+ try {
285
+ // First attempt uses normal transformation, subsequent attempts use rebuild
286
+ const transformedPayload = attempt === 0
287
+ ? await this.message(sourcePayload)
288
+ : await this.rebuild(lastPayload, lastError.message);
289
+
290
+ // Validate the transformed payload
291
+ const validatedPayload = await validatorFn(transformedPayload);
292
+
293
+ log.debug(`Transformation and validation succeeded on attempt ${attempt + 1}`);
294
+ return validatedPayload;
295
+
296
+ } catch (error) {
297
+ lastError = error;
298
+
299
+ if (attempt === 0) {
300
+ // First attempt failed - could be transformation or validation error
301
+ lastPayload = await this.message(sourcePayload).catch(() => null);
302
+ }
303
+
304
+ if (attempt < maxRetries) {
305
+ const delay = retryDelay * Math.pow(2, attempt); // Exponential backoff
306
+ log.warn(`Attempt ${attempt + 1} failed, retrying in ${delay}ms...`, error.message);
307
+ await new Promise(res => setTimeout(res, delay));
308
+ } else {
309
+ log.error(`All ${maxRetries + 1} attempts failed`);
310
+ throw new Error(`Transformation with validation failed after ${maxRetries + 1} attempts. Last error: ${error.message}`);
311
+ }
312
+ }
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Rebuilds a payload based on server error feedback
318
+ * @param {Object} lastPayload - The payload that failed validation
319
+ * @param {string} serverError - The error message from the server
320
+ * @returns {Promise<Object>} - A new corrected payload
321
+ * @throws {Error} If the rebuild process fails.
322
+ */
323
+ async function rebuildPayload(lastPayload, serverError) {
324
+ await this.init();
325
+
326
+ const prompt = `
327
+ The previous JSON payload (below) failed validation.
328
+ The server's error message is quoted afterward.
329
+
330
+ ---------------- BAD PAYLOAD ----------------
331
+ ${JSON.stringify(lastPayload, null, 2)}
332
+
333
+ ---------------- SERVER ERROR ----------------
334
+ ${serverError}
335
+
336
+ Please return a NEW JSON payload that corrects the issue.
337
+ Respond with JSON only – no comments or explanations.
338
+ `;
339
+
340
+ let result;
341
+ try {
342
+ result = await this.chat.sendMessage({ message: prompt });
343
+ } catch (err) {
344
+ throw new Error(`Gemini call failed while repairing payload: ${err.message}`);
345
+ }
346
+
347
+ try {
348
+ const text = result.text ?? result.response ?? '';
349
+ return typeof text === 'object' ? text : JSON.parse(text);
350
+ } catch (parseErr) {
351
+ throw new Error(`Gemini returned non-JSON while repairing payload: ${parseErr.message}`);
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Resets the current chat session, clearing all history and examples
357
+ * @this {ExportedAPI}
358
+ * @returns {Promise<void>}
359
+ */
360
+ async function resetChat() {
361
+ if (this.chat) {
362
+ log.debug("Resetting Gemini chat session...");
363
+ this.chat = await this.genAIClient.chats.create({
364
+ model: this.modelName,
365
+ // @ts-ignore
366
+ config: this.chatConfig,
367
+ history: [],
368
+ });
369
+ log.debug("Chat session reset.");
370
+ } else {
371
+ log.warn("Cannot reset chat session: chat not yet initialized.");
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Retrieves the current conversation history for debugging or inspection
377
+ * @returns {Array<Object>} - An array of message objects in the conversation.
378
+ */
379
+ function getChatHistory() {
380
+ if (!this.chat) {
381
+ log.warn("Chat session not initialized. No history available.");
382
+ return [];
383
+ }
384
+ return this.chat.getHistory();
385
+ }
386
+
387
+
388
+ if (import.meta.url === new URL(`file://${process.argv[1]}`).href) {
389
+ log.info("RUNNING AI Transformer as standalone script...");
390
+ (
391
+ async () => {
392
+ try {
393
+ log.info("Initializing AI Transformer...");
394
+ const transformer = new AITransformer({
395
+ modelName: 'gemini-2.0-flash',
396
+ sourceKey: 'INPUT', // Custom source key
397
+ targetKey: 'OUTPUT', // Custom target key
398
+ contextKey: 'CONTEXT', // Custom context key
399
+ maxRetries: 2,
400
+
401
+ });
402
+
403
+ const examples = [
404
+ {
405
+ CONTEXT: "Generate professional profiles with emoji representations",
406
+ INPUT: { "name": "Alice" },
407
+ OUTPUT: { "name": "Alice", "profession": "data scientist", "life_as_told_by_emoji": ["🔬", "💡", "📊", "🧠", "🌟"] }
408
+ },
409
+ {
410
+ INPUT: { "name": "Bob" },
411
+ OUTPUT: { "name": "Bob", "profession": "product manager", "life_as_told_by_emoji": ["📋", "🤝", "🚀", "💬", "🎯"] }
412
+ },
413
+ {
414
+ INPUT: { "name": "Eve" },
415
+ OUTPUT: { "name": "Even", "profession": "security analyst", "life_as_told_by_emoji": ["🕵️‍♀️", "🔒", "💻", "👀", "⚡️"] }
416
+ },
417
+ ];
418
+
419
+ await transformer.init();
420
+ await transformer.seed(examples);
421
+ log.info("AI Transformer initialized and seeded with examples.");
422
+
423
+ // Test normal transformation
424
+ const normalResponse = await transformer.message({ "name": "AK" });
425
+ log.info("Normal Payload Transformed", normalResponse);
426
+
427
+ // Test transformation with validation
428
+ const mockValidator = async (payload) => {
429
+ // Simulate validation logic
430
+ if (!payload.profession || !payload.life_as_told_by_emoji) {
431
+ throw new Error("Missing required fields: profession or life_as_told_by_emoji");
432
+ }
433
+ if (!Array.isArray(payload.life_as_told_by_emoji)) {
434
+ throw new Error("life_as_told_by_emoji must be an array");
435
+ }
436
+ return payload; // Return the payload if validation passes
437
+ };
438
+
439
+ const validatedResponse = await transformer.transformWithValidation(
440
+ { "name": "Lynn" },
441
+ mockValidator
442
+ );
443
+ log.info("Validated Payload Transformed", validatedResponse);
444
+
445
+ if (NODE_ENV === 'dev') debugger;
446
+ } catch (error) {
447
+ log.error("Error in AI Transformer script:", error);
448
+ if (NODE_ENV === 'dev') debugger;
449
+ }
450
+ })();
451
+ }
package/logger.js ADDED
@@ -0,0 +1,16 @@
1
+ import pino from 'pino';
2
+
3
+ // Optional: Configure based on environment (dev vs prod)
4
+ const isDev = process.env.NODE_ENV !== 'production';
5
+
6
+ const logger = pino({
7
+ level: process.env.LOG_LEVEL || 'info', // Supports 'fatal', 'error', 'warn', 'info', 'debug', 'trace'
8
+ transport: isDev
9
+ ? {
10
+ target: 'pino-pretty', // Prettified output for local dev
11
+ options: { colorize: true, translateTime: true }
12
+ }
13
+ : undefined // In prod/cloud, keep as JSON for cloud logging
14
+ });
15
+
16
+ export default logger;
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "ak-gemini",
3
+ "author": "ak@mixpanel.com",
4
+ "description": "AK's Generative AI Helper for doing... transforms",
5
+ "version": "1.0.0",
6
+ "main": "index.js",
7
+ "files": [
8
+ "index.js",
9
+ "index.cjs",
10
+ "types.ts",
11
+ "logger.js"
12
+ ],
13
+ "types": "types.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": "./index.js",
17
+ "require": "./index.cjs"
18
+ }
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/ak--47/ak-gemini"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/ak--47/ak-gemini/issues"
26
+ },
27
+ "homepage": "https://github.com/ak--47/ak-gemini#readme",
28
+ "scripts": {
29
+ "prepublishOnly": "npm run build:cjs",
30
+ "publish": "npm publish --access public",
31
+ "release": "npm version patch && npm publish --access public",
32
+ "local": "./scripts/local.sh",
33
+ "fire": "./scripts/fire.sh",
34
+ "deploy": "./scripts/deploy.sh",
35
+ "perms": "chmod +x ./scripts/*.sh",
36
+ "update-deps": "npx npm-check-updates -u && npm install",
37
+ "prune": "rm -rf tmp/*",
38
+ "test": "node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js",
39
+ "build:cjs": "esbuild index.js --bundle --platform=node --format=cjs --outfile=index.cjs"
40
+ },
41
+ "type": "module",
42
+ "keywords": [
43
+ "gemini",
44
+ "ai wrapper",
45
+ "json transform"
46
+ ],
47
+ "license": "ISC",
48
+ "dependencies": {
49
+ "@google-cloud/functions-framework": "^4.0.0",
50
+ "@google/genai": "^1.3.0",
51
+ "ak-tools": "^1.0.64",
52
+ "dotenv": "^16.5.0",
53
+ "pino": "^9.7.0"
54
+ },
55
+ "devDependencies": {
56
+ "@types/jest": "^29.5.14",
57
+ "esbuild": "^0.25.5",
58
+ "jest": "^29.7.0",
59
+ "nodemon": "^3.1.10",
60
+ "pino-pretty": "^13.0.0"
61
+ }
62
+ }
package/types.ts ADDED
@@ -0,0 +1,65 @@
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
+ }
17
+
18
+ export interface AITransformerContext {
19
+ modelName?: string;
20
+ systemInstructions?: string;
21
+ chatConfig?: ChatConfig;
22
+ genAI?: any;
23
+ chat?: any;
24
+ examplesFile?: string | null;
25
+ exampleData?: TransformationExample[] | null;
26
+ promptKey?: string;
27
+ answerKey?: string;
28
+ contextKey?: string;
29
+ maxRetries?: number;
30
+ retryDelay?: number;
31
+ init: () => Promise<void>; // Initialization function
32
+ seed: () => Promise<void>; // Function to seed the transformer with examples
33
+ message: (payload: Record<string, unknown>) => Promise<Record<string, unknown>>; // Function to send messages to the model
34
+ genAIClient?: GoogleGenAI; // Google GenAI client instance
35
+
36
+ }
37
+
38
+ export interface TransformationExample {
39
+ CONTEXT?: Record<string, unknown>; // optional context for the transformation
40
+ PROMPT?: Record<string, unknown>; // what the user provides as input
41
+ ANSWER?: Record<string, unknown>; // what the model should return as output
42
+ }
43
+
44
+ export interface ExampleFileContent {
45
+ examples: TransformationExample[];
46
+ }
47
+
48
+ export interface AITransformerOptions {
49
+ modelName?: string; // The Gemini model to use
50
+ systemInstructions?: string; // Custom system instructions for the model
51
+ chatConfig?: ChatConfig; // Configuration object for the chat session
52
+ examplesFile?: string; // Path to JSON file containing transformation examples
53
+ exampleData?: TransformationExample[]; // Inline examples to seed the transformer
54
+ sourceKey?: string; // Key name for source data in examples
55
+ targetKey?: string; // Key name for target data in examples
56
+ contextKey?: string; // Key name for context data in examples
57
+ maxRetries?: number; // Maximum retry attempts for auto-retry functionality
58
+ retryDelay?: number; // Initial retry delay in milliseconds
59
+ // ? https://ai.google.dev/gemini-api/docs/structured-output
60
+ responseSchema?: Object; // Schema for validating model responses
61
+ apiKey?: string; // API key for Google GenAI
62
+ }
63
+
64
+ // Async validator function type
65
+ export type AsyncValidatorFunction = (payload: Record<string, unknown>) => Promise<Record<string, unknown>>;