ag-common 0.0.908 → 0.0.910

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.
@@ -1,239 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveGroundedUrl = exports.geminiPromptDirect = exports.geminiPromptImage = exports.isTextGenerationModel = void 0;
4
- const genai_1 = require("@google/genai");
5
- const array_1 = require("../../../common/helpers/array");
6
- const async_1 = require("../../../common/helpers/async");
7
- const fetch_1 = require("../../../common/helpers/fetch");
8
- const log_1 = require("../../../common/helpers/log");
9
- const node_cache_1 = require("../../../common/helpers/node-cache");
10
- const retryOnError_1 = require("../retryOnError");
11
- const apikey_1 = require("./apikey");
12
- let genAIs = [];
13
- const geminiModelsCache = new node_cache_1.TypedNodeCache({ stdTTL: 86400 });
14
- const geminiModelsCacheKey = "gemini-models-v1";
15
- const FALLBACK_GEMINI_MODELS = [
16
- "gemini-3-flash-preview",
17
- "gemini-3-pro-preview",
18
- "gemini-2.5-pro",
19
- "gemini-2.5-flash",
20
- "gemini-2.5-flash-lite",
21
- ];
22
- // Helper to sort models based on preference
23
- const sortModelsByPreference = (models, prefer) => {
24
- const modelArray = [...models];
25
- if (prefer === "quality") {
26
- // Sort pro models first
27
- return modelArray.sort((a, b) => {
28
- const aIsPro = a.includes("pro");
29
- const bIsPro = b.includes("pro");
30
- if (aIsPro && !bIsPro)
31
- return -1;
32
- if (!aIsPro && bIsPro)
33
- return 1;
34
- return 0;
35
- });
36
- }
37
- else if (prefer === "fast") {
38
- // Sort flash models first
39
- return modelArray.sort((a, b) => {
40
- const aIsFlash = a.includes("flash");
41
- const bIsFlash = b.includes("flash");
42
- if (aIsFlash && !bIsFlash)
43
- return -1;
44
- if (!aIsFlash && bIsFlash)
45
- return 1;
46
- return 0;
47
- });
48
- }
49
- // Default behavior - no sorting
50
- return modelArray;
51
- };
52
- const normalizeModelName = (name) => name.replace(/^models\//, "");
53
- const NON_TEXT_MODEL_CAPABILITIES = [
54
- "audio",
55
- "computer-use",
56
- "embedding",
57
- "image",
58
- "live",
59
- "robotics",
60
- "tts",
61
- ];
62
- const isTextGenerationModel = (name) => {
63
- const normalizedName = name.toLowerCase();
64
- return (normalizedName.startsWith("gemini") &&
65
- !NON_TEXT_MODEL_CAPABILITIES.some((capability) => normalizedName.includes(capability)));
66
- };
67
- exports.isTextGenerationModel = isTextGenerationModel;
68
- const getAvailableGeminiModels = async () => {
69
- const cachedModels = geminiModelsCache.get(geminiModelsCacheKey);
70
- if (cachedModels && cachedModels.length > 0) {
71
- return cachedModels;
72
- }
73
- const keyServiceCombinations = (0, apikey_1.getAvailableCombinations)("gemini");
74
- const key = keyServiceCombinations[0]?.key;
75
- if (!key) {
76
- (0, log_1.warn)("No GOOGLE_API_KEY available. Falling back to default Gemini models.");
77
- return [...FALLBACK_GEMINI_MODELS];
78
- }
79
- try {
80
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(key)}`);
81
- if (!response.ok) {
82
- throw new Error(`model list fetch failed with status ${response.status}`);
83
- }
84
- const payload = (await response.json());
85
- const discoveredModels = [
86
- ...new Set((payload.models ?? [])
87
- .filter((m) => (m.supportedGenerationMethods ?? []).includes("generateContent"))
88
- .map((m) => normalizeModelName(m.name ?? ""))
89
- .filter(exports.isTextGenerationModel)),
90
- ];
91
- if (discoveredModels.length === 0) {
92
- throw new Error("no gemini generateContent models discovered");
93
- }
94
- geminiModelsCache.set(geminiModelsCacheKey, discoveredModels);
95
- (0, log_1.info)(`loaded ${discoveredModels.length} Gemini models from API`);
96
- return discoveredModels;
97
- }
98
- catch (e) {
99
- (0, log_1.warn)(`Failed to load Gemini models from API. Falling back to defaults. ${String(e)}`);
100
- return [...FALLBACK_GEMINI_MODELS];
101
- }
102
- };
103
- // Helper to get available key+model combinations
104
- const getAvailableGeminiCombinations = async (prefer) => {
105
- if (genAIs.length === 0) {
106
- const keyServiceCombinations = (0, apikey_1.getAvailableCombinations)("gemini");
107
- const keys = keyServiceCombinations.map((combo) => combo.key);
108
- genAIs = keys.map((k) => [k, new genai_1.GoogleGenAI({ apiKey: k })]);
109
- }
110
- const availableModels = await getAvailableGeminiModels();
111
- const sortedModels = sortModelsByPreference(availableModels, prefer);
112
- const combinations = [];
113
- for (const [key, ai] of genAIs) {
114
- for (const model of sortedModels) {
115
- const keyServiceCombinations = (0, apikey_1.getAvailableCombinations)(`gemini-${model}`, false);
116
- const isAvailable = keyServiceCombinations.some((k) => k.key === key);
117
- if (isAvailable) {
118
- combinations.push([key, ai, model]);
119
- }
120
- }
121
- }
122
- return combinations;
123
- };
124
- const geminiPromptImage = async ({ prompt, urls, ident, prefer, onGenerated, }) => {
125
- let images = [];
126
- if (urls && urls.length > 0) {
127
- images = await (0, async_1.asyncMap)(urls, (i) => (0, fetch_1.fetchToMemory)(i));
128
- }
129
- if (images.some((image) => image === undefined)) {
130
- throw new Error("image not downloaded correctly");
131
- }
132
- const r = await (0, exports.geminiPromptDirect)({
133
- prompt,
134
- images: images.filter(array_1.notEmpty),
135
- ident,
136
- prefer,
137
- onGenerated,
138
- });
139
- return r;
140
- };
141
- exports.geminiPromptImage = geminiPromptImage;
142
- const geminiPromptDirect = async ({ prompt, images = [], ident, prefer, groundedSearch = false, onGenerated, }) => {
143
- const parts = images.map((i) => ({
144
- inlineData: {
145
- data: Buffer.from(i.arraybuffer).toString("base64"),
146
- mimeType: i.type,
147
- },
148
- }));
149
- return (0, retryOnError_1.retryOnError)(`geminiPromptDirect:${ident ?? "unknown"}`, async () => {
150
- const combinations = await getAvailableGeminiCombinations(prefer);
151
- if (combinations.length === 0) {
152
- throw new Error("No available API key and model combinations");
153
- }
154
- let lastFailure;
155
- for (const [key, ai, selectedModel] of combinations) {
156
- // Another concurrent prompt may have exhausted this combination already.
157
- if (!(0, apikey_1.getAvailableCombinations)(`gemini-${selectedModel}`, false).some((entry) => entry.key === key))
158
- continue;
159
- // Prepare the request configuration
160
- const requestConfig = {
161
- model: selectedModel,
162
- contents: [{ parts: [{ text: prompt }, ...parts] }],
163
- config: {},
164
- };
165
- // Add grounded search configuration if enabled
166
- if (groundedSearch) {
167
- requestConfig.config.tools = [{ googleSearch: {} }];
168
- }
169
- (0, log_1.info)("gem query on:" + selectedModel, requestConfig);
170
- try {
171
- // oxlint-disable-next-line no-await-in-loop -- fallback depends on the previous model failing
172
- const response = await ai.models.generateContent(requestConfig);
173
- const rawtext = (response.text ?? "")
174
- .replace(/```(json)?/gi, "")
175
- .replace(/:[ ]+undefined/gim, ": null");
176
- (0, log_1.info)("gem response");
177
- (0, log_1.debug)("gem prompt:" + prompt, ident);
178
- (0, log_1.debug)("gem response:" + rawtext);
179
- (0, log_1.debug)("gem query usage:" + JSON.stringify(response.usageMetadata));
180
- onGenerated?.({ model: response.modelVersion || selectedModel, generatedAt: Date.now() });
181
- return rawtext;
182
- }
183
- catch (e) {
184
- const mod = `gemini-${selectedModel}`;
185
- const status = e.status;
186
- const message = e instanceof Error ? e.message : String(e);
187
- const unavailableModel = status === 404 && message.includes(selectedModel);
188
- if (status === 429 || (0, retryOnError_1.isOverloadedApiKeyError)(e) || unavailableModel) {
189
- (0, log_1.warn)("Gemini model attempt failed; trying next available combination", {
190
- model: selectedModel,
191
- status,
192
- reason: unavailableModel ? "model unavailable" : "quota or capacity exhausted",
193
- });
194
- (0, apikey_1.blockKeyService)(key, mod);
195
- lastFailure = e;
196
- continue;
197
- }
198
- throw e;
199
- }
200
- }
201
- throw lastFailure ?? new Error("No available API key and model combinations");
202
- }, 1, 5000);
203
- };
204
- exports.geminiPromptDirect = geminiPromptDirect;
205
- const resolveGroundedUrl = async (url) => {
206
- if (!url.includes("vertexaisearch.cloud.google.com")) {
207
- (0, log_1.debug)("not a grounded url", url);
208
- return url;
209
- }
210
- try {
211
- (0, log_1.debug)("resolving grounded url", url);
212
- // Fetch the grounded URL with redirect following disabled
213
- const response = await fetch(url, {
214
- method: "HEAD",
215
- redirect: "manual",
216
- });
217
- // Check for redirect responses (301, 302, 307, 308)
218
- if (response.status >= 300 && response.status < 400) {
219
- const location = response.headers.get("location") || response.headers.get("Location");
220
- if (location) {
221
- (0, log_1.debug)("resolved grounded url to", location);
222
- return location;
223
- }
224
- }
225
- // If no redirect, try a GET request to follow any JavaScript redirects
226
- const fullResponse = await fetch(url, {
227
- redirect: "follow",
228
- });
229
- const finalUrl = fullResponse.url;
230
- (0, log_1.debug)("resolved grounded url via GET to", finalUrl);
231
- return finalUrl;
232
- }
233
- catch (error) {
234
- (0, log_1.debug)("error resolving grounded url", error);
235
- // Return original URL if resolution fails
236
- return url;
237
- }
238
- };
239
- exports.resolveGroundedUrl = resolveGroundedUrl;