⚡ feat: Add Gemini 3.8 Flash Support (#15519)
Some checks failed
Backend Unit Tests / Build packages (push) Waiting to run
Backend Unit Tests / Codegraph select (push) Waiting to run
Backend Unit Tests / TypeScript type checks (push) Blocked by required conditions
Backend Unit Tests / Circular dependency checks (push) Waiting to run
Backend Unit Tests / Tests: api (shard 1/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 2/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 3/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: data-provider (push) Blocked by required conditions
Backend Unit Tests / Tests: data-schemas (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 1/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 2/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 3/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 4/4) (push) Blocked by required conditions
Codegraph E2E Votes / vote (full suite) (push) Waiting to run
Docker Dev Branch Images Build / publish (push) Waiting to run
Frontend Unit Tests / Codegraph select (push) Waiting to run
Frontend Unit Tests / Build packages (push) Waiting to run
Frontend Unit Tests / TypeScript type checks (client) (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/client (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 1/2) (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 2/2) (push) Blocked by required conditions
Frontend Unit Tests / Vite build verification (push) Blocked by required conditions
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Has been cancelled
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Has been cancelled

* ⚡ feat: Add Gemini 3.8 Flash Support

Adds first-class support for Google's Gemini 3.8 Flash (`gemini-3.8-flash`,
GA 2026-09-02) for both the Gemini API (AI Studio) and Google Cloud Gemini
Enterprise Agent Platform, following the Gemini 3.7 Flash integration (#14818).

The Flash-family handler that PR generalized already carries the shape 3.8
needs, so registering the model is a one-line rule rather than new behavior:
it inherits the strip of deprecated sampling params (temperature/topP/topK),
the rejected penalty params, and `thinkingBudget`, defaults to `medium`
thinking, and — like 3.7 — errors on `minimal`, so an explicit `minimal` is
substituted with the nearest supported level, `low`.

- Context window (1,048,576) in googleModels; API + cache pricing in tx.ts
- Model dropdown (config.ts) and GOOGLE_MODELS examples for both integrations
- Register the model in the Flash-family thinking-rule table
- Apply the same introductory pricing as 3.6/3.7 Flash ($0.75 in / $3.75 out /
  $0.075 cached, per 1M), reverting to $1.50 / $7.50 / $0.15 on 2027-01-01;
  the existing comments at both call sites now name 3.8 alongside 3.6/3.7
- Tests mirroring the 3.7 Flash coverage: thinking default, legacy-param strip,
  explicit level pass-through, `minimal` substitution, versioned aliases,
  context window, model-key mapping, and rates

Refs #15516

Ref: https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash
Ref: https://ai.google.dev/gemini-api/docs/pricing

* 🔧 chore: Update @librechat/agents dependency to version 3.7.15 in package-lock.json and related package.json files
This commit is contained in:
Danny Avila 2026-09-02 15:06:40 -04:00 • committed by GitHub
parent 29f738dea8
commit 790f2b3153
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 162 additions and 16 deletions

View file

@ -519,10 +519,10 @@ GOOGLE_KEY=user_provided
# GOOGLE_AUTH_HEADER=true
# Gemini API (AI Studio)
# GOOGLE_MODELS=gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite,gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash,gemini-2.0-flash-lite
# GOOGLE_MODELS=gemini-3.8-flash,gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite,gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash,gemini-2.0-flash-lite
# Vertex AI
# GOOGLE_MODELS=gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite,gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash-001,gemini-2.0-flash-lite-001
# GOOGLE_MODELS=gemini-3.8-flash,gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite,gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash-001,gemini-2.0-flash-lite-001
# GOOGLE_TITLE_MODEL=gemini-2.0-flash-lite-001

View file

@ -46,7 +46,7 @@
"@azure/storage-blob": "^12.30.0",
"@google/genai": "^2.8.0",
"@keyv/redis": "5.1.6",
"@librechat/agents": "^3.7.14",
"@librechat/agents": "^3.7.15",
"@librechat/api": "*",
"@librechat/data-schemas": "*",
"@microsoft/microsoft-graph-client": "^3.0.7",

View file

@ -359,6 +359,9 @@ describe('getModelMaxTokens', () => {
expect(getModelMaxTokens('gemini-3.7-flash', EModelEndpoint.google)).toBe(
maxTokensMap[EModelEndpoint.google]['gemini-3.7-flash'],
);
expect(getModelMaxTokens('gemini-3.8-flash', EModelEndpoint.google)).toBe(
maxTokensMap[EModelEndpoint.google]['gemini-3.8-flash'],
);
expect(getModelMaxTokens('gemini-2.5-pro', EModelEndpoint.google)).toBe(
maxTokensMap[EModelEndpoint.google]['gemini-2.5-pro'],
);

10
package-lock.json generated
View file

@ -63,7 +63,7 @@
"@azure/storage-blob": "^12.30.0",
"@google/genai": "^2.8.0",
"@keyv/redis": "5.1.6",
"@librechat/agents": "^3.7.14",
"@librechat/agents": "^3.7.15",
"@librechat/api": "*",
"@librechat/data-schemas": "*",
"@microsoft/microsoft-graph-client": "^3.0.7",
@ -10633,9 +10633,9 @@
}
},
"node_modules/@librechat/agents": {
"version": "3.7.14",
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.7.14.tgz",
"integrity": "sha512-aKWy0sJgR84eRUOujZAwL15nEfclDZ48ULlSGlazw5ozN/Ixiqr+bc+eY/cvDEBWPXpvoZKb/G+XjQJVUP3xVg==",
"version": "3.7.15",
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.7.15.tgz",
"integrity": "sha512-4huKqYzZ7mOav0NdtzId0adT4YE00bRfx+FaPtfzAeZxUryKXwRPmq8/La77tH7Ljf+AnC00pdJHUcPezuBoHQ==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.115.0",
@ -42639,7 +42639,7 @@
"@azure/storage-blob": "^12.30.0",
"@google/genai": "^2.8.0",
"@keyv/redis": "5.1.6",
"@librechat/agents": "^3.7.14",
"@librechat/agents": "^3.7.15",
"@librechat/data-schemas": "*",
"@modelcontextprotocol/sdk": "^1.30.0",
"@opentelemetry/api": "^1.9.0",

View file

@ -113,7 +113,7 @@
"@azure/storage-blob": "^12.30.0",
"@google/genai": "^2.8.0",
"@keyv/redis": "5.1.6",
"@librechat/agents": "^3.7.14",
"@librechat/agents": "^3.7.15",
"@librechat/data-schemas": "*",
"@modelcontextprotocol/sdk": "^1.30.0",
"@opentelemetry/api": "^1.9.0",

View file

@ -1069,6 +1069,111 @@ describe('getGoogleConfig', () => {
});
});
it('should default Gemini 3.8 Flash to medium thinkingLevel', () => {
const credentials = {
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
};
const result = getGoogleConfig(credentials, {
modelOptions: {
model: 'gemini-3.8-flash',
},
});
expect((result.llmConfig as Record<string, unknown>).thinkingConfig).toMatchObject({
includeThoughts: true,
thinkingLevel: 'MEDIUM',
});
});
it('should remove legacy sampling params for Gemini 3.8 Flash', () => {
const credentials = {
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
};
const modelOptions = {
model: 'gemini-3.8-flash',
temperature: 0.7,
topP: 0.9,
topK: 40,
top_p: 0.9,
top_k: 40,
presencePenalty: 0.5,
frequencyPenalty: 0.5,
thinking_budget: 5000,
} as unknown as t.GoogleParameters;
const result = getGoogleConfig(credentials, { modelOptions });
expect(result.llmConfig).not.toHaveProperty('temperature');
expect(result.llmConfig).not.toHaveProperty('topP');
expect(result.llmConfig).not.toHaveProperty('topK');
expect(result.llmConfig).not.toHaveProperty('top_p');
expect(result.llmConfig).not.toHaveProperty('top_k');
expect(result.llmConfig).not.toHaveProperty('presencePenalty');
expect(result.llmConfig).not.toHaveProperty('frequencyPenalty');
expect(result.llmConfig).not.toHaveProperty('thinking_budget');
});
it.each([
[ThinkingLevel.low, 'LOW'],
[ThinkingLevel.medium, 'MEDIUM'],
[ThinkingLevel.high, 'HIGH'],
])('should preserve explicit Gemini 3.8 Flash thinkingLevel "%s"', (level, expected) => {
const credentials = {
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
};
const result = getGoogleConfig(credentials, {
modelOptions: {
model: 'gemini-3.8-flash',
thinkingLevel: level,
},
});
expect((result.llmConfig as Record<string, unknown>).thinkingConfig).toMatchObject({
includeThoughts: true,
thinkingLevel: expected,
});
});
it('should substitute minimal thinkingLevel with low for Gemini 3.8 Flash', () => {
const credentials = {
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
};
const result = getGoogleConfig(credentials, {
modelOptions: {
model: 'gemini-3.8-flash',
thinkingLevel: ThinkingLevel.minimal,
},
});
expect((result.llmConfig as Record<string, unknown>).thinkingConfig).toMatchObject({
includeThoughts: true,
thinkingLevel: 'LOW',
});
});
it('should apply Gemini 3.8 Flash handling to versioned aliases', () => {
const credentials = {
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
};
const result = getGoogleConfig(credentials, {
modelOptions: {
model: 'models/gemini-3.8-flash-latest',
temperature: 0.7,
},
});
expect(result.llmConfig).not.toHaveProperty('temperature');
expect((result.llmConfig as Record<string, unknown>).thinkingConfig).toMatchObject({
includeThoughts: true,
thinkingLevel: 'MEDIUM',
});
});
it('should remove unsupported penalty params for Gemini 3.5 Flash-Lite', () => {
const credentials = {
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',

View file

@ -32,12 +32,15 @@ type GeminiFlashThinkingRule = {
* most-specific-first so `gemini-3.5-flash-lite` resolves before the
* `gemini-3.5-flash` prefix.
*
* Gemini 3.7 Flash supports only `low`/`medium`/`high` and errors on `minimal`,
* so an explicit `minimal` is substituted with the nearest supported level.
* Gemini 3.7 and 3.8 Flash support only `low`/`medium`/`high` and error on
* `minimal`, so an explicit `minimal` is substituted with the nearest supported
* level.
* @see https://ai.google.dev/gemini-api/docs/latest-model#api-changes-and-parameter-updates
* @see https://ai.google.dev/gemini-api/docs/models/gemini-3.7-flash
* @see https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash
*/
const geminiFlashThinkingRules: ReadonlyArray<GeminiFlashThinkingRule> = [
{ id: 'gemini-3.8-flash', default: 'MEDIUM', substitutions: { MINIMAL: 'LOW' } },
{ id: 'gemini-3.7-flash', default: 'MEDIUM', substitutions: { MINIMAL: 'LOW' } },
{ id: 'gemini-3.6-flash', default: 'MEDIUM' },
{ id: 'gemini-3.5-flash-lite', default: 'MINIMAL' },

View file

@ -153,7 +153,7 @@ describe('getOpenAIConfig - Google Compatibility', () => {
expect(result.tools).toEqual([]);
});
it.each(['gemini-3.6-flash', 'gemini-3.7-flash'])(
it.each(['gemini-3.6-flash', 'gemini-3.7-flash', 'gemini-3.8-flash'])(
'should strip Flash-blocked addParams so the transform cannot re-add them (%s)',
(model) => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });

View file

@ -79,6 +79,18 @@ describe('Gemini 3.7 Flash', () => {
});
});
describe('Gemini 3.8 Flash', () => {
it('resolves the 1,048,576-token context window', () => {
expect(getModelMaxTokens('gemini-3.8-flash', EModelEndpoint.google)).toBe(1048576);
});
it('matches the longest key over the shorter gemini-3 pattern for aliases', () => {
expect(getModelMaxTokens('models/gemini-3.8-flash-latest', EModelEndpoint.google)).toBe(
1048576,
);
});
});
describe('Qwen3.5 and later generations', () => {
it('resolves 262K for the vLLM model id that regressed to the qwen3 window', () => {
expect(getModelMaxTokens('Qwen/Qwen3.5-397B-A17B-FP8', EModelEndpoint.openAI)).toBe(262144);

View file

@ -143,6 +143,7 @@ const googleModels = {
'gemini-3.5-flash-lite': 1048576,
'gemini-3.6-flash': 1048576,
'gemini-3.7-flash': 1048576,
'gemini-3.8-flash': 1048576,
};
const anthropicModels = {

View file

@ -2739,6 +2739,8 @@ export const defaultModels = {
[EModelEndpoint.assistants]: [...sharedOpenAIModels, 'chatgpt-4o-latest'],
[EModelEndpoint.agents]: sharedOpenAIModels, // TODO: Add agent models (agentsModels)
[EModelEndpoint.google]: [
// Gemini 3.8 Models
'gemini-3.8-flash',
// Gemini 3.7 Models
'gemini-3.7-flash',
// Gemini 3.6 Models

View file

@ -1556,6 +1556,7 @@ describe('Google Model Tests', () => {
'gemini-3.1-pro-preview',
'gemini-3.1-pro-preview-customtools',
'gemini-3.1-flash-lite-preview',
'gemini-3.8-flash',
'gemini-3.7-flash',
'gemini-3.6-flash',
'gemini-3.5-flash',
@ -1607,6 +1608,7 @@ describe('Google Model Tests', () => {
'gemini-3.1-pro-preview': 'gemini-3.1',
'gemini-3.1-pro-preview-customtools': 'gemini-3.1',
'gemini-3.1-flash-lite-preview': 'gemini-3.1-flash-lite',
'gemini-3.8-flash': 'gemini-3.8-flash',
'gemini-3.7-flash': 'gemini-3.7-flash',
'gemini-3.6-flash': 'gemini-3.6-flash',
'gemini-3.5-flash': 'gemini-3.5-flash',
@ -1761,8 +1763,24 @@ describe('Google Model Tests', () => {
);
});
it('should apply the introductory Flash rates to Gemini 3.6 and 3.7 Flash', () => {
for (const model of ['gemini-3.6-flash', 'gemini-3.7-flash']) {
it('should return correct rates for Gemini 3.8 Flash', () => {
const model = 'gemini-3.8-flash';
expect(getMultiplier({ model, tokenType: 'prompt', endpoint: EModelEndpoint.google })).toBe(
tokenValues['gemini-3.8-flash'].prompt,
);
expect(getMultiplier({ model, tokenType: 'completion', endpoint: EModelEndpoint.google })).toBe(
tokenValues['gemini-3.8-flash'].completion,
);
expect(getCacheMultiplier({ model, cacheType: 'write' })).toBe(
cacheTokenValues['gemini-3.8-flash'].write,
);
expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(
cacheTokenValues['gemini-3.8-flash'].read,
);
});
it('should apply the introductory Flash rates to Gemini 3.6, 3.7 and 3.8 Flash', () => {
for (const model of ['gemini-3.6-flash', 'gemini-3.7-flash', 'gemini-3.8-flash']) {
expect(getMultiplier({ model, tokenType: 'prompt', endpoint: EModelEndpoint.google })).toBe(
0.75,
);

View file

@ -223,9 +223,10 @@ export const tokenValues: Record<string, { prompt: number; completion: number }>
'gemini-3.1-flash-lite': { prompt: 0.25, completion: 1.5 },
'gemini-3.5-flash': { prompt: 1.5, completion: 9 },
'gemini-3.5-flash-lite': { prompt: 0.3, completion: 2.5 },
// Gemini 3.6/3.7 Flash introductory pricing through 2026-12-31; revert to { prompt: 1.5, completion: 7.5 } after.
// Gemini 3.6/3.7/3.8 Flash introductory pricing through 2026-12-31; revert to { prompt: 1.5, completion: 7.5 } after.
'gemini-3.6-flash': { prompt: 0.75, completion: 3.75 },
'gemini-3.7-flash': { prompt: 0.75, completion: 3.75 },
'gemini-3.8-flash': { prompt: 0.75, completion: 3.75 },
'gemini-pro-vision': { prompt: 0.5, completion: 1.5 },
grok: { prompt: 2.0, completion: 10.0 },
'grok-beta': { prompt: 5.0, completion: 15.0 },
@ -423,9 +424,10 @@ export const cacheTokenValues: Record<string, { write: number; read: number }> =
'gemini-3.5-flash': { write: 1.5, read: 0.15 },
// Gemini 3.5 Flash-Lite - cache write: $0.30/1M, cache read: $0.03/1M
'gemini-3.5-flash-lite': { write: 0.3, read: 0.03 },
// Gemini 3.6/3.7 Flash introductory pricing through 2026-12-31; revert to { write: 1.5, read: 0.15 } after.
// Gemini 3.6/3.7/3.8 Flash introductory pricing through 2026-12-31; revert to { write: 1.5, read: 0.15 } after.
'gemini-3.6-flash': { write: 0.75, read: 0.075 },
'gemini-3.7-flash': { write: 0.75, read: 0.075 },
'gemini-3.8-flash': { write: 0.75, read: 0.075 },
};
/**