From af430e46f456cfdc9bedfac68a60171854b47f26 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Fri, 24 Jan 2025 18:15:47 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20Google=20Parameters,?= =?UTF-8?q?=20Ollama/Openrouter=20Reasoning,=20&=20UI=20Optimizations=20(#?= =?UTF-8?q?5456)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Google Model Parameters * fix: dynamic input number value, previously coerced by zod schema * refactor: support openrouter reasoning tokens and XML for thinking directive to conform to ollama * fix: virtualize combobox to prevent performance drop on re-renders of long model/agent/assistant lists * refactor: simplify Fork component by removing unnecessary chat context index * fix: prevent rendering of Thinking component when children are null * refactor: update Markdown component to replace tags and simplify remarkPlugins configuration * refactor: reorder remarkPlugins to improve plugin configuration in Markdown component --- api/app/clients/OpenAIClient.js | 14 ++- client/package.json | 1 + client/src/components/Artifacts/Thinking.tsx | 4 + .../Chat/Messages/Content/Markdown.tsx | 20 ++--- client/src/components/Conversations/Fork.tsx | 5 +- .../SidePanel/Parameters/DynamicInput.tsx | 10 +-- .../SidePanel/Parameters/settings.ts | 87 +++++++++++++++++++ client/src/components/ui/ControlCombobox.tsx | 72 ++++++++++----- .../hooks/Conversations/useDebouncedInput.ts | 9 +- package-lock.json | 25 +++++- packages/data-provider/package.json | 2 +- packages/data-provider/src/schemas.ts | 1 + 12 files changed, 200 insertions(+), 50 deletions(-) diff --git a/api/app/clients/OpenAIClient.js b/api/app/clients/OpenAIClient.js index 034547c38c..a4ff4d763c 100644 --- a/api/app/clients/OpenAIClient.js +++ b/api/app/clients/OpenAIClient.js @@ -1253,6 +1253,12 @@ ${convo} delete modelOptions.stop; } + let reasoningKey = 'reasoning_content'; + if (this.useOpenRouter) { + modelOptions.include_reasoning = true; + reasoningKey = 'reasoning'; + } + if (modelOptions.stream) { streamPromise = new Promise((resolve) => { streamResolve = resolve; @@ -1291,14 +1297,14 @@ ${convo} let reasoningCompleted = false; for await (const chunk of stream) { - if (chunk?.choices?.[0]?.delta?.reasoning_content) { + if (chunk?.choices?.[0]?.delta?.[reasoningKey]) { if (reasoningTokens.length === 0) { - const thinkingDirective = ':::thinking\n'; + const thinkingDirective = '\n'; intermediateReply.push(thinkingDirective); reasoningTokens.push(thinkingDirective); onProgress(thinkingDirective); } - const reasoning_content = chunk?.choices?.[0]?.delta?.reasoning_content || ''; + const reasoning_content = chunk?.choices?.[0]?.delta?.[reasoningKey] || ''; intermediateReply.push(reasoning_content); reasoningTokens.push(reasoning_content); onProgress(reasoning_content); @@ -1307,7 +1313,7 @@ ${convo} const token = chunk?.choices?.[0]?.delta?.content || ''; if (!reasoningCompleted && reasoningTokens.length > 0 && token) { reasoningCompleted = true; - const separatorTokens = '\n:::\n'; + const separatorTokens = '\n\n'; reasoningTokens.push(separatorTokens); onProgress(separatorTokens); } diff --git a/client/package.json b/client/package.json index 1a5a545c1e..057b150459 100644 --- a/client/package.json +++ b/client/package.json @@ -90,6 +90,7 @@ "react-speech-recognition": "^3.10.0", "react-textarea-autosize": "^8.4.0", "react-transition-group": "^4.4.5", + "react-virtualized": "^9.22.6", "recoil": "^0.7.7", "regenerator-runtime": "^0.14.1", "rehype-highlight": "^6.0.0", diff --git a/client/src/components/Artifacts/Thinking.tsx b/client/src/components/Artifacts/Thinking.tsx index 53bafba627..4335603788 100644 --- a/client/src/components/Artifacts/Thinking.tsx +++ b/client/src/components/Artifacts/Thinking.tsx @@ -16,6 +16,10 @@ const Thinking = ({ children }: ThinkingProps) => { setIsExpanded(!isExpanded); }; + if (children == null) { + return null; + } + return (
- - {matches.map((item) => ( - } - > - {item.icon != null && ( -
- {item.icon} -
- )} - {item.label} -
- ))} -
+
+ + {({ width }) => ( + + )} + +
diff --git a/client/src/hooks/Conversations/useDebouncedInput.ts b/client/src/hooks/Conversations/useDebouncedInput.ts index bae6ae0c7e..56584769ce 100644 --- a/client/src/hooks/Conversations/useDebouncedInput.ts +++ b/client/src/hooks/Conversations/useDebouncedInput.ts @@ -20,7 +20,7 @@ function useDebouncedInput({ initialValue: T; delay?: number; }): [ - (e: React.ChangeEvent | T) => void, + (e: React.ChangeEvent | T, numeric?: boolean) => void, T, SetterOrUpdater, // (newValue: string) => void, @@ -37,12 +37,15 @@ function useDebouncedInput({ /** An onChange handler that updates the local state and the debounced option */ const onChange = useCallback( - (e: React.ChangeEvent | T) => { - const newValue: T = + (e: React.ChangeEvent | T, numeric?: boolean) => { + let newValue: T = typeof e !== 'object' ? e : ((e as React.ChangeEvent).target .value as unknown as T); + if (numeric === true) { + newValue = Number(newValue) as unknown as T; + } setValue(newValue); setDebouncedOption(newValue); }, diff --git a/package-lock.json b/package-lock.json index 0f1bdf0c94..4fbf746833 100644 --- a/package-lock.json +++ b/package-lock.json @@ -952,6 +952,7 @@ "react-speech-recognition": "^3.10.0", "react-textarea-autosize": "^8.4.0", "react-transition-group": "^4.4.5", + "react-virtualized": "^9.22.6", "recoil": "^0.7.7", "regenerator-runtime": "^0.14.1", "rehype-highlight": "^6.0.0", @@ -29460,6 +29461,11 @@ "react-dom": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x" } }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "node_modules/react-markdown": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", @@ -29760,6 +29766,23 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-virtualized": { + "version": "9.22.6", + "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.6.tgz", + "integrity": "sha512-U5j7KuUQt3AaMatlMJ0UJddqSiX+Km0YJxSqbAzIiGw5EmNz0khMyqP2hzgu4+QUtm+QPIrxzUX4raJxmVJnHg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "clsx": "^1.0.4", + "dom-helpers": "^5.1.3", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -35067,7 +35090,7 @@ }, "packages/data-provider": { "name": "librechat-data-provider", - "version": "0.7.693", + "version": "0.7.694", "license": "ISC", "dependencies": { "axios": "^1.7.7", diff --git a/packages/data-provider/package.json b/packages/data-provider/package.json index 4e5b2e3439..224b286a26 100644 --- a/packages/data-provider/package.json +++ b/packages/data-provider/package.json @@ -1,6 +1,6 @@ { "name": "librechat-data-provider", - "version": "0.7.693", + "version": "0.7.694", "description": "data services for librechat apps", "main": "dist/index.js", "module": "dist/index.es.js", diff --git a/packages/data-provider/src/schemas.ts b/packages/data-provider/src/schemas.ts index 42f26eeffa..b20691064a 100644 --- a/packages/data-provider/src/schemas.ts +++ b/packages/data-provider/src/schemas.ts @@ -38,6 +38,7 @@ export const paramEndpoints = new Set([ EModelEndpoint.azureOpenAI, EModelEndpoint.anthropic, EModelEndpoint.custom, + EModelEndpoint.google, ]); export enum BedrockProviders {