Skip to content

Commit 4ac900f

Browse files
Gemini model list sync (#3609)
* Update defaultModels.js add gemma-3-27b-it to v1BetaModels * Update defaultModels.js 20250330 model update * Update defaultModels.js remove text embedding * Update name and inputTokenLimit modelMap.js * Update gemini to load models from both endpoints dedupe models decide endpoint based on expieremental status from fetch add util script for maintainers reduce cache time on gemini models to 1 day * remove comment --------- Co-authored-by: DreamerC <[email protected]>
1 parent a5e7d87 commit 4ac900f

File tree

5 files changed

+343
-141
lines changed

5 files changed

+343
-141
lines changed

.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ frontend/bundleinspector.html
1010

1111
#server
1212
server/swagger/openapi.json
13+
server/**/*.mjs
1314

1415
#embed
1516
**/static/**

server/utils/AiProviders/gemini/defaultModels.js

+41-18
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,61 @@
11
const { MODEL_MAP } = require("../modelMap");
22

33
const stableModels = [
4-
"gemini-pro",
5-
"gemini-1.0-pro",
6-
"gemini-1.5-pro-latest",
7-
"gemini-1.5-flash-latest",
4+
// %STABLE_MODELS% - updated 2025-04-07T20:29:49.276Z
5+
"gemini-1.5-pro-001",
6+
"gemini-1.5-pro-002",
7+
"gemini-1.5-pro",
8+
"gemini-1.5-flash-001",
9+
"gemini-1.5-flash",
10+
"gemini-1.5-flash-002",
11+
"gemini-1.5-flash-8b",
12+
"gemini-1.5-flash-8b-001",
13+
"gemini-2.0-flash",
14+
"gemini-2.0-flash-001",
15+
"gemini-2.0-flash-lite-001",
16+
"gemini-2.0-flash-lite",
17+
// %EOC_STABLE_MODELS%
818
];
919

10-
const experimentalModels = [
11-
"gemini-1.5-pro-exp-0801",
12-
"gemini-1.5-pro-exp-0827",
13-
"gemini-1.5-flash-exp-0827",
20+
// There are some models that are only available in the v1beta API
21+
// and some models that are only available in the v1 API
22+
// generally, v1beta models have `exp` in the name, but not always
23+
// so we check for both against a static list as well via API.
24+
const v1BetaModels = [
25+
// %V1BETA_MODELS% - updated 2025-04-07T20:29:49.276Z
26+
"gemini-1.5-pro-latest",
27+
"gemini-1.5-flash-latest",
28+
"gemini-1.5-flash-8b-latest",
1429
"gemini-1.5-flash-8b-exp-0827",
15-
"gemini-exp-1114",
16-
"gemini-exp-1121",
30+
"gemini-1.5-flash-8b-exp-0924",
31+
"gemini-2.5-pro-exp-03-25",
32+
"gemini-2.5-pro-preview-03-25",
33+
"gemini-2.0-flash-exp",
34+
"gemini-2.0-flash-exp-image-generation",
35+
"gemini-2.0-flash-lite-preview-02-05",
36+
"gemini-2.0-flash-lite-preview",
37+
"gemini-2.0-pro-exp",
38+
"gemini-2.0-pro-exp-02-05",
1739
"gemini-exp-1206",
40+
"gemini-2.0-flash-thinking-exp-01-21",
41+
"gemini-2.0-flash-thinking-exp",
42+
"gemini-2.0-flash-thinking-exp-1219",
1843
"learnlm-1.5-pro-experimental",
19-
"gemini-2.0-flash-exp",
44+
"gemma-3-1b-it",
45+
"gemma-3-4b-it",
46+
"gemma-3-12b-it",
47+
"gemma-3-27b-it",
48+
// %EOC_V1BETA_MODELS%
2049
];
2150

22-
// There are some models that are only available in the v1beta API
23-
// and some models that are only available in the v1 API
24-
// generally, v1beta models have `exp` in the name, but not always
25-
// so we check for both against a static list as well.
26-
const v1BetaModels = ["gemini-1.5-pro-latest", "gemini-1.5-flash-latest"];
27-
2851
const defaultGeminiModels = [
2952
...stableModels.map((model) => ({
3053
id: model,
3154
name: model,
3255
contextWindow: MODEL_MAP.gemini[model],
3356
experimental: false,
3457
})),
35-
...experimentalModels.map((model) => ({
58+
...v1BetaModels.map((model) => ({
3659
id: model,
3760
name: model,
3861
contextWindow: MODEL_MAP.gemini[model],

server/utils/AiProviders/gemini/index.js

+142-68
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,11 @@ class GeminiLLM {
2828
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
2929
this.model =
3030
modelPreference || process.env.GEMINI_LLM_MODEL_PREF || "gemini-pro";
31+
32+
const isExperimental = this.isExperimentalModel(this.model);
3133
this.gemini = genAI.getGenerativeModel(
3234
{ model: this.model },
33-
{
34-
apiVersion:
35-
/**
36-
* There are some models that are only available in the v1beta API
37-
* and some models that are only available in the v1 API
38-
* generally, v1beta models have `exp` in the name, but not always
39-
* so we check for both against a static list as well.
40-
* @see {v1BetaModels}
41-
*/
42-
this.model.includes("exp") || v1BetaModels.includes(this.model)
43-
? "v1beta"
44-
: "v1",
45-
}
35+
{ apiVersion: isExperimental ? "v1beta" : "v1" }
4636
);
4737
this.limits = {
4838
history: this.promptWindowLimit() * 0.15,
@@ -59,7 +49,7 @@ class GeminiLLM {
5949
this.cacheModelPath = path.resolve(cacheFolder, "models.json");
6050
this.cacheAtPath = path.resolve(cacheFolder, ".cached_at");
6151
this.#log(
62-
`Initialized with model: ${this.model} (${this.promptWindowLimit()})`
52+
`Initialized with model: ${this.model} ${isExperimental ? "[Experimental v1beta]" : "[Stable v1]"} - ctx: ${this.promptWindowLimit()}`
6353
);
6454
}
6555

@@ -71,7 +61,7 @@ class GeminiLLM {
7161
// from the current date. If it is, then we will refetch the API so that all the models are up
7262
// to date.
7363
static cacheIsStale() {
74-
const MAX_STALE = 6.048e8; // 1 Week in MS
64+
const MAX_STALE = 8.64e7; // 1 day in MS
7565
if (!fs.existsSync(path.resolve(cacheFolder, ".cached_at"))) return true;
7666
const now = Number(new Date());
7767
const timestampMs = Number(
@@ -168,6 +158,28 @@ class GeminiLLM {
168158
}
169159
}
170160

161+
/**
162+
* Checks if a model is experimental by reading from the cache if available, otherwise it will perform
163+
* a blind check against the v1BetaModels list - which is manually maintained and updated.
164+
* @param {string} modelName - The name of the model to check
165+
* @returns {boolean} A boolean indicating if the model is experimental
166+
*/
167+
isExperimentalModel(modelName) {
168+
if (
169+
fs.existsSync(cacheFolder) &&
170+
fs.existsSync(path.resolve(cacheFolder, "models.json"))
171+
) {
172+
const models = safeJsonParse(
173+
fs.readFileSync(path.resolve(cacheFolder, "models.json"))
174+
);
175+
const model = models.find((model) => model.id === modelName);
176+
if (!model) return false;
177+
return model.experimental;
178+
}
179+
180+
return modelName.includes("exp") || v1BetaModels.includes(modelName);
181+
}
182+
171183
/**
172184
* Fetches Gemini models from the Google Generative AI API
173185
* @param {string} apiKey - The API key to use for the request
@@ -186,63 +198,125 @@ class GeminiLLM {
186198
);
187199
}
188200

189-
const url = new URL(
190-
"https://generativelanguage.googleapis.com/v1beta/models"
191-
);
192-
url.searchParams.set("pageSize", limit);
193-
url.searchParams.set("key", apiKey);
194-
if (pageToken) url.searchParams.set("pageToken", pageToken);
195-
let success = false;
196-
197-
const models = await fetch(url.toString(), {
198-
method: "GET",
199-
headers: { "Content-Type": "application/json" },
200-
})
201-
.then((res) => res.json())
202-
.then((data) => {
203-
if (data.error) throw new Error(data.error.message);
204-
return data.models ?? [];
205-
})
206-
.then((models) => {
207-
success = true;
208-
return models
209-
.filter(
210-
(model) => !model.displayName.toLowerCase().includes("tuning")
211-
)
212-
.filter((model) =>
213-
model.supportedGenerationMethods.includes("generateContent")
214-
) // Only generateContent is supported
215-
.map((model) => {
216-
return {
217-
id: model.name.split("/").pop(),
218-
name: model.displayName,
219-
contextWindow: model.inputTokenLimit,
220-
experimental: model.name.includes("exp"),
221-
};
222-
});
223-
})
224-
.catch((e) => {
225-
console.error(`Gemini:getGeminiModels`, e.message);
226-
success = false;
227-
return defaultGeminiModels;
228-
});
201+
const stableModels = [];
202+
const allModels = [];
229203

230-
if (success) {
231-
console.log(
232-
`\x1b[32m[GeminiLLM]\x1b[0m Writing cached models API response to disk.`
233-
);
234-
if (!fs.existsSync(cacheFolder))
235-
fs.mkdirSync(cacheFolder, { recursive: true });
236-
fs.writeFileSync(
237-
path.resolve(cacheFolder, "models.json"),
238-
JSON.stringify(models)
204+
// Fetch from v1
205+
try {
206+
const url = new URL(
207+
"https://generativelanguage.googleapis.com/v1/models"
239208
);
240-
fs.writeFileSync(
241-
path.resolve(cacheFolder, ".cached_at"),
242-
new Date().getTime().toString()
209+
url.searchParams.set("pageSize", limit);
210+
url.searchParams.set("key", apiKey);
211+
if (pageToken) url.searchParams.set("pageToken", pageToken);
212+
await fetch(url.toString(), {
213+
method: "GET",
214+
headers: { "Content-Type": "application/json" },
215+
})
216+
.then((res) => res.json())
217+
.then((data) => {
218+
if (data.error) throw new Error(data.error.message);
219+
return data.models ?? [];
220+
})
221+
.then((models) => {
222+
return models
223+
.filter(
224+
(model) => !model.displayName?.toLowerCase()?.includes("tuning")
225+
) // remove tuning models
226+
.filter(
227+
(model) =>
228+
!model.description?.toLowerCase()?.includes("deprecated")
229+
) // remove deprecated models (in comment)
230+
.filter((model) =>
231+
// Only generateContent is supported
232+
model.supportedGenerationMethods.includes("generateContent")
233+
)
234+
.map((model) => {
235+
stableModels.push(model.name);
236+
allModels.push({
237+
id: model.name.split("/").pop(),
238+
name: model.displayName,
239+
contextWindow: model.inputTokenLimit,
240+
experimental: false,
241+
});
242+
});
243+
})
244+
.catch((e) => {
245+
console.error(`Gemini:getGeminiModelsV1`, e.message);
246+
return;
247+
});
248+
} catch (e) {
249+
console.error(`Gemini:getGeminiModelsV1`, e.message);
250+
}
251+
252+
// Fetch from v1beta
253+
try {
254+
const url = new URL(
255+
"https://generativelanguage.googleapis.com/v1beta/models"
243256
);
257+
url.searchParams.set("pageSize", limit);
258+
url.searchParams.set("key", apiKey);
259+
if (pageToken) url.searchParams.set("pageToken", pageToken);
260+
await fetch(url.toString(), {
261+
method: "GET",
262+
headers: { "Content-Type": "application/json" },
263+
})
264+
.then((res) => res.json())
265+
.then((data) => {
266+
if (data.error) throw new Error(data.error.message);
267+
return data.models ?? [];
268+
})
269+
.then((models) => {
270+
return models
271+
.filter((model) => !stableModels.includes(model.name)) // remove stable models that are already in the v1 list
272+
.filter(
273+
(model) => !model.displayName?.toLowerCase()?.includes("tuning")
274+
) // remove tuning models
275+
.filter(
276+
(model) =>
277+
!model.description?.toLowerCase()?.includes("deprecated")
278+
) // remove deprecated models (in comment)
279+
.filter((model) =>
280+
// Only generateContent is supported
281+
model.supportedGenerationMethods.includes("generateContent")
282+
)
283+
.map((model) => {
284+
allModels.push({
285+
id: model.name.split("/").pop(),
286+
name: model.displayName,
287+
contextWindow: model.inputTokenLimit,
288+
experimental: true,
289+
});
290+
});
291+
})
292+
.catch((e) => {
293+
console.error(`Gemini:getGeminiModelsV1beta`, e.message);
294+
return;
295+
});
296+
} catch (e) {
297+
console.error(`Gemini:getGeminiModelsV1beta`, e.message);
298+
}
299+
300+
if (allModels.length === 0) {
301+
console.error(`Gemini:getGeminiModels - No models found`);
302+
return defaultGeminiModels;
244303
}
245-
return models;
304+
305+
console.log(
306+
`\x1b[32m[GeminiLLM]\x1b[0m Writing cached models API response to disk.`
307+
);
308+
if (!fs.existsSync(cacheFolder))
309+
fs.mkdirSync(cacheFolder, { recursive: true });
310+
fs.writeFileSync(
311+
path.resolve(cacheFolder, "models.json"),
312+
JSON.stringify(allModels)
313+
);
314+
fs.writeFileSync(
315+
path.resolve(cacheFolder, ".cached_at"),
316+
new Date().getTime().toString()
317+
);
318+
319+
return allModels;
246320
}
247321

248322
/**

0 commit comments

Comments
 (0)