mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
feat: add support for thought processing in conversation flow and introduce ReActAgent
This commit is contained in:
11
frontend/src/assets/cloud.svg
Normal file
11
frontend/src/assets/cloud.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 16C2.53043 16 3.03914 16.2107 3.41421 16.5858C3.78929 16.9609 4 17.4696 4 18C4 18.5304 3.78929 19.0391 3.41421 19.4142C3.03914 19.7893 2.53043 20 2 20C1.46957 20 0.960859 19.7893 0.585786 19.4142C0.210714 19.0391 0 18.5304 0 18C0 17.4696 0.210714 16.9609 0.585786 16.5858C0.960859 16.2107 1.46957 16 2 16ZM7.5 13C8.16304 13 8.79893 13.2634 9.26777 13.7322C9.73661 14.2011 10 14.837 10 15.5C10 16.163 9.73661 16.7989 9.26777 17.2678C8.79893 17.7366 8.16304 18 7.5 18C6.83696 18 6.20107 17.7366 5.73223 17.2678C5.26339 16.7989 5 16.163 5 15.5C5 14.837 5.26339 14.2011 5.73223 13.7322C6.20107 13.2634 6.83696 13 7.5 13ZM10 0C11.272 0.000250351 12.5033 0.448355 13.4779 1.26572C14.4525 2.08308 15.1082 3.2175 15.33 4.47H15.412C16.4105 4.47 17.3682 4.86667 18.0743 5.57274C18.7803 6.27882 19.177 7.23646 19.177 8.235C19.177 9.23354 18.7803 10.1912 18.0743 10.8973C17.3682 11.6033 16.4105 12 15.412 12H4.588C3.58946 12 2.63182 11.6033 1.92574 10.8973C1.21967 10.1912 0.823 9.23354 0.823 8.235C0.823 7.23646 1.21967 6.27882 1.92574 5.57274C2.63182 4.86667 3.58946 4.47 4.588 4.47H4.67C4.89179 3.2175 5.54749 2.08308 6.52211 1.26572C7.49673 0.448355 8.72801 0.000250351 10 0Z" fill="url(#paint0_linear_6161_11984)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_6161_11984" x1="0" y1="10" x2="19.177" y2="10" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#70FDF7"/>
|
||||
<stop offset="0.325" stop-color="#747696"/>
|
||||
<stop offset="0.68" stop-color="#BD5372"/>
|
||||
<stop offset="1" stop-color="#F5A06C"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -6,16 +6,16 @@ import ReactMarkdown from 'react-markdown';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import {
|
||||
vscDarkPlus,
|
||||
oneLight,
|
||||
vscDarkPlus,
|
||||
} from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import { useDarkTheme } from '../hooks';
|
||||
|
||||
import DocsGPT3 from '../assets/cute_docsgpt3.svg';
|
||||
import ChevronDown from '../assets/chevron-down.svg';
|
||||
import Cloud from '../assets/cloud.svg';
|
||||
import DocsGPT3 from '../assets/cute_docsgpt3.svg';
|
||||
import Dislike from '../assets/dislike.svg?react';
|
||||
import Document from '../assets/document.svg';
|
||||
import Edit from '../assets/edit.svg';
|
||||
@@ -28,7 +28,7 @@ import Avatar from '../components/Avatar';
|
||||
import CopyButton from '../components/CopyButton';
|
||||
import Sidebar from '../components/Sidebar';
|
||||
import SpeakButton from '../components/TextToSpeechButton';
|
||||
import { useOutsideAlerter } from '../hooks';
|
||||
import { useDarkTheme, useOutsideAlerter } from '../hooks';
|
||||
import {
|
||||
selectChunks,
|
||||
selectSelectedDocs,
|
||||
@@ -42,11 +42,12 @@ const DisableSourceFE = import.meta.env.VITE_DISABLE_SOURCE_FE || false;
|
||||
const ConversationBubble = forwardRef<
|
||||
HTMLDivElement,
|
||||
{
|
||||
message: string;
|
||||
message?: string;
|
||||
type: MESSAGE_TYPE;
|
||||
className?: string;
|
||||
feedback?: FEEDBACK;
|
||||
handleFeedback?: (feedback: FEEDBACK) => void;
|
||||
thought?: string;
|
||||
sources?: { title: string; text: string; source: string }[];
|
||||
toolCalls?: ToolCallsType[];
|
||||
retryBtn?: React.ReactElement;
|
||||
@@ -64,6 +65,7 @@ const ConversationBubble = forwardRef<
|
||||
className,
|
||||
feedback,
|
||||
handleFeedback,
|
||||
thought,
|
||||
sources,
|
||||
toolCalls,
|
||||
retryBtn,
|
||||
@@ -125,7 +127,7 @@ const ConversationBubble = forwardRef<
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsEditClicked(true);
|
||||
setEditInputBox(message);
|
||||
setEditInputBox(message ?? '');
|
||||
}}
|
||||
className={`flex-shrink-0 h-fit mt-3 p-2 cursor-pointer rounded-full hover:bg-light-silver dark:hover:bg-[#35363B] flex items-center ${isQuestionHovered || isEditClicked ? 'visible' : 'invisible'}`}
|
||||
>
|
||||
@@ -329,230 +331,237 @@ const ConversationBubble = forwardRef<
|
||||
{toolCalls && toolCalls.length > 0 && (
|
||||
<ToolCalls toolCalls={toolCalls} />
|
||||
)}
|
||||
<div className="flex flex-col flex-wrap items-start self-start lg:flex-nowrap">
|
||||
<div className="my-2 flex flex-row items-center justify-center gap-3">
|
||||
<Avatar
|
||||
className="h-[34px] w-[34px] text-2xl"
|
||||
avatar={
|
||||
<img
|
||||
src={DocsGPT3}
|
||||
alt={t('conversation.answer')}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<p className="text-base font-semibold">
|
||||
{t('conversation.answer')}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className={`fade-in-bubble ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[18px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
type === 'ERROR'
|
||||
? 'relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal text-red-3000 dark:border-red-2000 dark:text-white'
|
||||
: 'flex-col rounded-3xl'
|
||||
}`}
|
||||
>
|
||||
<ReactMarkdown
|
||||
className="fade-in whitespace-pre-wrap break-words leading-normal"
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
code(props) {
|
||||
const { children, className, node, ref, ...rest } = props;
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const language = match ? match[1] : '';
|
||||
|
||||
return match ? (
|
||||
<div className="group relative rounded-[14px] overflow-hidden border border-light-silver dark:border-raisin-black">
|
||||
<div className="flex justify-between items-center px-2 py-1 bg-platinum dark:bg-eerie-black-2">
|
||||
<span className="text-xs font-medium text-just-black dark:text-chinese-white">
|
||||
{language}
|
||||
</span>
|
||||
<CopyButton
|
||||
text={String(children).replace(/\n$/, '')}
|
||||
/>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
{...rest}
|
||||
PreTag="div"
|
||||
language={language}
|
||||
style={isDarkTheme ? vscDarkPlus : oneLight}
|
||||
className="!mt-0"
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
borderRadius: 0,
|
||||
scrollbarWidth: 'thin',
|
||||
}}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
) : (
|
||||
<code className="whitespace-pre-line rounded-[6px] bg-gray-200 px-[8px] py-[4px] text-xs font-normal dark:bg-independence dark:text-bright-gray">
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
ul({ children }) {
|
||||
return (
|
||||
<ul
|
||||
className={`list-inside list-disc whitespace-normal pl-4 ${classes.list}`}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
ol({ children }) {
|
||||
return (
|
||||
<ol
|
||||
className={`list-inside list-decimal whitespace-normal pl-4 ${classes.list}`}
|
||||
>
|
||||
{children}
|
||||
</ol>
|
||||
);
|
||||
},
|
||||
table({ children }) {
|
||||
return (
|
||||
<div className="relative overflow-x-auto rounded-lg border border-silver/40 dark:border-silver/40">
|
||||
<table className="w-full text-left text-gray-700 dark:text-bright-gray">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
thead({ children }) {
|
||||
return (
|
||||
<thead className="text-xs uppercase text-gray-900 dark:text-bright-gray bg-gray-50 dark:bg-[#26272E]/50">
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
},
|
||||
tr({ children }) {
|
||||
return (
|
||||
<tr className="border-b border-gray-200 dark:border-silver/40 odd:bg-white dark:odd:bg-[#26272E] even:bg-gray-50 dark:even:bg-[#26272E]/50">
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="px-6 py-3">{children}</th>;
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="px-6 py-3">{children}</td>;
|
||||
},
|
||||
}}
|
||||
{thought && (
|
||||
<Thought thought={thought} preprocessLaTeX={preprocessLaTeX} />
|
||||
)}
|
||||
{message && (
|
||||
<div className="flex flex-col flex-wrap items-start self-start lg:flex-nowrap">
|
||||
<div className="my-2 flex flex-row items-center justify-center gap-3">
|
||||
<Avatar
|
||||
className="h-[34px] w-[34px] text-2xl"
|
||||
avatar={
|
||||
<img
|
||||
src={DocsGPT3}
|
||||
alt={t('conversation.answer')}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<p className="text-base font-semibold">
|
||||
{t('conversation.answer')}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className={`fade-in-bubble ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[18px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
type === 'ERROR'
|
||||
? 'relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal text-red-3000 dark:border-red-2000 dark:text-white'
|
||||
: 'flex-col rounded-3xl'
|
||||
}`}
|
||||
>
|
||||
{preprocessLaTeX(message)}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-2 ml-2 flex justify-start">
|
||||
<div
|
||||
className={`relative mr-2 block items-center justify-center lg:invisible
|
||||
${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
|
||||
>
|
||||
<div>
|
||||
<CopyButton text={message} />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`relative mr-2 block items-center justify-center lg:invisible
|
||||
${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
|
||||
>
|
||||
<div>
|
||||
<SpeakButton text={message} />
|
||||
</div>
|
||||
</div>
|
||||
{type === 'ERROR' && (
|
||||
<div className="relative mr-2 block items-center justify-center">
|
||||
<div>{retryBtn}</div>
|
||||
</div>
|
||||
)}
|
||||
{handleFeedback && (
|
||||
<>
|
||||
<div
|
||||
className={`relative mr-2 flex items-center justify-center ${
|
||||
feedback === 'LIKE' || isLikeClicked
|
||||
? 'visible'
|
||||
: 'lg:invisible'
|
||||
} ${type !== 'ERROR' ? 'group-hover:lg:visible' : ''}
|
||||
${feedback === 'DISLIKE' && type !== 'ERROR' ? 'hidden' : ''}`}
|
||||
<ReactMarkdown
|
||||
className="fade-in whitespace-pre-wrap break-words leading-normal"
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
code(props) {
|
||||
const { children, className, node, ref, ...rest } = props;
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const language = match ? match[1] : '';
|
||||
|
||||
return match ? (
|
||||
<div className="group relative rounded-[14px] overflow-hidden border border-light-silver dark:border-raisin-black">
|
||||
<div className="flex justify-between items-center px-2 py-1 bg-platinum dark:bg-eerie-black-2">
|
||||
<span className="text-xs font-medium text-just-black dark:text-chinese-white">
|
||||
{language}
|
||||
</span>
|
||||
<CopyButton
|
||||
text={String(children).replace(/\n$/, '')}
|
||||
/>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
{...rest}
|
||||
PreTag="div"
|
||||
language={language}
|
||||
style={isDarkTheme ? vscDarkPlus : oneLight}
|
||||
className="!mt-0"
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
borderRadius: 0,
|
||||
scrollbarWidth: 'thin',
|
||||
}}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
) : (
|
||||
<code className="whitespace-pre-line rounded-[6px] bg-gray-200 px-[8px] py-[4px] text-xs font-normal dark:bg-independence dark:text-bright-gray">
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
ul({ children }) {
|
||||
return (
|
||||
<ul
|
||||
className={`list-inside list-disc whitespace-normal pl-4 ${classes.list}`}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
ol({ children }) {
|
||||
return (
|
||||
<ol
|
||||
className={`list-inside list-decimal whitespace-normal pl-4 ${classes.list}`}
|
||||
>
|
||||
{children}
|
||||
</ol>
|
||||
);
|
||||
},
|
||||
table({ children }) {
|
||||
return (
|
||||
<div className="relative overflow-x-auto rounded-lg border border-silver/40 dark:border-silver/40">
|
||||
<table className="w-full text-left text-gray-700 dark:text-bright-gray">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
thead({ children }) {
|
||||
return (
|
||||
<thead className="text-xs uppercase text-gray-900 dark:text-bright-gray bg-gray-50 dark:bg-[#26272E]/50">
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
},
|
||||
tr({ children }) {
|
||||
return (
|
||||
<tr className="border-b border-gray-200 dark:border-silver/40 odd:bg-white dark:odd:bg-[#26272E] even:bg-gray-50 dark:even:bg-[#26272E]/50">
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="px-6 py-3">{children}</th>;
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="px-6 py-3">{children}</td>;
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full p-2 ${
|
||||
isLikeHovered
|
||||
? 'bg-[#EEEEEE] dark:bg-purple-taupe'
|
||||
: 'bg-[#ffffff] dark:bg-transparent'
|
||||
}`}
|
||||
>
|
||||
<Like
|
||||
className={`cursor-pointer
|
||||
{preprocessLaTeX(message)}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{message && (
|
||||
<div className="my-2 ml-2 flex justify-start">
|
||||
<div
|
||||
className={`relative mr-2 block items-center justify-center lg:invisible
|
||||
${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
|
||||
>
|
||||
<div>
|
||||
<CopyButton text={message} />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`relative mr-2 block items-center justify-center lg:invisible
|
||||
${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
|
||||
>
|
||||
<div>
|
||||
<SpeakButton text={message} />
|
||||
</div>
|
||||
</div>
|
||||
{type === 'ERROR' && (
|
||||
<div className="relative mr-2 block items-center justify-center">
|
||||
<div>{retryBtn}</div>
|
||||
</div>
|
||||
)}
|
||||
{handleFeedback && (
|
||||
<>
|
||||
<div
|
||||
className={`relative mr-2 flex items-center justify-center ${
|
||||
feedback === 'LIKE' || isLikeClicked
|
||||
? 'visible'
|
||||
: 'lg:invisible'
|
||||
} ${type !== 'ERROR' ? 'group-hover:lg:visible' : ''}
|
||||
${feedback === 'DISLIKE' && type !== 'ERROR' ? 'hidden' : ''}`}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full p-2 ${
|
||||
isLikeHovered
|
||||
? 'bg-[#EEEEEE] dark:bg-purple-taupe'
|
||||
: 'bg-[#ffffff] dark:bg-transparent'
|
||||
}`}
|
||||
>
|
||||
<Like
|
||||
className={`cursor-pointer
|
||||
${
|
||||
isLikeClicked || feedback === 'LIKE'
|
||||
? 'fill-white-3000 stroke-purple-30 dark:fill-transparent'
|
||||
: 'fill-none stroke-gray-4000'
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (feedback === undefined || feedback === null) {
|
||||
handleFeedback?.('LIKE');
|
||||
setIsLikeClicked(true);
|
||||
setIsDislikeClicked(false);
|
||||
} else if (feedback === 'LIKE') {
|
||||
handleFeedback?.(null);
|
||||
setIsLikeClicked(false);
|
||||
setIsDislikeClicked(false);
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => setIsLikeHovered(true)}
|
||||
onMouseLeave={() => setIsLikeHovered(false)}
|
||||
></Like>
|
||||
onClick={() => {
|
||||
if (feedback === undefined || feedback === null) {
|
||||
handleFeedback?.('LIKE');
|
||||
setIsLikeClicked(true);
|
||||
setIsDislikeClicked(false);
|
||||
} else if (feedback === 'LIKE') {
|
||||
handleFeedback?.(null);
|
||||
setIsLikeClicked(false);
|
||||
setIsDislikeClicked(false);
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => setIsLikeHovered(true)}
|
||||
onMouseLeave={() => setIsLikeHovered(false)}
|
||||
></Like>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`relative mr-2 flex items-center justify-center ${
|
||||
feedback === 'DISLIKE' || isLikeClicked
|
||||
? 'visible'
|
||||
: 'lg:invisible'
|
||||
} ${type !== 'ERROR' ? 'group-hover:lg:visible' : ''}
|
||||
<div
|
||||
className={`relative mr-2 flex items-center justify-center ${
|
||||
feedback === 'DISLIKE' || isLikeClicked
|
||||
? 'visible'
|
||||
: 'lg:invisible'
|
||||
} ${type !== 'ERROR' ? 'group-hover:lg:visible' : ''}
|
||||
${feedback === 'LIKE' && type !== 'ERROR' ? 'hidden' : ''}`}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full p-2 ${
|
||||
isDislikeHovered
|
||||
? 'bg-[#EEEEEE] dark:bg-purple-taupe'
|
||||
: 'bg-[#ffffff] dark:bg-transparent'
|
||||
}`}
|
||||
>
|
||||
<Dislike
|
||||
className={`cursor-pointer ${
|
||||
isDislikeClicked || feedback === 'DISLIKE'
|
||||
? 'fill-white-3000 stroke-red-2000 dark:fill-transparent'
|
||||
: 'fill-none stroke-gray-4000'
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full p-2 ${
|
||||
isDislikeHovered
|
||||
? 'bg-[#EEEEEE] dark:bg-purple-taupe'
|
||||
: 'bg-[#ffffff] dark:bg-transparent'
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (feedback === undefined || feedback === null) {
|
||||
handleFeedback?.('DISLIKE');
|
||||
setIsDislikeClicked(true);
|
||||
setIsLikeClicked(false);
|
||||
} else if (feedback === 'DISLIKE') {
|
||||
handleFeedback?.(null);
|
||||
setIsLikeClicked(false);
|
||||
setIsDislikeClicked(false);
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => setIsDislikeHovered(true)}
|
||||
onMouseLeave={() => setIsDislikeHovered(false)}
|
||||
></Dislike>
|
||||
>
|
||||
<Dislike
|
||||
className={`cursor-pointer ${
|
||||
isDislikeClicked || feedback === 'DISLIKE'
|
||||
? 'fill-white-3000 stroke-red-2000 dark:fill-transparent'
|
||||
: 'fill-none stroke-gray-4000'
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (feedback === undefined || feedback === null) {
|
||||
handleFeedback?.('DISLIKE');
|
||||
setIsDislikeClicked(true);
|
||||
setIsLikeClicked(false);
|
||||
} else if (feedback === 'DISLIKE') {
|
||||
handleFeedback?.(null);
|
||||
setIsLikeClicked(false);
|
||||
setIsDislikeClicked(false);
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => setIsDislikeHovered(true)}
|
||||
onMouseLeave={() => setIsDislikeHovered(false)}
|
||||
></Dislike>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{sources && (
|
||||
<Sidebar
|
||||
isOpen={isSidebarOpen}
|
||||
@@ -699,3 +708,136 @@ function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Thought({
|
||||
thought,
|
||||
preprocessLaTeX,
|
||||
}: {
|
||||
thought: string;
|
||||
preprocessLaTeX: (content: string) => string;
|
||||
}) {
|
||||
const [isDarkTheme] = useDarkTheme();
|
||||
const [isThoughtOpen, setIsThoughtOpen] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="mb-4 w-full flex flex-col flex-wrap items-start self-start lg:flex-nowrap">
|
||||
<div className="my-2 flex flex-row items-center justify-center gap-3">
|
||||
<Avatar
|
||||
className="h-[26px] w-[30px] text-xl"
|
||||
avatar={
|
||||
<img
|
||||
src={Cloud}
|
||||
alt={'Thought'}
|
||||
className="h-full w-full object-fill"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<button
|
||||
className="flex flex-row items-center gap-2"
|
||||
onClick={() => setIsThoughtOpen(!isThoughtOpen)}
|
||||
>
|
||||
<p className="text-base font-semibold">Reasoning</p>
|
||||
<img
|
||||
src={ChevronDown}
|
||||
alt="ChevronDown"
|
||||
className={`h-4 w-4 transform transition-transform duration-200 dark:invert ${isThoughtOpen ? 'rotate-180' : ''}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{isThoughtOpen && (
|
||||
<div className="fade-in ml-2 mr-5 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
|
||||
<div className="rounded-[28px] bg-gray-1000 py-[18px] px-7 dark:bg-gun-metal">
|
||||
<ReactMarkdown
|
||||
className="fade-in whitespace-pre-wrap break-words leading-normal"
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
code(props) {
|
||||
const { children, className, node, ref, ...rest } = props;
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const language = match ? match[1] : '';
|
||||
|
||||
return match ? (
|
||||
<div className="group relative rounded-[14px] overflow-hidden border border-light-silver dark:border-raisin-black">
|
||||
<div className="flex justify-between items-center px-2 py-1 bg-platinum dark:bg-eerie-black-2">
|
||||
<span className="text-xs font-medium text-just-black dark:text-chinese-white">
|
||||
{language}
|
||||
</span>
|
||||
<CopyButton
|
||||
text={String(children).replace(/\n$/, '')}
|
||||
/>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
{...rest}
|
||||
PreTag="div"
|
||||
language={language}
|
||||
style={isDarkTheme ? vscDarkPlus : oneLight}
|
||||
className="!mt-0"
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
borderRadius: 0,
|
||||
scrollbarWidth: 'thin',
|
||||
}}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
) : (
|
||||
<code className="whitespace-pre-line rounded-[6px] bg-gray-200 px-[8px] py-[4px] text-xs font-normal dark:bg-independence dark:text-bright-gray">
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
ul({ children }) {
|
||||
return (
|
||||
<ul className="list-inside list-disc whitespace-normal pl-4">
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
ol({ children }) {
|
||||
return (
|
||||
<ol className="list-inside list-decimal whitespace-normal pl-4">
|
||||
{children}
|
||||
</ol>
|
||||
);
|
||||
},
|
||||
table({ children }) {
|
||||
return (
|
||||
<div className="relative overflow-x-auto rounded-lg border border-silver/40 dark:border-silver/40">
|
||||
<table className="w-full text-left text-gray-700 dark:text-bright-gray">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
thead({ children }) {
|
||||
return (
|
||||
<thead className="text-xs uppercase text-gray-900 dark:text-bright-gray bg-gray-50 dark:bg-[#26272E]/50">
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
},
|
||||
tr({ children }) {
|
||||
return (
|
||||
<tr className="border-b border-gray-200 dark:border-silver/40 odd:bg-white dark:odd:bg-[#26272E] even:bg-gray-50 dark:even:bg-[#26272E]/50">
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="px-6 py-3">{children}</th>;
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="px-6 py-3">{children}</td>;
|
||||
},
|
||||
}}
|
||||
>
|
||||
{preprocessLaTeX(thought ?? '')}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ConversationBubble from './ConversationBubble';
|
||||
import Hero from '../Hero';
|
||||
import { FEEDBACK, Query, Status } from './conversationModels';
|
||||
|
||||
import ArrowDown from '../assets/arrow-down.svg';
|
||||
import RetryIcon from '../components/RetryIcon';
|
||||
import Hero from '../Hero';
|
||||
import { useDarkTheme } from '../hooks';
|
||||
import ConversationBubble from './ConversationBubble';
|
||||
import { FEEDBACK, Query, Status } from './conversationModels';
|
||||
|
||||
interface ConversationMessagesProps {
|
||||
handleQuestion: (params: {
|
||||
@@ -83,13 +84,14 @@ export default function ConversationMessages({
|
||||
|
||||
const prepResponseView = (query: Query, index: number) => {
|
||||
let responseView;
|
||||
if (query.response) {
|
||||
if (query.thought || query.response) {
|
||||
responseView = (
|
||||
<ConversationBubble
|
||||
className={`${index === queries.length - 1 ? 'mb-32' : 'mb-7'}`}
|
||||
key={`${index}ANSWER`}
|
||||
message={query.response}
|
||||
type={'ANSWER'}
|
||||
thought={query.thought}
|
||||
sources={query.sources}
|
||||
toolCalls={query.tool_calls}
|
||||
feedback={query.feedback}
|
||||
|
||||
@@ -17,6 +17,7 @@ export function handleFetchAnswer(
|
||||
| {
|
||||
result: any;
|
||||
answer: any;
|
||||
thought: any;
|
||||
sources: any;
|
||||
toolCalls: ToolCallsType[];
|
||||
conversationId: any;
|
||||
@@ -25,6 +26,7 @@ export function handleFetchAnswer(
|
||||
| {
|
||||
result: any;
|
||||
answer: any;
|
||||
thought: any;
|
||||
sources: any;
|
||||
toolCalls: ToolCallsType[];
|
||||
query: string;
|
||||
@@ -67,9 +69,11 @@ export function handleFetchAnswer(
|
||||
answer: result,
|
||||
query: question,
|
||||
result,
|
||||
thought: data.thought,
|
||||
sources: data.sources,
|
||||
toolCalls: data.tool_calls,
|
||||
conversationId: data.conversation_id,
|
||||
title: data.title || null,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -91,6 +95,7 @@ export function handleFetchAnswerSteaming(
|
||||
return {
|
||||
prompt: item.prompt,
|
||||
response: item.response,
|
||||
thought: item.thought,
|
||||
tool_calls: item.tool_calls,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface Answer {
|
||||
result: string;
|
||||
conversationId: string | null;
|
||||
title: string | null;
|
||||
thought: string;
|
||||
sources: { title: string; text: string; source: string }[];
|
||||
tool_calls: ToolCallsType[];
|
||||
}
|
||||
@@ -32,6 +33,7 @@ export interface Query {
|
||||
error?: string;
|
||||
conversationId?: string | null;
|
||||
title?: string | null;
|
||||
thought?: string;
|
||||
sources?: { title: string; text: string; source: string }[];
|
||||
tool_calls?: ToolCallsType[];
|
||||
}
|
||||
|
||||
@@ -75,6 +75,15 @@ export const fetchAnswer = createAsyncThunk<
|
||||
query: { conversationId: data.id },
|
||||
}),
|
||||
);
|
||||
} else if (data.type === 'thought') {
|
||||
const result = data.thought;
|
||||
console.log('thought', result);
|
||||
dispatch(
|
||||
updateThought({
|
||||
index: indx ?? state.conversation.queries.length - 1,
|
||||
query: { thought: result },
|
||||
}),
|
||||
);
|
||||
} else if (data.type === 'source') {
|
||||
isSourceUpdated = true;
|
||||
dispatch(
|
||||
@@ -141,6 +150,7 @@ export const fetchAnswer = createAsyncThunk<
|
||||
index: indx ?? state.conversation.queries.length - 1,
|
||||
query: {
|
||||
response: answer.answer,
|
||||
thought: answer.thought,
|
||||
sources: sourcesPrepped,
|
||||
tool_calls: answer.toolCalls,
|
||||
},
|
||||
@@ -168,6 +178,7 @@ export const fetchAnswer = createAsyncThunk<
|
||||
answer: '',
|
||||
query: question,
|
||||
result: '',
|
||||
thought: '',
|
||||
sources: [],
|
||||
tool_calls: [],
|
||||
};
|
||||
@@ -215,6 +226,21 @@ export const conversationSlice = createSlice({
|
||||
state.conversationId = action.payload.query.conversationId ?? null;
|
||||
state.status = 'idle';
|
||||
},
|
||||
updateThought(
|
||||
state,
|
||||
action: PayloadAction<{ index: number; query: Partial<Query> }>,
|
||||
) {
|
||||
const { index, query } = action.payload;
|
||||
if (query.thought != undefined) {
|
||||
state.queries[index].thought =
|
||||
(state.queries[index].thought || '') + query.thought;
|
||||
} else {
|
||||
state.queries[index] = {
|
||||
...state.queries[index],
|
||||
...query,
|
||||
};
|
||||
}
|
||||
},
|
||||
updateStreamingSource(
|
||||
state,
|
||||
action: PayloadAction<{ index: number; query: Partial<Query> }>,
|
||||
@@ -284,6 +310,7 @@ export const {
|
||||
resendQuery,
|
||||
updateStreamingQuery,
|
||||
updateConversationId,
|
||||
updateThought,
|
||||
updateStreamingSource,
|
||||
updateToolCalls,
|
||||
setConversation,
|
||||
|
||||
Reference in New Issue
Block a user