LibreChat/packages/api/src/endpoints/openai/config.google.spec.ts
Danny Avila 754b495fb8
🔑 fix: Gemini Custom Endpoint Auth. for OAI-Compatible API (#10806)
* 🔧 fix: Gemini as Custom Endpoint Auth. Error for OAI-compatible API

* refactor: Google Compatibility in OpenAI Config

- Added a test to ensure `googleSearch` is filtered out when `web_search` is only present in `modelOptions`, not in `addParams` or `defaultParams`.
- Updated `transformToOpenAIConfig` to preserve `googleSearch` tools if `web_search` is explicitly enabled via `addParams` or `defaultParams`.
- Refactored the filtering logic for Google-specific tools to accommodate the new behavior.
2025-12-04 14:09:42 -05:00

407 lines
14 KiB
TypeScript

import { getOpenAIConfig } from './config';
describe('getOpenAIConfig - Google Compatibility', () => {
describe('Google via Custom Endpoint', () => {
describe('Web Search Support via addParams', () => {
it('should enable googleSearch tool when web_search: true in addParams', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const endpoint = 'Gemini (Custom)';
const options = {
modelOptions: {
model: 'gemini-2.0-flash-exp',
},
customParams: {
defaultParamsEndpoint: 'google',
},
addParams: {
web_search: true,
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
};
const result = getOpenAIConfig(apiKey, options, endpoint);
expect(result.tools).toEqual([{ googleSearch: {} }]);
expect(result.llmConfig).toMatchObject({
model: 'gemini-2.0-flash-exp',
});
});
it('should disable googleSearch tool when web_search: false in addParams', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const endpoint = 'Gemini (Custom)';
const options = {
modelOptions: {
model: 'gemini-2.0-flash-exp',
web_search: true, // Should be overridden by addParams
},
customParams: {
defaultParamsEndpoint: 'google',
},
addParams: {
web_search: false,
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
};
const result = getOpenAIConfig(apiKey, options, endpoint);
expect(result.tools).toEqual([]);
});
it('should disable googleSearch when in dropParams', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const endpoint = 'Gemini (Custom)';
const options = {
modelOptions: {
model: 'gemini-2.0-flash-exp',
web_search: true,
},
customParams: {
defaultParamsEndpoint: 'google',
},
dropParams: ['web_search'],
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
};
const result = getOpenAIConfig(apiKey, options, endpoint);
expect(result.tools).toEqual([]);
});
it('should filter out googleSearch when web_search is only in modelOptions (not explicitly in addParams/defaultParams)', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const endpoint = 'Gemini (Custom)';
const options = {
modelOptions: {
model: 'gemini-2.0-flash-exp',
web_search: true,
},
customParams: {
defaultParamsEndpoint: 'google',
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
};
const result = getOpenAIConfig(apiKey, options, endpoint);
/** googleSearch should be filtered out since web_search was not explicitly added via addParams or defaultParams */
expect(result.tools).toEqual([]);
});
it('should handle web_search with mixed Google and OpenAI params in addParams', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const endpoint = 'Gemini (Custom)';
const options = {
modelOptions: {
model: 'gemini-2.0-flash-exp',
},
customParams: {
defaultParamsEndpoint: 'google',
},
addParams: {
web_search: true,
temperature: 0.8, // Shared param (both Google and OpenAI)
topK: 40, // Google-only param, goes to modelKwargs
frequencyPenalty: 0.5, // Known OpenAI param, goes to top level
customUnknown: 'test', // Unknown param, goes to modelKwargs
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
};
const result = getOpenAIConfig(apiKey, options, endpoint);
expect(result.tools).toEqual([{ googleSearch: {} }]);
expect(result.llmConfig.temperature).toBe(0.8); // Shared param at top level
expect(result.llmConfig.frequencyPenalty).toBe(0.5); // Known OpenAI param at top level
expect(result.llmConfig.modelKwargs).toMatchObject({
topK: 40, // Google-specific in modelKwargs
customUnknown: 'test', // Unknown param in modelKwargs
});
});
it('should handle Google native params in addParams without web_search', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const endpoint = 'Gemini (Custom)';
const options = {
modelOptions: {
model: 'gemini-2.0-flash-exp',
},
customParams: {
defaultParamsEndpoint: 'google',
},
addParams: {
temperature: 0.9, // Shared param (both Google and OpenAI)
topP: 0.95, // Shared param (both Google and OpenAI)
topK: 50, // Google-only, goes to modelKwargs
maxOutputTokens: 8192, // Google-only, goes to modelKwargs
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
};
const result = getOpenAIConfig(apiKey, options, endpoint);
expect(result.llmConfig).toMatchObject({
model: 'gemini-2.0-flash-exp',
temperature: 0.9, // Shared params at top level
topP: 0.95,
});
expect(result.llmConfig.modelKwargs).toMatchObject({
topK: 50, // Google-specific in modelKwargs
maxOutputTokens: 8192,
});
expect(result.tools).toEqual([]);
});
it('should drop Google native params with dropParams', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const endpoint = 'Gemini (Custom)';
const options = {
modelOptions: {
model: 'gemini-2.0-flash-exp',
temperature: 0.7,
topK: 40,
topP: 0.9,
},
customParams: {
defaultParamsEndpoint: 'google',
},
dropParams: ['topK', 'topP'],
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
};
const result = getOpenAIConfig(apiKey, options, endpoint);
expect(result.llmConfig.temperature).toBe(0.7);
expect((result.llmConfig as Record<string, unknown>).topK).toBeUndefined();
expect(result.llmConfig.topP).toBeUndefined();
});
it('should handle both addParams and dropParams for Google', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const endpoint = 'Gemini (Custom)';
const options = {
modelOptions: {
model: 'gemini-2.0-flash-exp',
topK: 30, // Will be dropped
},
customParams: {
defaultParamsEndpoint: 'google',
},
addParams: {
web_search: true,
temperature: 0.8, // Shared param
maxOutputTokens: 4096, // Google-only param
},
dropParams: ['topK'],
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
};
const result = getOpenAIConfig(apiKey, options, endpoint);
expect(result.tools).toEqual([{ googleSearch: {} }]);
expect(result.llmConfig).toMatchObject({
model: 'gemini-2.0-flash-exp',
temperature: 0.8,
});
expect(result.llmConfig.modelKwargs).toMatchObject({
maxOutputTokens: 4096, // Google-specific in modelKwargs
});
expect((result.llmConfig as Record<string, unknown>).topK).toBeUndefined();
// Verify topK is not in modelKwargs either
expect(result.llmConfig.modelKwargs?.topK).toBeUndefined();
});
});
describe('defaultParams Support via customParams', () => {
it('should apply defaultParams when fields are undefined', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
},
customParams: {
defaultParamsEndpoint: 'google',
paramDefinitions: [
{ key: 'temperature', default: 0.6 },
{ key: 'topK', default: 40 },
],
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.llmConfig.temperature).toBe(0.6);
expect(result.llmConfig.modelKwargs?.topK).toBe(40);
});
it('should not override existing modelOptions with defaultParams', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
temperature: 0.9,
},
customParams: {
defaultParamsEndpoint: 'google',
paramDefinitions: [
{ key: 'temperature', default: 0.5 },
{ key: 'topK', default: 40 },
],
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.llmConfig.temperature).toBe(0.9);
expect(result.llmConfig.modelKwargs?.topK).toBe(40);
});
it('should allow addParams to override defaultParams', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
},
customParams: {
defaultParamsEndpoint: 'google',
paramDefinitions: [
{ key: 'temperature', default: 0.5 },
{ key: 'topK', default: 30 },
],
},
addParams: {
temperature: 0.8,
topK: 50,
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.llmConfig.temperature).toBe(0.8);
expect(result.llmConfig.modelKwargs?.topK).toBe(50);
});
it('should handle defaultParams with web_search', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
},
customParams: {
defaultParamsEndpoint: 'google',
paramDefinitions: [{ key: 'web_search', default: true }],
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.tools).toEqual([{ googleSearch: {} }]);
});
it('should allow addParams to override defaultParams web_search', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
},
customParams: {
defaultParamsEndpoint: 'google',
paramDefinitions: [{ key: 'web_search', default: true }],
},
addParams: {
web_search: false,
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.tools).toEqual([]);
});
it('should handle dropParams overriding defaultParams', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
},
customParams: {
defaultParamsEndpoint: 'google',
paramDefinitions: [
{ key: 'temperature', default: 0.7 },
{ key: 'topK', default: 40 },
{ key: 'web_search', default: true },
],
},
dropParams: ['topK', 'web_search'],
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.llmConfig.temperature).toBe(0.7);
expect(result.llmConfig.modelKwargs?.topK).toBeUndefined();
expect(result.tools).toEqual([]);
});
it('should preserve order: defaultParams < addParams < modelOptions', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
temperature: 0.9,
},
customParams: {
defaultParamsEndpoint: 'google',
paramDefinitions: [
{ key: 'temperature', default: 0.3 },
{ key: 'topP', default: 0.5 },
{ key: 'topK', default: 20 },
],
},
addParams: {
topP: 0.8,
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.llmConfig.temperature).toBe(0.9);
expect(result.llmConfig.topP).toBe(0.8);
expect(result.llmConfig.modelKwargs?.topK).toBe(20);
});
it('should handle empty paramDefinitions', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
temperature: 0.8,
},
customParams: {
defaultParamsEndpoint: 'google',
paramDefinitions: [],
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.llmConfig.temperature).toBe(0.8);
});
it('should handle missing paramDefinitions', () => {
const apiKey = JSON.stringify({ GOOGLE_API_KEY: 'test-google-key' });
const result = getOpenAIConfig(apiKey, {
modelOptions: {
model: 'gemini-2.0-flash-exp',
temperature: 0.8,
},
customParams: {
defaultParamsEndpoint: 'google',
},
reverseProxyUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
});
expect(result.llmConfig.temperature).toBe(0.8);
});
});
});
});