ag-common 0.0.778 → 0.0.779
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,150 @@
|
|
|
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
|
+
// if url contains "vertexaisearch.cloud.google.com", follow redirect
|
|
140
|
+
const resolveGroundedUrl = (url) => __awaiter(void 0, void 0, void 0, function* () {
|
|
141
|
+
if (url.includes('vertexaisearch.cloud.google.com')) {
|
|
142
|
+
const response = yield fetch(url);
|
|
143
|
+
const redirectUrl = response.headers.get('Location');
|
|
144
|
+
if (redirectUrl) {
|
|
145
|
+
return redirectUrl;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return url;
|
|
149
|
+
});
|
|
150
|
+
exports.resolveGroundedUrl = resolveGroundedUrl;
|
|
@@ -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.
|
|
2
|
+
"version": "0.0.779",
|
|
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",
|