diff --git a/.env.example b/.env.example index 1ad728ac8c..4255bc2256 100644 --- a/.env.example +++ b/.env.example @@ -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 diff --git a/api/package.json b/api/package.json index c8a2b20c7e..2a45e638c4 100644 --- a/api/package.json +++ b/api/package.json @@ -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", diff --git a/api/utils/tokens.spec.js b/api/utils/tokens.spec.js index 338c20ad89..8ac3f7e908 100644 --- a/api/utils/tokens.spec.js +++ b/api/utils/tokens.spec.js @@ -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'], ); diff --git a/package-lock.json b/package-lock.json index 2496056d3d..3701b90dd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/packages/api/package.json b/packages/api/package.json index 11721140b1..30a7024c92 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -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", diff --git a/packages/api/src/endpoints/google/llm.spec.ts b/packages/api/src/endpoints/google/llm.spec.ts index 9a5daa5a32..8b323b824f 100644 --- a/packages/api/src/endpoints/google/llm.spec.ts +++ b/packages/api/src/endpoints/google/llm.spec.ts @@ -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).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).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).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).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', diff --git a/packages/api/src/endpoints/google/llm.ts b/packages/api/src/endpoints/google/llm.ts index d793c16788..0c3c6daa59 100644 --- a/packages/api/src/endpoints/google/llm.ts +++ b/packages/api/src/endpoints/google/llm.ts @@ -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 = [ + { 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' }, diff --git a/packages/api/src/endpoints/openai/config.google.spec.ts b/packages/api/src/endpoints/openai/config.google.spec.ts index 818cf85587..1e94c54afe 100644 --- a/packages/api/src/endpoints/openai/config.google.spec.ts +++ b/packages/api/src/endpoints/openai/config.google.spec.ts @@ -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' }); diff --git a/packages/api/src/utils/tokens.spec.ts b/packages/api/src/utils/tokens.spec.ts index 9c8eb9de9e..aac009bce3 100644 --- a/packages/api/src/utils/tokens.spec.ts +++ b/packages/api/src/utils/tokens.spec.ts @@ -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); diff --git a/packages/api/src/utils/tokens.ts b/packages/api/src/utils/tokens.ts index 3166c0584f..951f44a020 100644 --- a/packages/api/src/utils/tokens.ts +++ b/packages/api/src/utils/tokens.ts @@ -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 = { diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index 4e9e33822f..bfb1b67298 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -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 diff --git a/packages/data-schemas/src/methods/tx.spec.ts b/packages/data-schemas/src/methods/tx.spec.ts index 5344927137..676484c963 100644 --- a/packages/data-schemas/src/methods/tx.spec.ts +++ b/packages/data-schemas/src/methods/tx.spec.ts @@ -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, ); diff --git a/packages/data-schemas/src/methods/tx.ts b/packages/data-schemas/src/methods/tx.ts index b8a6392262..d2e3548807 100644 --- a/packages/data-schemas/src/methods/tx.ts +++ b/packages/data-schemas/src/methods/tx.ts @@ -223,9 +223,10 @@ export const tokenValues: Record '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 = '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 }, }; /**