2024-12-28 17:15:03 -05:00
|
|
|
const { Providers } = require('@librechat/agents');
|
|
|
|
|
const { AuthKeys } = require('librechat-data-provider');
|
|
|
|
|
|
|
|
|
|
// Example internal constant from your code
|
|
|
|
|
const EXCLUDED_GENAI_MODELS = /gemini-(?:1\.0|1-0|pro)/;
|
|
|
|
|
|
2025-01-17 12:55:48 -05:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {boolean} isGemini2
|
|
|
|
|
* @returns {Array<{category: string, threshold: string}>}
|
|
|
|
|
*/
|
|
|
|
|
function getSafetySettings(isGemini2) {
|
|
|
|
|
const mapThreshold = (value) => {
|
|
|
|
|
if (isGemini2 && value === 'BLOCK_NONE') {
|
|
|
|
|
return 'OFF';
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-28 17:15:03 -05:00
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
2025-01-17 12:55:48 -05:00
|
|
|
threshold: mapThreshold(
|
|
|
|
|
process.env.GOOGLE_SAFETY_SEXUALLY_EXPLICIT || 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
|
|
|
|
),
|
2024-12-28 17:15:03 -05:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
category: 'HARM_CATEGORY_HATE_SPEECH',
|
2025-01-17 12:55:48 -05:00
|
|
|
threshold: mapThreshold(
|
|
|
|
|
process.env.GOOGLE_SAFETY_HATE_SPEECH || 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
|
|
|
|
),
|
2024-12-28 17:15:03 -05:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
category: 'HARM_CATEGORY_HARASSMENT',
|
2025-01-17 12:55:48 -05:00
|
|
|
threshold: mapThreshold(
|
|
|
|
|
process.env.GOOGLE_SAFETY_HARASSMENT || 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
|
|
|
|
),
|
2024-12-28 17:15:03 -05:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
|
2025-01-17 12:55:48 -05:00
|
|
|
threshold: mapThreshold(
|
|
|
|
|
process.env.GOOGLE_SAFETY_DANGEROUS_CONTENT || 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
|
|
|
|
|
),
|
2024-12-28 17:15:03 -05:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
category: 'HARM_CATEGORY_CIVIC_INTEGRITY',
|
2025-01-17 12:55:48 -05:00
|
|
|
threshold: mapThreshold(process.env.GOOGLE_SAFETY_CIVIC_INTEGRITY || 'BLOCK_NONE'),
|
2024-12-28 17:15:03 -05:00
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Replicates core logic from GoogleClient's constructor and setOptions, plus client determination.
|
|
|
|
|
* Returns an object with the provider label and the final options that would be passed to createLLM.
|
|
|
|
|
*
|
|
|
|
|
* @param {string | object} credentials - Either a JSON string or an object containing Google keys
|
|
|
|
|
* @param {object} [options={}] - The same shape as the "GoogleClient" constructor options
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
function getLLMConfig(credentials, options = {}) {
|
|
|
|
|
// 1. Parse credentials
|
|
|
|
|
let creds = {};
|
|
|
|
|
if (typeof credentials === 'string') {
|
|
|
|
|
try {
|
|
|
|
|
creds = JSON.parse(credentials);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
throw new Error(`Error parsing string credentials: ${err.message}`);
|
|
|
|
|
}
|
|
|
|
|
} else if (credentials && typeof credentials === 'object') {
|
|
|
|
|
creds = credentials;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract from credentials
|
|
|
|
|
const serviceKeyRaw = creds[AuthKeys.GOOGLE_SERVICE_KEY] ?? {};
|
|
|
|
|
const serviceKey =
|
|
|
|
|
typeof serviceKeyRaw === 'string' ? JSON.parse(serviceKeyRaw) : serviceKeyRaw ?? {};
|
|
|
|
|
|
|
|
|
|
const project_id = serviceKey?.project_id ?? null;
|
|
|
|
|
const apiKey = creds[AuthKeys.GOOGLE_API_KEY] ?? null;
|
|
|
|
|
|
|
|
|
|
const reverseProxyUrl = options.reverseProxyUrl;
|
|
|
|
|
const authHeader = options.authHeader;
|
|
|
|
|
|
|
|
|
|
/** @type {GoogleClientOptions | VertexAIClientOptions} */
|
|
|
|
|
let llmConfig = {
|
|
|
|
|
...(options.modelOptions || {}),
|
|
|
|
|
maxRetries: 2,
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-22 07:50:09 -05:00
|
|
|
/** Used only for Safety Settings */
|
|
|
|
|
const isGemini2 = llmConfig.model.includes('gemini-2.0') && !llmConfig.model.includes('thinking');
|
2024-12-28 17:15:03 -05:00
|
|
|
const isGenerativeModel = llmConfig.model.includes('gemini');
|
|
|
|
|
const isChatModel = !isGenerativeModel && llmConfig.model.includes('chat');
|
|
|
|
|
const isTextModel = !isGenerativeModel && !isChatModel && /code|text/.test(llmConfig.model);
|
|
|
|
|
|
2025-01-17 12:55:48 -05:00
|
|
|
llmConfig.safetySettings = getSafetySettings(isGemini2);
|
|
|
|
|
|
2024-12-28 17:15:03 -05:00
|
|
|
let provider;
|
|
|
|
|
|
|
|
|
|
if (project_id && isTextModel) {
|
|
|
|
|
provider = Providers.VERTEXAI;
|
|
|
|
|
} else if (project_id && isChatModel) {
|
|
|
|
|
provider = Providers.VERTEXAI;
|
|
|
|
|
} else if (project_id) {
|
|
|
|
|
provider = Providers.VERTEXAI;
|
|
|
|
|
} else if (!EXCLUDED_GENAI_MODELS.test(llmConfig.model)) {
|
|
|
|
|
provider = Providers.GOOGLE;
|
|
|
|
|
} else {
|
|
|
|
|
provider = Providers.GOOGLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have a GCP project => Vertex AI
|
|
|
|
|
if (project_id && provider === Providers.VERTEXAI) {
|
|
|
|
|
/** @type {VertexAIClientOptions['authOptions']} */
|
|
|
|
|
llmConfig.authOptions = {
|
|
|
|
|
credentials: { ...serviceKey },
|
|
|
|
|
projectId: project_id,
|
|
|
|
|
};
|
|
|
|
|
llmConfig.location = process.env.GOOGLE_LOC || 'us-central1';
|
|
|
|
|
} else if (apiKey && provider === Providers.GOOGLE) {
|
|
|
|
|
llmConfig.apiKey = apiKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
let legacyOptions = {};
|
|
|
|
|
// Filter out any "examples" that are empty
|
|
|
|
|
legacyOptions.examples = (legacyOptions.examples ?? [])
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
.filter((obj) => obj?.input?.content !== '' && obj?.output?.content !== '');
|
|
|
|
|
|
|
|
|
|
// If user has "examples" from legacyOptions, push them onto llmConfig
|
|
|
|
|
if (legacyOptions.examples?.length) {
|
|
|
|
|
llmConfig.examples = legacyOptions.examples.map((ex) => {
|
|
|
|
|
const { input, output } = ex;
|
|
|
|
|
if (!input?.content || !output?.content) {return undefined;}
|
|
|
|
|
return {
|
|
|
|
|
input: new HumanMessage(input.content),
|
|
|
|
|
output: new AIMessage(output.content),
|
|
|
|
|
};
|
|
|
|
|
}).filter(Boolean);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (reverseProxyUrl) {
|
|
|
|
|
llmConfig.baseUrl = reverseProxyUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (authHeader) {
|
|
|
|
|
/**
|
|
|
|
|
* NOTE: NOT SUPPORTED BY LANGCHAIN GENAI CLIENT,
|
|
|
|
|
* REQUIRES PR IN https://github.com/langchain-ai/langchainjs
|
|
|
|
|
*/
|
|
|
|
|
llmConfig.customHeaders = {
|
|
|
|
|
Authorization: `Bearer ${apiKey}`,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the final shape
|
|
|
|
|
return {
|
|
|
|
|
/** @type {Providers.GOOGLE | Providers.VERTEXAI} */
|
|
|
|
|
provider,
|
|
|
|
|
/** @type {GoogleClientOptions | VertexAIClientOptions} */
|
|
|
|
|
llmConfig,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
getLLMConfig,
|
|
|
|
|
};
|