From 043284f51eccf3c19555243eb50aa10739bb8793 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Sat, 1 Feb 2025 22:22:45 +0700 Subject: [PATCH] chore: reasoning block (#4551) * chore: reasoning text block * chore: update interface support all theme * chore: update failed test * chore: update state collapsed based on message index * fix: use reserve_id instead of message index * chore: clean up * chore: fix loading indicator --------- Co-authored-by: Louis --- extensions/yarn.lock | 24 ++++---- web/containers/Providers/ModelHandler.tsx | 8 ++- web/package.json | 2 +- .../ThreadCenterPanel/ChatBody/index.tsx | 7 ++- .../TextMessage/ThinkingBlock.tsx | 59 +++++++++++++++++++ .../ThreadCenterPanel/TextMessage/index.tsx | 25 +++++++- web/tailwind.config.js | 5 ++ yarn.lock | 59 ++++--------------- 8 files changed, 127 insertions(+), 62 deletions(-) create mode 100644 web/screens/Thread/ThreadCenterPanel/TextMessage/ThinkingBlock.tsx diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 685b1f7a6..1be5d6cef 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -509,61 +509,61 @@ __metadata: "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=546b6d&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=af6a7b&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/abb0aeb570396ef5d2fe482410a178f422ced7da132fa28f92b930eba191439a6a9b3567641c283eb6bf6413ace2950669393fc8f1e415d442e22d7107c23a44 + checksum: 10c0/482f1f5363ca64f48a40c3d2937ac07dcf42a6d2dd6c82356359cec986e9312611f84ca8cdaed4ef338d978cd163909a2708d75f8e957aa51b09447aae83eca0 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=546b6d&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=af6a7b&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/abb0aeb570396ef5d2fe482410a178f422ced7da132fa28f92b930eba191439a6a9b3567641c283eb6bf6413ace2950669393fc8f1e415d442e22d7107c23a44 + checksum: 10c0/482f1f5363ca64f48a40c3d2937ac07dcf42a6d2dd6c82356359cec986e9312611f84ca8cdaed4ef338d978cd163909a2708d75f8e957aa51b09447aae83eca0 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=546b6d&locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=af6a7b&locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/abb0aeb570396ef5d2fe482410a178f422ced7da132fa28f92b930eba191439a6a9b3567641c283eb6bf6413ace2950669393fc8f1e415d442e22d7107c23a44 + checksum: 10c0/482f1f5363ca64f48a40c3d2937ac07dcf42a6d2dd6c82356359cec986e9312611f84ca8cdaed4ef338d978cd163909a2708d75f8e957aa51b09447aae83eca0 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=546b6d&locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=af6a7b&locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/abb0aeb570396ef5d2fe482410a178f422ced7da132fa28f92b930eba191439a6a9b3567641c283eb6bf6413ace2950669393fc8f1e415d442e22d7107c23a44 + checksum: 10c0/482f1f5363ca64f48a40c3d2937ac07dcf42a6d2dd6c82356359cec986e9312611f84ca8cdaed4ef338d978cd163909a2708d75f8e957aa51b09447aae83eca0 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=546b6d&locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=af6a7b&locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/abb0aeb570396ef5d2fe482410a178f422ced7da132fa28f92b930eba191439a6a9b3567641c283eb6bf6413ace2950669393fc8f1e415d442e22d7107c23a44 + checksum: 10c0/482f1f5363ca64f48a40c3d2937ac07dcf42a6d2dd6c82356359cec986e9312611f84ca8cdaed4ef338d978cd163909a2708d75f8e957aa51b09447aae83eca0 languageName: node linkType: hard "@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension": version: 0.1.10 - resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=546b6d&locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension" + resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=af6a7b&locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension" dependencies: rxjs: "npm:^7.8.1" ulidx: "npm:^2.3.0" - checksum: 10c0/abb0aeb570396ef5d2fe482410a178f422ced7da132fa28f92b930eba191439a6a9b3567641c283eb6bf6413ace2950669393fc8f1e415d442e22d7107c23a44 + checksum: 10c0/482f1f5363ca64f48a40c3d2937ac07dcf42a6d2dd6c82356359cec986e9312611f84ca8cdaed4ef338d978cd163909a2708d75f8e957aa51b09447aae83eca0 languageName: node linkType: hard diff --git a/web/containers/Providers/ModelHandler.tsx b/web/containers/Providers/ModelHandler.tsx index 2c027539e..ad6d817f1 100644 --- a/web/containers/Providers/ModelHandler.tsx +++ b/web/containers/Providers/ModelHandler.tsx @@ -290,7 +290,13 @@ export default function ModelHandler() { .catch(() => undefined) if (updatedMessage) { deleteMessage(message.id) - addNewMessage(updatedMessage) + addNewMessage({ + ...updatedMessage, + metadata: { + ...updatedMessage.metadata, + reserve_id: message.id, + }, + }) setTokenSpeed((prev) => prev ? { ...prev, message: updatedMessage.id } : undefined ) diff --git a/web/package.json b/web/package.json index c562b6aa6..0f66b3f00 100644 --- a/web/package.json +++ b/web/package.json @@ -56,7 +56,7 @@ "slate-react": "0.110.3", "swr": "^2.2.5", "tailwind-merge": "^2.0.0", - "tailwindcss": "3.3.5", + "tailwindcss": "3.4.17", "ulidx": "^2.3.0", "use-debounce": "^10.0.0", "uuid": "^9.0.1", diff --git a/web/screens/Thread/ThreadCenterPanel/ChatBody/index.tsx b/web/screens/Thread/ThreadCenterPanel/ChatBody/index.tsx index c47d19d67..1f4308bf3 100644 --- a/web/screens/Thread/ThreadCenterPanel/ChatBody/index.tsx +++ b/web/screens/Thread/ThreadCenterPanel/ChatBody/index.tsx @@ -160,7 +160,12 @@ const ChatBody = memo( > {items.map((virtualRow) => (
diff --git a/web/screens/Thread/ThreadCenterPanel/TextMessage/ThinkingBlock.tsx b/web/screens/Thread/ThreadCenterPanel/TextMessage/ThinkingBlock.tsx new file mode 100644 index 000000000..59ff720c8 --- /dev/null +++ b/web/screens/Thread/ThreadCenterPanel/TextMessage/ThinkingBlock.tsx @@ -0,0 +1,59 @@ +import React from 'react' + +import { atom, useAtom } from 'jotai' +import { ChevronDown, ChevronUp, Loader } from 'lucide-react' + +interface Props { + text: string + status: string + id: string +} + +const thinkingBlockStateAtom = atom<{ [id: string]: boolean }>({}) + +const ThinkingBlock = ({ id, text, status }: Props) => { + const [thinkingState, setThinkingState] = useAtom(thinkingBlockStateAtom) + + const isExpanded = thinkingState[id] ?? false + + const loading = !text.includes('') && status === 'pending' + + const handleClick = () => { + setThinkingState((prev) => ({ ...prev, [id]: !isExpanded })) + } + + if (!text.replace(/<\/?think>/g, '').trim()) return null + + return ( +
+
+
+ {loading && ( + + )} + +
+ + {isExpanded && ( +
+ {text.replace(/<\/?think>/g, '').trim()} +
+ )} +
+
+ ) +} + +export default ThinkingBlock diff --git a/web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx b/web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx index a44adb431..efa2b084c 100644 --- a/web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx +++ b/web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx @@ -16,6 +16,7 @@ import MessageToolbar from '../MessageToolbar' import DocMessage from './DocMessage' import ImageMessage from './ImageMessage' import { MarkdownTextMessage } from './MarkdownTextMessage' +import ThinkingBlock from './ThinkingBlock' import { activeAssistantAtom } from '@/helpers/atoms/Assistant.atom' import { @@ -41,6 +42,21 @@ const MessageContainer: React.FC< [props.content] ) + const { reasoningSegment, textSegment } = useMemo(() => { + const isThinking = text.includes('') && !text.includes('') + if (isThinking) return { reasoningSegment: text, textSegment: '' } + + const match = text.match(/([\s\S]*?)<\/think>/) + if (match?.index === undefined) + return { reasoningSegment: undefined, textSegment: text } + + const splitIndex = match.index + match[0].length + return { + reasoningSegment: text.slice(0, splitIndex), + textSegment: text.slice(splitIndex), + } + }, [text]) + const image = useMemo( () => props.content.find((e) => e.type === ContentType.Image)?.image_url?.url, @@ -144,9 +160,16 @@ const MessageContainer: React.FC< )} dir="ltr" > + {reasoningSegment && ( + + )}
diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 2d9685c1a..e7ad20019 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -18,6 +18,7 @@ module.exports = { 'slide-in': 'slide-in 1.2s cubic-bezier(.41,.73,.51,1.02)', 'leave': 'leave 150ms ease-in forwards', 'bounce-right': 'bounce-right 3s infinite', + 'spin': 'spin 2s linear infinite', }, keyframes: { 'wave': { @@ -47,6 +48,10 @@ module.exports = { '40%': { transform: 'translateX(-8px)' }, '60%': { transform: 'translateX(-4px)' }, }, + 'spin': { + '0%': { transform: 'rotate(0deg)' }, + '100%': { transform: 'rotate(360deg)' }, + }, }, extend: { fontFamily: { diff --git a/yarn.lock b/yarn.lock index fcd00767c..5367fc4f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1138,7 +1138,7 @@ __metadata: slate-react: "npm:0.110.3" swr: "npm:^2.2.5" tailwind-merge: "npm:^2.0.0" - tailwindcss: "npm:3.3.5" + tailwindcss: "npm:3.4.17" ts-jest: "npm:^29.2.5" typescript: "npm:^5.3.3" ulidx: "npm:^2.3.0" @@ -6141,7 +6141,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": +"chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -8451,7 +8451,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.2": +"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" dependencies: @@ -11483,7 +11483,7 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^1.19.1, jiti@npm:^1.21.6": +"jiti@npm:^1.21.6": version: 1.21.7 resolution: "jiti@npm:1.21.7" bin: @@ -11946,7 +11946,7 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:^2.0.3, lilconfig@npm:^2.0.5, lilconfig@npm:^2.1.0": +"lilconfig@npm:^2.0.3, lilconfig@npm:^2.0.5": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" checksum: 10c0/64645641aa8d274c99338e130554abd6a0190533c0d9eb2ce7ebfaf2e05c7d9961f3ffe2bfa39efd3b60c521ba3dd24fa236fe2775fc38501bf82bf49d4678b8 @@ -14609,7 +14609,7 @@ __metadata: languageName: node linkType: hard -"postcss-load-config@npm:^4.0.1, postcss-load-config@npm:^4.0.2": +"postcss-load-config@npm:^4.0.2": version: 4.0.2 resolution: "postcss-load-config@npm:4.0.2" dependencies: @@ -14763,7 +14763,7 @@ __metadata: languageName: node linkType: hard -"postcss-nested@npm:^6.0.1, postcss-nested@npm:^6.2.0": +"postcss-nested@npm:^6.2.0": version: 6.2.0 resolution: "postcss-nested@npm:6.2.0" dependencies: @@ -14908,7 +14908,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.5, postcss-selector-parser@npm:^6.0.9, postcss-selector-parser@npm:^6.1.1, postcss-selector-parser@npm:^6.1.2": +"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.5, postcss-selector-parser@npm:^6.0.9, postcss-selector-parser@npm:^6.1.1, postcss-selector-parser@npm:^6.1.2": version: 6.1.2 resolution: "postcss-selector-parser@npm:6.1.2" dependencies: @@ -14983,7 +14983,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.23, postcss@npm:^8.4.47": +"postcss@npm:^8.4.47": version: 8.4.49 resolution: "postcss@npm:8.4.49" dependencies: @@ -15959,7 +15959,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.7, resolve@npm:^1.11.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.2, resolve@npm:^1.22.4, resolve@npm:^1.22.8": +"resolve@npm:^1.1.7, resolve@npm:^1.11.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.22.8": version: 1.22.10 resolution: "resolve@npm:1.22.10" dependencies: @@ -15985,7 +15985,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.11.0#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.2#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.11.0#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": version: 1.22.10 resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d" dependencies: @@ -17449,7 +17449,7 @@ __metadata: languageName: node linkType: hard -"sucrase@npm:^3.32.0, sucrase@npm:^3.35.0": +"sucrase@npm:^3.35.0": version: 3.35.0 resolution: "sucrase@npm:3.35.0" dependencies: @@ -17561,40 +17561,7 @@ __metadata: languageName: node linkType: hard -"tailwindcss@npm:3.3.5": - version: 3.3.5 - resolution: "tailwindcss@npm:3.3.5" - dependencies: - "@alloc/quick-lru": "npm:^5.2.0" - arg: "npm:^5.0.2" - chokidar: "npm:^3.5.3" - didyoumean: "npm:^1.2.2" - dlv: "npm:^1.1.3" - fast-glob: "npm:^3.3.0" - glob-parent: "npm:^6.0.2" - is-glob: "npm:^4.0.3" - jiti: "npm:^1.19.1" - lilconfig: "npm:^2.1.0" - micromatch: "npm:^4.0.5" - normalize-path: "npm:^3.0.0" - object-hash: "npm:^3.0.0" - picocolors: "npm:^1.0.0" - postcss: "npm:^8.4.23" - postcss-import: "npm:^15.1.0" - postcss-js: "npm:^4.0.1" - postcss-load-config: "npm:^4.0.1" - postcss-nested: "npm:^6.0.1" - postcss-selector-parser: "npm:^6.0.11" - resolve: "npm:^1.22.2" - sucrase: "npm:^3.32.0" - bin: - tailwind: lib/cli.js - tailwindcss: lib/cli.js - checksum: 10c0/a57c0a9cdba9db19097e34e25b7e4690fab43f31ba200afc3bb9635a03036ca93e9884a17b616fb8a2486d57d2ecc9a06862ce4685b3ace57f7a67436e7594a0 - languageName: node - linkType: hard - -"tailwindcss@npm:^3.4.1": +"tailwindcss@npm:3.4.17, tailwindcss@npm:^3.4.1": version: 3.4.17 resolution: "tailwindcss@npm:3.4.17" dependencies: