From e5f31c2e14058228769158d2a1f654e3cecdb05a Mon Sep 17 00:00:00 2001 From: Algorithm5838 <108630393+Algorithm5838@users.noreply.github.com> Date: Fri, 17 Apr 2026 08:22:06 +0300 Subject: [PATCH] perf: replace JSON.stringify equality with fast-deep-equal (#23370) --- package-lock.json | 2 +- package.json | 1 + .../components/chat/Messages/MultiResponseMessages.svelte | 3 ++- src/lib/components/chat/Messages/ResponseMessage.svelte | 3 ++- .../chat/Messages/ResponseMessage/StatusHistory.svelte | 6 ++---- src/lib/components/chat/Messages/UserMessage.svelte | 3 ++- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 538792470e..e917faa608 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "dayjs": "^1.11.10", "dompurify": "^3.2.6", "eventsource-parser": "^1.1.2", + "fast-deep-equal": "^3.1.3", "file-saver": "^2.0.5", "focus-trap": "^7.6.4", "fuse.js": "^7.0.0", @@ -8462,7 +8463,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-fifo": { diff --git a/package.json b/package.json index ab0cf78afc..204f35d51f 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "dayjs": "^1.11.10", "dompurify": "^3.2.6", "eventsource-parser": "^1.1.2", + "fast-deep-equal": "^3.1.3", "file-saver": "^2.0.5", "focus-trap": "^7.6.4", "fuse.js": "^7.0.0", diff --git a/src/lib/components/chat/Messages/MultiResponseMessages.svelte b/src/lib/components/chat/Messages/MultiResponseMessages.svelte index ae2be93ff2..a6d5a9b8a7 100644 --- a/src/lib/components/chat/Messages/MultiResponseMessages.svelte +++ b/src/lib/components/chat/Messages/MultiResponseMessages.svelte @@ -19,6 +19,7 @@ import localizedFormat from 'dayjs/plugin/localizedFormat'; import ProfileImage from './ProfileImage.svelte'; import { WEBUI_BASE_URL } from '$lib/constants'; + import equal from 'fast-deep-equal'; const i18n = getContext('i18n'); dayjs.extend(localizedFormat); @@ -66,7 +67,7 @@ if (source) { if (message.content !== source.content || message.done !== source.done) { message = structuredClone(source); - } else if (JSON.stringify(message) !== JSON.stringify(source)) { + } else if (!equal(message, source)) { message = structuredClone(source); } } diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 35e592d139..ebae97d95b 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -37,6 +37,7 @@ removeAllDetails } from '$lib/utils'; import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; + import equal from 'fast-deep-equal'; import Name from './Name.svelte'; import ProfileImage from './ProfileImage.svelte'; @@ -127,7 +128,7 @@ // Avoids 2x O(n) JSON.stringify calls that are always true during streaming anyway if (message.content !== source.content || message.done !== source.done) { message = structuredClone(source); - } else if (JSON.stringify(message) !== JSON.stringify(source)) { + } else if (!equal(message, source)) { // Slow path: full comparison for infrequent changes (sources, annotations, status, etc.) message = structuredClone(source); } diff --git a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte index 5aaf774694..bb65bf331b 100644 --- a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte @@ -3,6 +3,7 @@ const i18n = getContext('i18n'); import StatusItem from './StatusHistory/StatusItem.svelte'; + import equal from 'fast-deep-equal'; export let statusHistory = []; export let expand = false; @@ -21,10 +22,7 @@ status = history.at(-1); } - $: if ( - statusHistory.length !== history.length || - JSON.stringify(statusHistory) !== JSON.stringify(history) - ) { + $: if (!equal(statusHistory, history)) { history = statusHistory; } diff --git a/src/lib/components/chat/Messages/UserMessage.svelte b/src/lib/components/chat/Messages/UserMessage.svelte index ec48a2e1ff..3b26dba1ff 100644 --- a/src/lib/components/chat/Messages/UserMessage.svelte +++ b/src/lib/components/chat/Messages/UserMessage.svelte @@ -7,6 +7,7 @@ import { user as _user } from '$lib/stores'; import { copyToClipboard as _copyToClipboard, formatDate } from '$lib/utils'; import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; + import equal from 'fast-deep-equal'; import Name from './Name.svelte'; import ProfileImage from './ProfileImage.svelte'; @@ -58,7 +59,7 @@ if (source) { if (message.content !== source.content) { message = structuredClone(source); - } else if (JSON.stringify(message) !== JSON.stringify(source)) { + } else if (!equal(message, source)) { message = structuredClone(source); } }