From c517f668fc4293ec45ea3ae9fafda3f944e4269a Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Thu, 29 May 2025 11:08:42 -0400 Subject: [PATCH 001/104] =?UTF-8?q?=F0=9F=94=A7=20chore:=20Remove=20`rollu?= =?UTF-8?q?p-plugin-visualizer`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package.json | 1 - client/vite.config.ts | 11 +----- package-lock.json | 81 +++++++------------------------------------ 3 files changed, 14 insertions(+), 79 deletions(-) diff --git a/client/package.json b/client/package.json index 8e4be78764..7cb983d218 100644 --- a/client/package.json +++ b/client/package.json @@ -139,7 +139,6 @@ "postcss": "^8.4.31", "postcss-loader": "^7.1.0", "postcss-preset-env": "^8.2.0", - "rollup-plugin-visualizer": "^6.0.0", "tailwindcss": "^3.4.1", "ts-jest": "^29.2.5", "typescript": "^5.3.3", diff --git a/client/vite.config.ts b/client/vite.config.ts index e1fe5aded5..98451b6c06 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -2,7 +2,6 @@ import path from 'path'; import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { VitePWA } from 'vite-plugin-pwa'; -import { visualizer } from 'rollup-plugin-visualizer'; import { compression } from 'vite-plugin-compression2'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; import type { Plugin } from 'vite'; @@ -93,15 +92,7 @@ export default defineConfig(({ command }) => ({ compression({ threshold: 10240, }), - process.env.VITE_BUNDLE_ANALYSIS === 'true' && - visualizer({ - filename: 'dist/bundle-analysis.html', - open: true, - gzipSize: true, - brotliSize: true, - template: 'treemap', // 'treemap' | 'sunburst' | 'network' - }), - ].filter(Boolean), + ], publicDir: command === 'serve' ? './public' : false, build: { sourcemap: process.env.NODE_ENV === 'development', diff --git a/package-lock.json b/package-lock.json index fa49421c86..2a863009ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2632,7 +2632,6 @@ "postcss": "^8.4.31", "postcss-loader": "^7.1.0", "postcss-preset-env": "^8.2.0", - "rollup-plugin-visualizer": "^6.0.0", "tailwindcss": "^3.4.1", "ts-jest": "^29.2.5", "typescript": "^5.3.3", @@ -19835,6 +19834,19 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@node-saml/node-saml": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-5.0.0.tgz", @@ -19932,19 +19944,6 @@ "node": ">=0.6.0" } }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@node-saml/passport-saml": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-5.0.0.tgz", @@ -41160,60 +41159,6 @@ "node": ">= 10.0.0" } }, - "node_modules/rollup-plugin-visualizer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.0.tgz", - "integrity": "sha512-9aXBJh1uzI6XNmAeATox2z5MWrEPzL6hQgEMOYxTltWOy5x2ycQCec0Y9fC19sBgf3FvIF36aG9DrvUcdnXPew==", - "dev": true, - "license": "MIT", - "dependencies": { - "open": "^10.1.2", - "picomatch": "^4.0.2", - "source-map": "^0.7.4", - "yargs": "^17.5.1" - }, - "bin": { - "rollup-plugin-visualizer": "dist/bin/cli.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "rolldown": "1.x", - "rollup": "2.x || 3.x || 4.x" - }, - "peerDependenciesMeta": { - "rolldown": { - "optional": true - }, - "rollup": { - "optional": true - } - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", From 4808c5be48a561fe856151233550090b2b92b2eb Mon Sep 17 00:00:00 2001 From: Ruben Talstra Date: Thu, 29 May 2025 20:19:08 +0200 Subject: [PATCH 002/104] =?UTF-8?q?=F0=9F=94=A7=20fix:=20Update=20xml-cryp?= =?UTF-8?q?to=20and=20xmldom=20dependencies=20in=20package-lock.json=20(#7?= =?UTF-8?q?630)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 61 +++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a863009ef..cec3766730 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19869,35 +19869,6 @@ "node": ">= 18" } }, - "node_modules/@node-saml/node-saml/node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@node-saml/node-saml/node_modules/xml-crypto": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-6.0.0.tgz", - "integrity": "sha512-L3RgnkaDrHaYcCnoENv4Idzt1ZRj5U1z1BDH98QdDTQfssScx8adgxhd9qwyYo+E3fXbQZjEQH7aiXHLVgxGvw==", - "dependencies": { - "@xmldom/is-dom-node": "^1.0.1", - "@xmldom/xmldom": "^0.8.10", - "xpath": "^0.0.33" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@node-saml/node-saml/node_modules/xml-crypto/node_modules/xpath": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.33.tgz", - "integrity": "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==", - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/@node-saml/node-saml/node_modules/xml-encryption": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.1.0.tgz", @@ -25521,6 +25492,15 @@ "node": ">= 16" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -45253,6 +45233,20 @@ "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", "dev": true }, + "node_modules/xml-crypto": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-6.1.2.tgz", + "integrity": "sha512-leBOVQdVi8FvPJrMYoum7Ici9qyxfE4kVi+AkpUoYCSXaQF4IlBm1cneTK9oAxR61LpYxTx7lNcsnBIeRpGW2w==", + "license": "MIT", + "dependencies": { + "@xmldom/is-dom-node": "^1.0.1", + "@xmldom/xmldom": "^0.8.10", + "xpath": "^0.0.33" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", @@ -45276,6 +45270,15 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "devOptional": true }, + "node_modules/xpath": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.33.tgz", + "integrity": "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==", + "license": "MIT", + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", From 4cbab86b452b3a8ef914d8f876a0441e6a1a1550 Mon Sep 17 00:00:00 2001 From: Ruben Talstra Date: Fri, 30 May 2025 18:16:34 +0200 Subject: [PATCH 003/104] =?UTF-8?q?=F0=9F=93=88=20feat:=20Chat=20rating=20?= =?UTF-8?q?for=20feedback=20(#5878)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: working started for feedback implementation. TODO: - needs some refactoring. - needs some UI animations. * feat: working rate functionality * feat: works now as well to reader the already rated responses from the server. * feat: added the option to give feedback in text (optional) * feat: added Dismiss option `x` to the `FeedbackTagOptions` * ✨ feat: Add rating and ratingContent fields to message schema * 🔧 chore: Bump version to 0.0.3 in package.json * ✨ feat: Enhance feedback localization and update UI elements * 🚀 feat: Implement feedback tagging system with thumbs up/down options * 🚀 feat: Add data-provider package to unused i18n keys detection * 🎨 style: update HoverButtons' style * 🎨 style: Update HoverButtons and Fork components for improved styling and visibility * 🔧 feat: Implement feedback system with rating and content options * 🔧 feat: Enhance feedback handling with improved rating toggle and tag options * 🔧 feat: Integrate toast notifications for feedback submission and clean up unused state * 🔧 feat: Remove unused feedback tag options from translation file * ✨ refactor: clean up Feedback component and improve HoverButtons structure * ✨ refactor: remove unused settings switches for auto scroll, hide side panel, and user message markdown * refactor: reorganize import order * ✨ refactor: enhance HoverButtons and Fork components with improved styles and animations * ✨ refactor: update feedback response phrases for improved user engagement * ✨ refactor: add CheckboxOption component and streamline fork options rendering * Refactor feedback components and logic - Consolidated feedback handling into a single Feedback component, removing FeedbackButtons and FeedbackTagOptions. - Introduced new feedback tagging system with detailed tags for both thumbs up and thumbs down ratings. - Updated feedback schema to include new tags and improved type definitions. - Enhanced user interface for feedback collection, including a dialog for additional comments. - Removed obsolete files and adjusted imports accordingly. - Updated translations for new feedback tags and placeholders. * ✨ refactor: update feedback handling by replacing rating fields with feedback in message updates * fix: add missing validateMessageReq middleware to feedback route and refactor feedback system * 🗑️ chore: Remove redundant fork option explanations from translation file * 🔧 refactor: Remove unused dependency from feedback callback * 🔧 refactor: Simplify message update response structure and improve error logging * Chore: removed unused tests. --------- Co-authored-by: Marco Beretta <81851188+berry-13@users.noreply.github.com> --- .github/workflows/i18n-unused-keys.yml | 3 +- api/app/clients/ChatGPTClient.js | 7 +- api/app/clients/GoogleClient.js | 10 +- api/app/clients/specs/BaseClient.test.js | 4 +- api/app/clients/specs/OpenAIClient.test.js | 6 +- .../tools/structured/OpenAIImageTools.js | 2 +- api/models/Message.js | 1 + api/models/Message.spec.js | 6 +- api/server/controllers/agents/request.js | 2 +- api/server/middleware/abortMiddleware.js | 2 +- api/server/routes/messages.js | 25 ++ client/src/common/types.ts | 17 +- client/src/components/Audio/TTS.tsx | 46 +- .../Endpoints/components/EndpointItem.tsx | 12 +- .../components/EndpointModelItem.tsx | 8 +- .../Endpoints/components/SearchResults.tsx | 24 +- .../src/components/Chat/Messages/Feedback.tsx | 347 ++++++++++++++ client/src/components/Chat/Messages/Fork.tsx | 424 ++++++++++++++++++ .../components/Chat/Messages/HoverButtons.tsx | 285 ++++++++---- .../Chat/Messages/ui/MessageRender.tsx | 7 +- client/src/components/Chat/TemporaryChat.tsx | 2 +- client/src/components/Conversations/Fork.tsx | 408 ----------------- client/src/components/Conversations/index.ts | 2 +- .../components/Input/Generations/Continue.tsx | 15 - .../Generations/__tests__/Continue.spec.tsx | 22 - .../src/components/Messages/ContentRender.tsx | 3 +- .../SettingsTabs/General/AutoScrollSwitch.tsx | 34 -- .../Nav/SettingsTabs/General/General.tsx | 7 +- .../General/HideSidePanelSwitch.tsx | 35 -- .../General/UserMsgMarkdownSwitch.tsx | 35 -- .../Speech/STT/EngineSTTDropdown.tsx | 6 +- .../Speech/TTS/EngineTTSDropdown.tsx | 6 +- client/src/components/svg/ContinueIcon.tsx | 4 +- client/src/components/svg/ThumbDownIcon.tsx | 39 ++ client/src/components/svg/ThumbUpIcon.tsx | 39 ++ client/src/components/svg/index.ts | 2 + client/src/hooks/Endpoint/useEndpoints.ts | 10 +- .../src/hooks/Messages/useMessageActions.tsx | 62 ++- client/src/locales/ar/translation.json | 2 +- client/src/locales/cs/translation.json | 2 +- client/src/locales/de/translation.json | 2 +- client/src/locales/en/translation.json | 18 +- client/src/locales/es/translation.json | 2 +- client/src/locales/et/translation.json | 2 +- client/src/locales/fa/translation.json | 2 +- client/src/locales/fi/translation.json | 2 +- client/src/locales/fr/translation.json | 2 +- client/src/locales/he/translation.json | 2 +- client/src/locales/hu/translation.json | 2 +- client/src/locales/id/translation.json | 2 +- client/src/locales/it/translation.json | 2 +- client/src/locales/ja/translation.json | 2 +- client/src/locales/ko/translation.json | 2 +- client/src/locales/nl/translation.json | 2 +- client/src/locales/pl/translation.json | 2 +- client/src/locales/pt-BR/translation.json | 2 +- client/src/locales/pt-PT/translation.json | 2 +- client/src/locales/ru/translation.json | 2 +- client/src/locales/sv/translation.json | 2 +- client/src/locales/th/translation.json | 2 +- client/src/locales/tr/translation.json | 2 +- client/src/locales/zh-Hans/translation.json | 2 +- client/src/locales/zh-Hant/translation.json | 2 +- client/src/style.css | 12 + packages/data-provider/specs/actions.spec.ts | 16 +- packages/data-provider/src/actions.ts | 25 +- packages/data-provider/src/api-endpoints.ts | 4 + packages/data-provider/src/data-service.ts | 9 + packages/data-provider/src/feedback.ts | 141 ++++++ packages/data-provider/src/file-config.ts | 1 - packages/data-provider/src/index.ts | 2 + .../src/react-query/react-query-service.ts | 16 + packages/data-provider/src/schemas.ts | 3 + packages/data-provider/src/types.ts | 12 + packages/data-provider/src/zod.spec.ts | 126 +++--- packages/data-schemas/src/schema/message.ts | 25 ++ 76 files changed, 1592 insertions(+), 835 deletions(-) create mode 100644 client/src/components/Chat/Messages/Feedback.tsx create mode 100644 client/src/components/Chat/Messages/Fork.tsx delete mode 100644 client/src/components/Conversations/Fork.tsx delete mode 100644 client/src/components/Input/Generations/Continue.tsx delete mode 100644 client/src/components/Input/Generations/__tests__/Continue.spec.tsx delete mode 100644 client/src/components/Nav/SettingsTabs/General/AutoScrollSwitch.tsx delete mode 100644 client/src/components/Nav/SettingsTabs/General/HideSidePanelSwitch.tsx delete mode 100644 client/src/components/Nav/SettingsTabs/General/UserMsgMarkdownSwitch.tsx create mode 100644 client/src/components/svg/ThumbDownIcon.tsx create mode 100644 client/src/components/svg/ThumbUpIcon.tsx create mode 100644 packages/data-provider/src/feedback.ts diff --git a/.github/workflows/i18n-unused-keys.yml b/.github/workflows/i18n-unused-keys.yml index 6bcf824946..07cc77a1ae 100644 --- a/.github/workflows/i18n-unused-keys.yml +++ b/.github/workflows/i18n-unused-keys.yml @@ -5,12 +5,13 @@ on: paths: - "client/src/**" - "api/**" + - "packages/data-provider/src/**" jobs: detect-unused-i18n-keys: runs-on: ubuntu-latest permissions: - pull-requests: write # Required for posting PR comments + pull-requests: write steps: - name: Checkout repository uses: actions/checkout@v3 diff --git a/api/app/clients/ChatGPTClient.js b/api/app/clients/ChatGPTClient.js index 07b2fa97bb..36a3f4936a 100644 --- a/api/app/clients/ChatGPTClient.js +++ b/api/app/clients/ChatGPTClient.js @@ -244,9 +244,9 @@ class ChatGPTClient extends BaseClient { baseURL = this.langchainProxy ? constructAzureURL({ - baseURL: this.langchainProxy, - azureOptions: this.azure, - }) + baseURL: this.langchainProxy, + azureOptions: this.azure, + }) : this.azureEndpoint.split(/(? { try { let done = false; diff --git a/api/app/clients/GoogleClient.js b/api/app/clients/GoogleClient.js index c9102e9ae2..4151e6663a 100644 --- a/api/app/clients/GoogleClient.js +++ b/api/app/clients/GoogleClient.js @@ -236,11 +236,11 @@ class GoogleClient extends BaseClient { msg.content = ( !Array.isArray(msg.content) ? [ - { - type: ContentTypes.TEXT, - [ContentTypes.TEXT]: msg.content, - }, - ] + { + type: ContentTypes.TEXT, + [ContentTypes.TEXT]: msg.content, + }, + ] : msg.content ).concat(message.image_urls); diff --git a/api/app/clients/specs/BaseClient.test.js b/api/app/clients/specs/BaseClient.test.js index d620d5f647..1cd4a80db7 100644 --- a/api/app/clients/specs/BaseClient.test.js +++ b/api/app/clients/specs/BaseClient.test.js @@ -52,7 +52,7 @@ const messageHistory = [ { role: 'user', isCreatedByUser: true, - text: 'What\'s up', + text: "What's up", messageId: '3', parentMessageId: '2', }, @@ -456,7 +456,7 @@ describe('BaseClient', () => { const chatMessages2 = await TestClient.loadHistory(conversationId, '3'); expect(TestClient.currentMessages).toHaveLength(3); - expect(chatMessages2[chatMessages2.length - 1].text).toEqual('What\'s up'); + expect(chatMessages2[chatMessages2.length - 1].text).toEqual("What's up"); }); /* Most of the new sendMessage logic revolving around edited/continued AI messages diff --git a/api/app/clients/specs/OpenAIClient.test.js b/api/app/clients/specs/OpenAIClient.test.js index 579f636eef..8e470727da 100644 --- a/api/app/clients/specs/OpenAIClient.test.js +++ b/api/app/clients/specs/OpenAIClient.test.js @@ -462,17 +462,17 @@ describe('OpenAIClient', () => { role: 'system', name: 'example_user', content: - 'Let\'s circle back when we have more bandwidth to touch base on opportunities for increased leverage.', + "Let's circle back when we have more bandwidth to touch base on opportunities for increased leverage.", }, { role: 'system', name: 'example_assistant', - content: 'Let\'s talk later when we\'re less busy about how to do better.', + content: "Let's talk later when we're less busy about how to do better.", }, { role: 'user', content: - 'This late pivot means we don\'t have time to boil the ocean for the client deliverable.', + "This late pivot means we don't have time to boil the ocean for the client deliverable.", }, ]; diff --git a/api/app/clients/tools/structured/OpenAIImageTools.js b/api/app/clients/tools/structured/OpenAIImageTools.js index afea9dfd55..499cda3ea7 100644 --- a/api/app/clients/tools/structured/OpenAIImageTools.js +++ b/api/app/clients/tools/structured/OpenAIImageTools.js @@ -64,7 +64,7 @@ const DEFAULT_IMAGE_EDIT_PROMPT_DESCRIPTION = `Describe the changes, enhancement Always base this prompt on the most recently uploaded reference images.`; const displayMessage = - 'The tool displayed an image. All generated images are already plainly visible, so don\'t repeat the descriptions in detail. Do not list download links as they are available in the UI already. The user may download the images by clicking on them, but do not mention anything about downloading to the user.'; + "The tool displayed an image. All generated images are already plainly visible, so don't repeat the descriptions in detail. Do not list download links as they are available in the UI already. The user may download the images by clicking on them, but do not mention anything about downloading to the user."; /** * Replaces unwanted characters from the input string diff --git a/api/models/Message.js b/api/models/Message.js index 86fd2fd549..9384c35f72 100644 --- a/api/models/Message.js +++ b/api/models/Message.js @@ -255,6 +255,7 @@ async function updateMessage(req, message, metadata) { text: updatedMessage.text, isCreatedByUser: updatedMessage.isCreatedByUser, tokenCount: updatedMessage.tokenCount, + feedback: updatedMessage.feedback, }; } catch (err) { logger.error('Error updating message:', err); diff --git a/api/models/Message.spec.js b/api/models/Message.spec.js index a542130b59..7aef843678 100644 --- a/api/models/Message.spec.js +++ b/api/models/Message.spec.js @@ -153,7 +153,7 @@ describe('Message Operations', () => { }); describe('Conversation Hijacking Prevention', () => { - it('should not allow editing a message in another user\'s conversation', async () => { + it("should not allow editing a message in another user's conversation", async () => { const attackerReq = { user: { id: 'attacker123' } }; const victimConversationId = 'victim-convo-123'; const victimMessageId = 'victim-msg-123'; @@ -175,7 +175,7 @@ describe('Message Operations', () => { ); }); - it('should not allow deleting messages from another user\'s conversation', async () => { + it("should not allow deleting messages from another user's conversation", async () => { const attackerReq = { user: { id: 'attacker123' } }; const victimConversationId = 'victim-convo-123'; const victimMessageId = 'victim-msg-123'; @@ -193,7 +193,7 @@ describe('Message Operations', () => { }); }); - it('should not allow inserting a new message into another user\'s conversation', async () => { + it("should not allow inserting a new message into another user's conversation", async () => { const attackerReq = { user: { id: 'attacker123' } }; const victimConversationId = uuidv4(); // Use a valid UUID diff --git a/api/server/controllers/agents/request.js b/api/server/controllers/agents/request.js index fcee62edc7..24b7822c1f 100644 --- a/api/server/controllers/agents/request.js +++ b/api/server/controllers/agents/request.js @@ -228,7 +228,7 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => { // Save user message if needed if (!client.skipSaveUserMessage) { await saveMessage(req, userMessage, { - context: 'api/server/controllers/agents/request.js - don\'t skip saving user message', + context: "api/server/controllers/agents/request.js - don't skip saving user message", }); } diff --git a/api/server/middleware/abortMiddleware.js b/api/server/middleware/abortMiddleware.js index bfc28f513d..94d69004bd 100644 --- a/api/server/middleware/abortMiddleware.js +++ b/api/server/middleware/abortMiddleware.js @@ -327,7 +327,7 @@ const handleAbortError = async (res, req, error, data) => { errorText = `{"type":"${ErrorTypes.INVALID_REQUEST}"}`; } - if (error?.message?.includes('does not support \'system\'')) { + if (error?.message?.includes("does not support 'system'")) { errorText = `{"type":"${ErrorTypes.NO_SYSTEM_MESSAGES}"}`; } diff --git a/api/server/routes/messages.js b/api/server/routes/messages.js index d5980ae55b..7f771a4821 100644 --- a/api/server/routes/messages.js +++ b/api/server/routes/messages.js @@ -253,6 +253,31 @@ router.put('/:conversationId/:messageId', validateMessageReq, async (req, res) = } }); +router.put('/:conversationId/:messageId/feedback', validateMessageReq, async (req, res) => { + try { + const { conversationId, messageId } = req.params; + const { feedback } = req.body; + + const updatedMessage = await updateMessage( + req, + { + messageId, + feedback: feedback || null, + }, + { context: 'updateFeedback' }, + ); + + res.json({ + messageId, + conversationId, + feedback: updatedMessage.feedback, + }); + } catch (error) { + logger.error('Error updating message feedback:', error); + res.status(500).json({ error: 'Failed to update feedback' }); + } +}); + router.delete('/:conversationId/:messageId', validateMessageReq, async (req, res) => { try { const { messageId } = req.params; diff --git a/client/src/common/types.ts b/client/src/common/types.ts index 6837869e8e..0ac6387c33 100644 --- a/client/src/common/types.ts +++ b/client/src/common/types.ts @@ -457,11 +457,20 @@ export type VoiceOption = { }; export type TMessageAudio = { - messageId?: string; - content?: t.TMessageContentParts[] | string; - className?: string; - isLast: boolean; + isLast?: boolean; index: number; + messageId: string; + content: string; + className?: string; + renderButton?: (props: { + onClick: (e?: React.MouseEvent) => void; + title: string; + icon: React.ReactNode; + isActive?: boolean; + isVisible?: boolean; + isDisabled?: boolean; + className?: string; + }) => React.ReactNode; }; export type OptionWithIcon = Option & { icon?: React.ReactNode }; diff --git a/client/src/components/Audio/TTS.tsx b/client/src/components/Audio/TTS.tsx index 3ceacb7f8d..d5fcb91120 100644 --- a/client/src/components/Audio/TTS.tsx +++ b/client/src/components/Audio/TTS.tsx @@ -1,5 +1,5 @@ /* eslint-disable jsx-a11y/media-has-caption */ -import { useEffect, useMemo } from 'react'; +import { useEffect } from 'react'; import { useRecoilValue } from 'recoil'; import type { TMessageAudio } from '~/common'; import { useLocalize, useTTSBrowser, useTTSExternal } from '~/hooks'; @@ -7,7 +7,14 @@ import { VolumeIcon, VolumeMuteIcon, Spinner } from '~/components'; import { logger } from '~/utils'; import store from '~/store'; -export function BrowserTTS({ isLast, index, messageId, content, className }: TMessageAudio) { +export function BrowserTTS({ + isLast, + index, + messageId, + content, + className, + renderButton, +}: TMessageAudio) { const localize = useLocalize(); const playbackRate = useRecoilValue(store.playbackRate); @@ -46,21 +53,30 @@ export function BrowserTTS({ isLast, index, messageId, content, className }: TMe audioRef.current, ); + const handleClick = () => { + if (audioRef.current) { + audioRef.current.muted = false; + } + toggleSpeech(); + }; + + const title = isSpeaking === true ? localize('com_ui_stop') : localize('com_ui_read_aloud'); + return ( <> - + {renderButton ? ( + renderButton({ + onClick: handleClick, + title: title, + icon: renderIcon('19'), + isActive: isSpeaking, + className, + }) + ) : ( + + )}