From 5e73dce506a3e26ba3ad12802742a0e5003c03b0 Mon Sep 17 00:00:00 2001 From: Timothy Carambat <rambat1010@gmail.com> Date: Mon, 29 Jul 2024 11:49:14 -0700 Subject: [PATCH] Enable editing of OpenRouter stream timeout for slower connections (#1994) --- .../LLMSelection/OpenRouterOptions/index.jsx | 81 ++++++++++++++----- server/models/systemSettings.js | 1 + server/utils/AiProviders/openRouter/index.js | 19 ++++- server/utils/helpers/updateENV.js | 4 + 4 files changed, 86 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx b/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx index 470340d2f..2b3e72b3f 100644 --- a/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx +++ b/frontend/src/components/LLMSelection/OpenRouterOptions/index.jsx @@ -1,27 +1,72 @@ import System from "@/models/system"; +import { CaretDown, CaretUp } from "@phosphor-icons/react"; import { useState, useEffect } from "react"; export default function OpenRouterOptions({ settings }) { + const [showAdvancedControls, setShowAdvancedControls] = useState(false); + return ( - <div className="flex gap-[36px] mt-1.5"> - <div className="flex flex-col w-60"> - <label className="text-white text-sm font-semibold block mb-3"> - OpenRouter API Key - </label> - <input - type="password" - name="OpenRouterApiKey" - className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5" - placeholder="OpenRouter API Key" - defaultValue={settings?.OpenRouterApiKey ? "*".repeat(20) : ""} - required={true} - autoComplete="off" - spellCheck={false} - /> + <div className="flex flex-col gap-y-4 mt-1.5"> + <div className="flex gap-[36px]"> + <div className="flex flex-col w-60"> + <label className="text-white text-sm font-semibold block mb-3"> + OpenRouter API Key + </label> + <input + type="password" + name="OpenRouterApiKey" + className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5" + placeholder="OpenRouter API Key" + defaultValue={settings?.OpenRouterApiKey ? "*".repeat(20) : ""} + required={true} + autoComplete="off" + spellCheck={false} + /> + </div> + {!settings?.credentialsOnly && ( + <OpenRouterModelSelection settings={settings} /> + )} + </div> + <AdvancedControls settings={settings} /> + </div> + ); +} + +function AdvancedControls({ settings }) { + const [showAdvancedControls, setShowAdvancedControls] = useState(false); + + return ( + <div className="flex flex-col gap-y-4"> + <button + type="button" + onClick={() => setShowAdvancedControls(!showAdvancedControls)} + className="text-white hover:text-white/70 flex items-center text-sm" + > + {showAdvancedControls ? "Hide" : "Show"} advanced controls + {showAdvancedControls ? ( + <CaretUp size={14} className="ml-1" /> + ) : ( + <CaretDown size={14} className="ml-1" /> + )} + </button> + <div hidden={!showAdvancedControls}> + <div className="flex flex-col w-60"> + <label className="text-white text-sm font-semibold block mb-3"> + Stream Timeout (ms) + </label> + <input + type="number" + name="OpenRouterTimeout" + className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5" + placeholder="Timeout value between token responses to auto-timeout the stream" + defaultValue={settings?.OpenRouterTimeout ?? 500} + autoComplete="off" + onScroll={(e) => e.target.blur()} + min={500} + step={1} + /> + </div> </div> - {!settings?.credentialsOnly && ( - <OpenRouterModelSelection settings={settings} /> - )} </div> ); } diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index 6f5b4238d..485837506 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -424,6 +424,7 @@ const SystemSettings = { // OpenRouter Keys OpenRouterApiKey: !!process.env.OPENROUTER_API_KEY, OpenRouterModelPref: process.env.OPENROUTER_MODEL_PREF, + OpenRouterTimeout: process.env.OPENROUTER_TIMEOUT_MS, // Mistral AI (API) Keys MistralApiKey: !!process.env.MISTRAL_API_KEY, diff --git a/server/utils/AiProviders/openRouter/index.js b/server/utils/AiProviders/openRouter/index.js index 7d0ff3e3b..c7d4dfb0b 100644 --- a/server/utils/AiProviders/openRouter/index.js +++ b/server/utils/AiProviders/openRouter/index.js @@ -38,6 +38,7 @@ class OpenRouterLLM { this.embedder = embedder ?? new NativeEmbedder(); this.defaultTemp = 0.7; + this.timeout = this.#parseTimeout(); if (!fs.existsSync(cacheFolder)) fs.mkdirSync(cacheFolder, { recursive: true }); @@ -49,6 +50,22 @@ class OpenRouterLLM { console.log(`\x1b[36m[${this.constructor.name}]\x1b[0m ${text}`, ...args); } + /** + * OpenRouter has various models that never return `finish_reasons` and thus leave the stream open + * which causes issues in subsequent messages. This timeout value forces us to close the stream after + * x milliseconds. This is a configurable value via the OPENROUTER_TIMEOUT_MS value + * @returns {number} The timeout value in milliseconds (default: 500) + */ + #parseTimeout() { + this.log( + `OpenRouter timeout is set to ${process.env.OPENROUTER_TIMEOUT_MS ?? 500}ms` + ); + if (isNaN(Number(process.env.OPENROUTER_TIMEOUT_MS))) return 500; + const setValue = Number(process.env.OPENROUTER_TIMEOUT_MS); + if (setValue < 500) return 500; + return setValue; + } + // This checks if the .cached_at file has a timestamp that is more than 1Week (in millis) // from the current date. If it is, then we will refetch the API so that all the models are up // to date. @@ -161,7 +178,7 @@ class OpenRouterLLM { } handleStream(response, stream, responseProps) { - const timeoutThresholdMs = 500; + const timeoutThresholdMs = this.timeout; const { uuid = uuidv4(), sources = [] } = responseProps; return new Promise(async (resolve) => { diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js index d39941ec4..afacb7279 100644 --- a/server/utils/helpers/updateENV.js +++ b/server/utils/helpers/updateENV.js @@ -365,6 +365,10 @@ const KEY_MAPPING = { envKey: "OPENROUTER_MODEL_PREF", checks: [isNotEmpty], }, + OpenRouterTimeout: { + envKey: "OPENROUTER_TIMEOUT_MS", + checks: [], + }, // Groq Options GroqApiKey: { -- GitLab