diff --git a/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx b/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx index 8cb513f3..d2846704 100644 --- a/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx +++ b/frontend/src/components/LLMSelection/GeminiLLMOptions/index.jsx @@ -19,25 +19,47 @@ export default function GeminiLLMOptions({ settings }) { {!settings?.credentialsOnly && ( -
- - -
+ <> +
+ + +
+
+ + +
+ )} diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index a5bb6a23..70913fd9 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -354,6 +354,8 @@ const SystemSettings = { // Gemini Keys GeminiLLMApiKey: !!process.env.GEMINI_API_KEY, GeminiLLMModelPref: process.env.GEMINI_LLM_MODEL_PREF || "gemini-pro", + GeminiSafetySetting: + process.env.GEMINI_SAFETY_SETTING || "BLOCK_MEDIUM_AND_ABOVE", // LMStudio Keys LMStudioBasePath: process.env.LMSTUDIO_BASE_PATH, diff --git a/server/utils/AiProviders/gemini/index.js b/server/utils/AiProviders/gemini/index.js index 3dd307af..0c2cc769 100644 --- a/server/utils/AiProviders/gemini/index.js +++ b/server/utils/AiProviders/gemini/index.js @@ -29,6 +29,7 @@ class GeminiLLM { this.embedder = embedder ?? new NativeEmbedder(); this.defaultTemp = 0.7; // not used for Gemini + this.safetyThreshold = this.#fetchSafetyThreshold(); } #appendContext(contextTexts = []) { @@ -43,6 +44,41 @@ class GeminiLLM { ); } + // BLOCK_NONE can be a special candidate for some fields + // https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-attributes#how_to_remove_automated_response_blocking_for_select_safety_attributes + // so if you are wondering why BLOCK_NONE still failed, the link above will explain why. + #fetchSafetyThreshold() { + const threshold = + process.env.GEMINI_SAFETY_SETTING ?? "BLOCK_MEDIUM_AND_ABOVE"; + const safetyThresholds = [ + "BLOCK_NONE", + "BLOCK_ONLY_HIGH", + "BLOCK_MEDIUM_AND_ABOVE", + "BLOCK_LOW_AND_ABOVE", + ]; + return safetyThresholds.includes(threshold) + ? threshold + : "BLOCK_MEDIUM_AND_ABOVE"; + } + + #safetySettings() { + return [ + { + category: "HARM_CATEGORY_HATE_SPEECH", + threshold: this.safetyThreshold, + }, + { + category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", + threshold: this.safetyThreshold, + }, + { category: "HARM_CATEGORY_HARASSMENT", threshold: this.safetyThreshold }, + { + category: "HARM_CATEGORY_DANGEROUS_CONTENT", + threshold: this.safetyThreshold, + }, + ]; + } + streamingEnabled() { return "streamGetChatCompletion" in this; } @@ -143,6 +179,7 @@ class GeminiLLM { )?.content; const chatThread = this.gemini.startChat({ history: this.formatMessages(messages), + safetySettings: this.#safetySettings(), }); const result = await chatThread.sendMessage(prompt); const response = result.response; @@ -164,6 +201,7 @@ class GeminiLLM { )?.content; const chatThread = this.gemini.startChat({ history: this.formatMessages(messages), + safetySettings: this.#safetySettings(), }); const responseStream = await chatThread.sendMessageStream(prompt); if (!responseStream.stream) diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js index 40154163..c8811c9d 100644 --- a/server/utils/helpers/updateENV.js +++ b/server/utils/helpers/updateENV.js @@ -52,6 +52,10 @@ const KEY_MAPPING = { envKey: "GEMINI_LLM_MODEL_PREF", checks: [isNotEmpty, validGeminiModel], }, + GeminiSafetySetting: { + envKey: "GEMINI_SAFETY_SETTING", + checks: [validGeminiSafetySetting], + }, // LMStudio Settings LMStudioBasePath: { @@ -528,6 +532,18 @@ function validGeminiModel(input = "") { : `Invalid Model type. Must be one of ${validModels.join(", ")}.`; } +function validGeminiSafetySetting(input = "") { + const validModes = [ + "BLOCK_NONE", + "BLOCK_ONLY_HIGH", + "BLOCK_MEDIUM_AND_ABOVE", + "BLOCK_LOW_AND_ABOVE", + ]; + return validModes.includes(input) + ? null + : `Invalid Safety setting. Must be one of ${validModes.join(", ")}.`; +} + function validAnthropicModel(input = "") { const validModels = [ "claude-instant-1.2",