ag-common 0.0.778 → 0.0.780

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.
@@ -0,0 +1,12 @@
1
+ export type ModelPreference = 'quality' | 'fast' | undefined;
2
+ export declare const geminiPromptDirect: ({ prompt, images, ident, prefer, groundedSearch, }: {
3
+ prompt: string;
4
+ images?: {
5
+ arraybuffer: ArrayBuffer;
6
+ type: string;
7
+ }[];
8
+ ident: string | undefined;
9
+ prefer?: ModelPreference;
10
+ groundedSearch?: boolean;
11
+ }) => Promise<string>;
12
+ export declare const resolveGroundedUrl: (url: string) => Promise<string>;
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.resolveGroundedUrl = exports.geminiPromptDirect = void 0;
16
+ const genai_1 = require("@google/genai");
17
+ const node_cache_1 = __importDefault(require("node-cache"));
18
+ const log_1 = require("../../../common/helpers/log");
19
+ const truncate_1 = require("../../../common/helpers/string/truncate");
20
+ let genAIs;
21
+ // Available Gemini models
22
+ const GEMINI_MODELS = ['gemini-2.5-pro', 'gemini-2.5-flash'];
23
+ // Initialize NodeCache with 1 hour TTL for blocklisted key+model combinations
24
+ const blocklist = new node_cache_1.default({ stdTTL: 3600 });
25
+ // Helper to generate blocklist key
26
+ const getBlocklistKey = (apiKey, model) => `${(0, truncate_1.truncate)(apiKey, 10)}_${model}`;
27
+ // Helper to check if a key+model combination is blocklisted
28
+ const isBlocklisted = (apiKey, model) => {
29
+ return blocklist.has(getBlocklistKey(apiKey, model));
30
+ };
31
+ // Helper to add a key+model combination to the blocklist
32
+ const addToBlocklist = (apiKey, model) => {
33
+ blocklist.set(getBlocklistKey(apiKey, model), true);
34
+ };
35
+ // Helper to sort models based on preference
36
+ const sortModelsByPreference = (models, prefer) => {
37
+ const modelArray = [...models];
38
+ if (prefer === 'quality') {
39
+ // Sort pro models first
40
+ return modelArray.sort((a, b) => {
41
+ const aIsPro = a.includes('pro');
42
+ const bIsPro = b.includes('pro');
43
+ if (aIsPro && !bIsPro)
44
+ return -1;
45
+ if (!aIsPro && bIsPro)
46
+ return 1;
47
+ return 0;
48
+ });
49
+ }
50
+ else if (prefer === 'fast') {
51
+ // Sort flash models first
52
+ return modelArray.sort((a, b) => {
53
+ const aIsFlash = a.includes('flash');
54
+ const bIsFlash = b.includes('flash');
55
+ if (aIsFlash && !bIsFlash)
56
+ return -1;
57
+ if (!aIsFlash && bIsFlash)
58
+ return 1;
59
+ return 0;
60
+ });
61
+ }
62
+ // Default behavior - no sorting
63
+ return modelArray;
64
+ };
65
+ // Helper to get available key+model combinations
66
+ const getAvailableCombinations = (prefer) => {
67
+ var _a;
68
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
69
+ if (!genAIs) {
70
+ const keys = ((_a = process.env.GOOGLE_API_KEY) !== null && _a !== void 0 ? _a : '').split(',');
71
+ genAIs = keys.map((k) => [k, new genai_1.GoogleGenAI({ apiKey: k })]);
72
+ }
73
+ const sortedModels = sortModelsByPreference(GEMINI_MODELS, prefer);
74
+ const combinations = [];
75
+ for (const [key, ai] of genAIs) {
76
+ for (const model of sortedModels) {
77
+ if (!isBlocklisted(key, model)) {
78
+ combinations.push([key, ai, model]);
79
+ }
80
+ }
81
+ }
82
+ // If all combinations are blocklisted, clear the blocklist and try again
83
+ if (combinations.length === 0) {
84
+ (0, log_1.warn)('All API key + model combinations were blocklisted. Clearing blocklist and trying again.');
85
+ blocklist.flushAll();
86
+ for (const [key, ai] of genAIs) {
87
+ for (const model of sortedModels) {
88
+ combinations.push([key, ai, model]);
89
+ }
90
+ }
91
+ }
92
+ return combinations;
93
+ };
94
+ const geminiPromptDirect = (_a) => __awaiter(void 0, [_a], void 0, function* ({ prompt, images = [], ident, prefer, groundedSearch = false, }) {
95
+ var _b;
96
+ const combinations = getAvailableCombinations(prefer);
97
+ if (combinations.length === 0) {
98
+ throw new Error('No available API key and model combinations');
99
+ }
100
+ const [key, ai, selectedModel] = combinations[0];
101
+ const parts = images.map((i) => ({
102
+ inlineData: {
103
+ data: Buffer.from(i.arraybuffer).toString('base64'),
104
+ mimeType: i.type,
105
+ },
106
+ }));
107
+ // Prepare the request configuration
108
+ const requestConfig = {
109
+ model: selectedModel,
110
+ contents: [{ parts: [{ text: prompt }, ...parts] }],
111
+ config: {},
112
+ };
113
+ // Add grounded search configuration if enabled
114
+ if (groundedSearch) {
115
+ requestConfig.config.tools = [{ googleSearch: {} }];
116
+ }
117
+ (0, log_1.info)('gem query on:' + selectedModel, requestConfig);
118
+ try {
119
+ const response = yield ai.models.generateContent(requestConfig);
120
+ const rawtext = ((_b = response.text) !== null && _b !== void 0 ? _b : '')
121
+ .replace(/```(json)?/gi, '')
122
+ .replace(/:[ ]+undefined/gim, ': null');
123
+ (0, log_1.info)('gem response');
124
+ (0, log_1.debug)('gem prompt:' + prompt, ident);
125
+ (0, log_1.debug)('gem response:' + rawtext);
126
+ (0, log_1.debug)('gem query usage:' + JSON.stringify(response.usageMetadata));
127
+ return rawtext;
128
+ }
129
+ catch (e) {
130
+ const em = e.message.toLowerCase();
131
+ if (em.includes('429') || em.includes('safety')) {
132
+ (0, log_1.warn)(`key+model blocklisted: ${(0, truncate_1.truncate)(key, 10)}_${selectedModel}`);
133
+ addToBlocklist(key, selectedModel);
134
+ }
135
+ throw e;
136
+ }
137
+ });
138
+ exports.geminiPromptDirect = geminiPromptDirect;
139
+ const resolveGroundedUrl = (url) => __awaiter(void 0, void 0, void 0, function* () {
140
+ if (!url.includes('vertexaisearch.cloud.google.com')) {
141
+ (0, log_1.debug)('not a grounded url', url);
142
+ return url;
143
+ }
144
+ try {
145
+ (0, log_1.debug)('resolving grounded url', url);
146
+ const response = yield fetch(url);
147
+ const redirectUrl = response.headers.get('Location') || response.headers.get('location');
148
+ if (redirectUrl) {
149
+ (0, log_1.debug)('resolved grounded url', url, redirectUrl);
150
+ return redirectUrl;
151
+ }
152
+ (0, log_1.debug)('no redirect found, returning original url', url);
153
+ }
154
+ catch (e) {
155
+ (0, log_1.debug)('error resolving grounded url', url, e);
156
+ }
157
+ return url;
158
+ });
159
+ exports.resolveGroundedUrl = resolveGroundedUrl;
@@ -4,6 +4,7 @@ export * from './aws';
4
4
  export * from './cosmos';
5
5
  export * from './dynamo';
6
6
  export * from './enforceDynamoProvisionCap';
7
+ export * from './gemini';
7
8
  export * from './s3';
8
9
  export * from './ses';
9
10
  export * from './sqs';
@@ -20,6 +20,7 @@ __exportStar(require("./aws"), exports);
20
20
  __exportStar(require("./cosmos"), exports);
21
21
  __exportStar(require("./dynamo"), exports);
22
22
  __exportStar(require("./enforceDynamoProvisionCap"), exports);
23
+ __exportStar(require("./gemini"), exports);
23
24
  __exportStar(require("./s3"), exports);
24
25
  __exportStar(require("./ses"), exports);
25
26
  __exportStar(require("./sqs"), exports);
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.778",
2
+ "version": "0.0.780",
3
3
  "name": "ag-common",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
@@ -26,6 +26,7 @@
26
26
  "@aws-sdk/lib-dynamodb": "^3.901.0",
27
27
  "@aws-sdk/s3-presigned-post": "^3.901.0",
28
28
  "@azure/cosmos": "^4.5.1",
29
+ "@google/genai": "^1.22.0",
29
30
  "@storybook/addon-styling-webpack": "^2.0.0",
30
31
  "@storybook/react-vite": "^9.1.10",
31
32
  "aws-cdk-lib": "^2.219.0",