mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-20 17:13:44 +00:00
⚡ feat: Add Gemini 3.7 Flash Support (#14818)
* ⚡ feat: Add Gemini 3.7 Flash Support Adds first-class support for Google's Gemini 3.7 Flash (`gemini-3.7-flash`) for both the Gemini API (AI Studio) and Google Cloud Gemini Enterprise Agent Platform, following the Gemini 3.6 Flash integration (#14369). - 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 handler so it inherits the existing strip of deprecated sampling params (temperature/topP/topK), rejected penalty params, and thinkingBudget, and defaults to `medium` thinking. - Generalize that handler's enumerated table from a [id, level] tuple to a rule object, so a model can also declare thinking levels it rejects. Gemini 3.7 Flash errors on `minimal` (which the Google endpoint offers in its thinkingLevel slider), so an explicit `minimal` is substituted with the nearest supported level, `low`. Explicit low/medium/high pass through unchanged. - Apply Google's introductory pricing ($0.75 in / $3.75 out / $0.075 cached, per 1M) to Gemini 3.7 Flash and correct Gemini 3.6 Flash to the same rates. Both revert to $1.50 / $7.50 / $0.15 on 2027-01-01; noted at both call sites. Resolves #14802 Ref: https://ai.google.dev/gemini-api/docs/models/gemini-3.7-flash Ref: https://ai.google.dev/gemini-api/docs/pricing * 📝 docs: Match the House Style for Promotional Rate Comments Align the Gemini 3.6/3.7 Flash introductory-pricing notes with the existing Sonnet 5 convention in the same file: one comment per group, naming the models and the exact values to restore, so the manual follow-up is unambiguous. No rate changes. * ⬆️ chore: Bump `@librechat/agents` to 3.4.7 for Gemini 3.7 Flash Prefill Unblocks this PR. `NO_PREFILL_GEMINI_MODELS` is model-enumerated in the agents SDK, so 3.4.6 does not know `gemini-3.7-flash` forbids a trailing `model`-role turn — editing an assistant reply and resubmitting would reach Google as a prefill and return HTTP 400 on a model this PR adds to the default list. 3.4.7 (danny-avila/agents#412, released via #413) adds it. Verified the published tarball: `3.4.6...3.4.7` touches only `dist/{cjs,esm}/llm/google/utils/common.*` — the prefill array and its comment. `dist/types` is byte-identical, so there is no API surface change. Raises the declared range in both workspaces alongside the lock. `^3.4.6` already permitted 3.4.7, but the fix is required rather than merely compatible, so the floor should say so.
This commit is contained in:
parent
5e464bc930
commit
24d111fde9
13 changed files with 235 additions and 54 deletions
|
|
@ -419,10 +419,10 @@ GOOGLE_KEY=user_provided
|
|||
# GOOGLE_AUTH_HEADER=true
|
||||
|
||||
# Gemini API (AI Studio)
|
||||
# GOOGLE_MODELS=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.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.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.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
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
"@azure/storage-blob": "^12.30.0",
|
||||
"@google/genai": "^2.8.0",
|
||||
"@keyv/redis": "^4.3.3",
|
||||
"@librechat/agents": "^3.4.6",
|
||||
"@librechat/agents": "^3.4.7",
|
||||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
|
|
|
|||
|
|
@ -356,6 +356,9 @@ describe('getModelMaxTokens', () => {
|
|||
expect(getModelMaxTokens('gemini-3.6-flash', EModelEndpoint.google)).toBe(
|
||||
maxTokensMap[EModelEndpoint.google]['gemini-3.6-flash'],
|
||||
);
|
||||
expect(getModelMaxTokens('gemini-3.7-flash', EModelEndpoint.google)).toBe(
|
||||
maxTokensMap[EModelEndpoint.google]['gemini-3.7-flash'],
|
||||
);
|
||||
expect(getModelMaxTokens('gemini-2.5-pro', EModelEndpoint.google)).toBe(
|
||||
maxTokensMap[EModelEndpoint.google]['gemini-2.5-pro'],
|
||||
);
|
||||
|
|
|
|||
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -63,7 +63,7 @@
|
|||
"@azure/storage-blob": "^12.30.0",
|
||||
"@google/genai": "^2.8.0",
|
||||
"@keyv/redis": "^4.3.3",
|
||||
"@librechat/agents": "^3.4.6",
|
||||
"@librechat/agents": "^3.4.7",
|
||||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
|
|
@ -10628,9 +10628,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@librechat/agents": {
|
||||
"version": "3.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.4.6.tgz",
|
||||
"integrity": "sha512-ZgFNhOGZpFUGZVh+K+E18SzV53iXbmnhN8Z+B/5KbIPwrZ3WLXw9whJ2xCpVpDk0VBGy9oZu8SSPD4cl0eUiQQ==",
|
||||
"version": "3.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.4.7.tgz",
|
||||
"integrity": "sha512-0JPTM/rYjudtl6uhVbAMDyq1vxa40oeacVLWuEF+S611hwpO9kuXho+X4+XbTrRAGTzkGJrYBsvc/ddzxtEBAA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.115.0",
|
||||
|
|
@ -42849,7 +42849,7 @@
|
|||
"@azure/storage-blob": "^12.30.0",
|
||||
"@google/genai": "^2.8.0",
|
||||
"@keyv/redis": "^4.3.3",
|
||||
"@librechat/agents": "^3.4.6",
|
||||
"@librechat/agents": "^3.4.7",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@
|
|||
"@azure/storage-blob": "^12.30.0",
|
||||
"@google/genai": "^2.8.0",
|
||||
"@keyv/redis": "^4.3.3",
|
||||
"@librechat/agents": "^3.4.6",
|
||||
"@librechat/agents": "^3.4.7",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
|
|
|
|||
|
|
@ -964,6 +964,111 @@ describe('getGoogleConfig', () => {
|
|||
expect(result.llmConfig).not.toHaveProperty('thinking_budget');
|
||||
});
|
||||
|
||||
it('should default Gemini 3.7 Flash to medium thinkingLevel', () => {
|
||||
const credentials = {
|
||||
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
|
||||
};
|
||||
|
||||
const result = getGoogleConfig(credentials, {
|
||||
modelOptions: {
|
||||
model: 'gemini-3.7-flash',
|
||||
},
|
||||
});
|
||||
|
||||
expect((result.llmConfig as Record<string, unknown>).thinkingConfig).toMatchObject({
|
||||
includeThoughts: true,
|
||||
thinkingLevel: 'MEDIUM',
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove legacy sampling params for Gemini 3.7 Flash', () => {
|
||||
const credentials = {
|
||||
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
|
||||
};
|
||||
|
||||
const modelOptions = {
|
||||
model: 'gemini-3.7-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.7 Flash thinkingLevel "%s"', (level, expected) => {
|
||||
const credentials = {
|
||||
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
|
||||
};
|
||||
|
||||
const result = getGoogleConfig(credentials, {
|
||||
modelOptions: {
|
||||
model: 'gemini-3.7-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.7 Flash', () => {
|
||||
const credentials = {
|
||||
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
|
||||
};
|
||||
|
||||
const result = getGoogleConfig(credentials, {
|
||||
modelOptions: {
|
||||
model: 'gemini-3.7-flash',
|
||||
thinkingLevel: ThinkingLevel.minimal,
|
||||
},
|
||||
});
|
||||
|
||||
expect((result.llmConfig as Record<string, unknown>).thinkingConfig).toMatchObject({
|
||||
includeThoughts: true,
|
||||
thinkingLevel: 'LOW',
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply Gemini 3.7 Flash handling to versioned aliases', () => {
|
||||
const credentials = {
|
||||
[AuthKeys.GOOGLE_API_KEY]: 'test-api-key',
|
||||
};
|
||||
|
||||
const result = getGoogleConfig(credentials, {
|
||||
modelOptions: {
|
||||
model: 'models/gemini-3.7-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',
|
||||
|
|
|
|||
|
|
@ -13,6 +13,15 @@ type GoogleThinkingConfig = {
|
|||
thinkingLevel?: GoogleThinkingLevel;
|
||||
};
|
||||
|
||||
type GeminiFlashThinkingRule = {
|
||||
/** Model id, matched exactly or as a `${id}-` prefix for versioned aliases */
|
||||
id: string;
|
||||
/** Thinking level applied when the request doesn't set one */
|
||||
default: GoogleThinkingLevel;
|
||||
/** Levels the model rejects, mapped to the nearest level it accepts */
|
||||
substitutions?: Partial<Record<GoogleThinkingLevel, GoogleThinkingLevel>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gemini Flash models (3.5+) that drop the deprecated sampling parameters
|
||||
* (`temperature`/`topP`/`topK`) and `thinkingBudget` in favor of the qualitative
|
||||
|
|
@ -22,12 +31,17 @@ type GoogleThinkingConfig = {
|
|||
* default thinking level when the request doesn't set one. Ordered
|
||||
* 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.
|
||||
* @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
|
||||
*/
|
||||
const geminiFlashThinkingDefaults: ReadonlyArray<readonly [string, GoogleThinkingLevel]> = [
|
||||
['gemini-3.6-flash', 'MEDIUM'],
|
||||
['gemini-3.5-flash-lite', 'MINIMAL'],
|
||||
['gemini-3.5-flash', 'MEDIUM'],
|
||||
const geminiFlashThinkingRules: ReadonlyArray<GeminiFlashThinkingRule> = [
|
||||
{ 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' },
|
||||
{ id: 'gemini-3.5-flash', default: 'MEDIUM' },
|
||||
];
|
||||
|
||||
const geminiFlashLegacyParams = [
|
||||
|
|
@ -149,12 +163,12 @@ function normalizeGoogleThinkingLevel(value: unknown): GoogleThinkingLevel | und
|
|||
return normalized;
|
||||
}
|
||||
|
||||
function getGeminiFlashDefaultThinkingLevel(model: string): GoogleThinkingLevel | undefined {
|
||||
function getGeminiFlashThinkingRule(model: string): GeminiFlashThinkingRule | undefined {
|
||||
const normalized = model.toLowerCase();
|
||||
const modelId = normalized.split('/').pop() ?? normalized;
|
||||
for (const [id, level] of geminiFlashThinkingDefaults) {
|
||||
if (modelId === id || modelId.startsWith(`${id}-`)) {
|
||||
return level;
|
||||
for (const rule of geminiFlashThinkingRules) {
|
||||
if (modelId === rule.id || modelId.startsWith(`${rule.id}-`)) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
|
|
@ -173,7 +187,7 @@ export function stripGeminiFlashBlockedParams<T extends Record<string, unknown>
|
|||
params: T,
|
||||
model: string | undefined,
|
||||
): T {
|
||||
if (params == null || getGeminiFlashDefaultThinkingLevel(model ?? '') == null) {
|
||||
if (params == null || getGeminiFlashThinkingRule(model ?? '') == null) {
|
||||
return params;
|
||||
}
|
||||
const sanitized = { ...params };
|
||||
|
|
@ -233,8 +247,8 @@ function applyGeminiFlashOverrides({
|
|||
if (typeof model !== 'string') {
|
||||
return;
|
||||
}
|
||||
const defaultThinkingLevel = getGeminiFlashDefaultThinkingLevel(model);
|
||||
if (!defaultThinkingLevel) {
|
||||
const thinkingRule = getGeminiFlashThinkingRule(model);
|
||||
if (!thinkingRule) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -268,8 +282,14 @@ function applyGeminiFlashOverrides({
|
|||
thinkingConfig.includeThoughts = true;
|
||||
}
|
||||
|
||||
if (!shouldDropThinkingLevel && !thinkingConfig.thinkingLevel) {
|
||||
thinkingConfig.thinkingLevel = defaultThinkingLevel;
|
||||
if (!shouldDropThinkingLevel) {
|
||||
if (!thinkingConfig.thinkingLevel) {
|
||||
thinkingConfig.thinkingLevel = thinkingRule.default;
|
||||
}
|
||||
const substitute = thinkingRule.substitutions?.[thinkingConfig.thinkingLevel];
|
||||
if (substitute) {
|
||||
thinkingConfig.thinkingLevel = substitute;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(thinkingConfig).length > 0) {
|
||||
|
|
|
|||
|
|
@ -153,37 +153,40 @@ describe('getOpenAIConfig - Google Compatibility', () => {
|
|||
expect(result.tools).toEqual([]);
|
||||
});
|
||||
|
||||
it('should strip Flash-blocked addParams so the transform cannot re-add them', () => {
|
||||
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
|
||||
const endpoint = 'Gemini (Custom)';
|
||||
const options = {
|
||||
modelOptions: {
|
||||
model: 'gemini-3.6-flash',
|
||||
},
|
||||
customParams: {
|
||||
defaultParamsEndpoint: 'google',
|
||||
},
|
||||
addParams: {
|
||||
temperature: 0.8,
|
||||
topP: 0.95,
|
||||
topK: 40,
|
||||
presencePenalty: 0.5,
|
||||
frequencyPenalty: 0.5,
|
||||
maxOutputTokens: 8192, // Supported Google param, should survive
|
||||
},
|
||||
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
|
||||
};
|
||||
it.each(['gemini-3.6-flash', 'gemini-3.7-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' });
|
||||
const endpoint = 'Gemini (Custom)';
|
||||
const options = {
|
||||
modelOptions: {
|
||||
model,
|
||||
},
|
||||
customParams: {
|
||||
defaultParamsEndpoint: 'google',
|
||||
},
|
||||
addParams: {
|
||||
temperature: 0.8,
|
||||
topP: 0.95,
|
||||
topK: 40,
|
||||
presencePenalty: 0.5,
|
||||
frequencyPenalty: 0.5,
|
||||
maxOutputTokens: 8192, // Supported Google param, should survive
|
||||
},
|
||||
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
|
||||
};
|
||||
|
||||
const result = getOpenAIConfig(apiKey, options, endpoint);
|
||||
const result = getOpenAIConfig(apiKey, options, endpoint);
|
||||
|
||||
expect(result.llmConfig).not.toHaveProperty('temperature');
|
||||
expect(result.llmConfig).not.toHaveProperty('topP');
|
||||
expect(result.llmConfig).not.toHaveProperty('presencePenalty');
|
||||
expect(result.llmConfig).not.toHaveProperty('frequencyPenalty');
|
||||
expect(result.llmConfig.modelKwargs ?? {}).not.toHaveProperty('topK');
|
||||
expect(result.llmConfig.modelKwargs ?? {}).not.toHaveProperty('presencePenalty');
|
||||
expect(result.llmConfig.modelKwargs).toMatchObject({ maxOutputTokens: 8192 });
|
||||
});
|
||||
expect(result.llmConfig).not.toHaveProperty('temperature');
|
||||
expect(result.llmConfig).not.toHaveProperty('topP');
|
||||
expect(result.llmConfig).not.toHaveProperty('presencePenalty');
|
||||
expect(result.llmConfig).not.toHaveProperty('frequencyPenalty');
|
||||
expect(result.llmConfig.modelKwargs ?? {}).not.toHaveProperty('topK');
|
||||
expect(result.llmConfig.modelKwargs ?? {}).not.toHaveProperty('presencePenalty');
|
||||
expect(result.llmConfig.modelKwargs).toMatchObject({ maxOutputTokens: 8192 });
|
||||
},
|
||||
);
|
||||
|
||||
it('should drop Google native params with dropParams', () => {
|
||||
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
|
||||
|
|
|
|||
|
|
@ -47,3 +47,16 @@ describe('gpt-5.6 tiers', () => {
|
|||
expect(getModelMaxTokens('gpt-5', EModelEndpoint.openAI)).toBe(400000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Gemini 3.7 Flash', () => {
|
||||
it('resolves the 1,048,576-token context window', () => {
|
||||
expect(getModelMaxTokens('gemini-3.7-flash', EModelEndpoint.google)).toBe(1048576);
|
||||
});
|
||||
|
||||
it('matches the longest key over the shorter gemini-3 pattern for aliases', () => {
|
||||
expect(getModelMaxTokens('models/gemini-3.7-flash-latest', EModelEndpoint.google)).toBe(
|
||||
1048576,
|
||||
);
|
||||
expect(getModelMaxTokens('gemini-3', EModelEndpoint.google)).toBe(1000000);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ const googleModels = {
|
|||
'gemini-3.5-flash': 1048576,
|
||||
'gemini-3.5-flash-lite': 1048576,
|
||||
'gemini-3.6-flash': 1048576,
|
||||
'gemini-3.7-flash': 1048576,
|
||||
};
|
||||
|
||||
const anthropicModels = {
|
||||
|
|
|
|||
|
|
@ -2271,6 +2271,8 @@ export const defaultModels = {
|
|||
[EModelEndpoint.assistants]: [...sharedOpenAIModels, 'chatgpt-4o-latest'],
|
||||
[EModelEndpoint.agents]: sharedOpenAIModels, // TODO: Add agent models (agentsModels)
|
||||
[EModelEndpoint.google]: [
|
||||
// Gemini 3.7 Models
|
||||
'gemini-3.7-flash',
|
||||
// Gemini 3.6 Models
|
||||
'gemini-3.6-flash',
|
||||
// Gemini 3.5 Models
|
||||
|
|
|
|||
|
|
@ -1526,6 +1526,7 @@ describe('Google Model Tests', () => {
|
|||
'gemini-3.1-pro-preview',
|
||||
'gemini-3.1-pro-preview-customtools',
|
||||
'gemini-3.1-flash-lite-preview',
|
||||
'gemini-3.7-flash',
|
||||
'gemini-3.6-flash',
|
||||
'gemini-3.5-flash',
|
||||
'gemini-3.5-flash-lite',
|
||||
|
|
@ -1576,6 +1577,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.7-flash': 'gemini-3.7-flash',
|
||||
'gemini-3.6-flash': 'gemini-3.6-flash',
|
||||
'gemini-3.5-flash': 'gemini-3.5-flash',
|
||||
'gemini-3.5-flash-lite': 'gemini-3.5-flash-lite',
|
||||
|
|
@ -1713,6 +1715,35 @@ describe('Google Model Tests', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should return correct rates for Gemini 3.7 Flash', () => {
|
||||
const model = 'gemini-3.7-flash';
|
||||
expect(getMultiplier({ model, tokenType: 'prompt', endpoint: EModelEndpoint.google })).toBe(
|
||||
tokenValues['gemini-3.7-flash'].prompt,
|
||||
);
|
||||
expect(getMultiplier({ model, tokenType: 'completion', endpoint: EModelEndpoint.google })).toBe(
|
||||
tokenValues['gemini-3.7-flash'].completion,
|
||||
);
|
||||
expect(getCacheMultiplier({ model, cacheType: 'write' })).toBe(
|
||||
cacheTokenValues['gemini-3.7-flash'].write,
|
||||
);
|
||||
expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(
|
||||
cacheTokenValues['gemini-3.7-flash'].read,
|
||||
);
|
||||
});
|
||||
|
||||
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']) {
|
||||
expect(getMultiplier({ model, tokenType: 'prompt', endpoint: EModelEndpoint.google })).toBe(
|
||||
0.75,
|
||||
);
|
||||
expect(
|
||||
getMultiplier({ model, tokenType: 'completion', endpoint: EModelEndpoint.google }),
|
||||
).toBe(3.75);
|
||||
expect(getCacheMultiplier({ model, cacheType: 'write' })).toBe(0.75);
|
||||
expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(0.075);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return correct rates for Gemini 3.5 Flash-Lite', () => {
|
||||
const model = 'gemini-3.5-flash-lite';
|
||||
expect(getMultiplier({ model, tokenType: 'prompt', endpoint: EModelEndpoint.google })).toBe(
|
||||
|
|
|
|||
|
|
@ -205,7 +205,9 @@ 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-flash': { prompt: 1.5, completion: 7.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-flash': { prompt: 0.75, completion: 3.75 },
|
||||
'gemini-3.7-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 },
|
||||
|
|
@ -374,8 +376,9 @@ 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 Flash - cache write: $1.50/1M, cache read: $0.15/1M
|
||||
'gemini-3.6-flash': { write: 1.5, read: 0.15 },
|
||||
// Gemini 3.6/3.7 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 },
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue