mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-06-18 07:48:35 +00:00
feat(app): add thinking model support
This commit is contained in:
@ -12,13 +12,18 @@ import {
|
|||||||
Layers3,
|
Layers3,
|
||||||
Plus,
|
Plus,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import Markdown from 'markdown-to-jsx';
|
import Markdown, { MarkdownToJSX } from 'markdown-to-jsx';
|
||||||
import Copy from './MessageActions/Copy';
|
import Copy from './MessageActions/Copy';
|
||||||
import Rewrite from './MessageActions/Rewrite';
|
import Rewrite from './MessageActions/Rewrite';
|
||||||
import MessageSources from './MessageSources';
|
import MessageSources from './MessageSources';
|
||||||
import SearchImages from './SearchImages';
|
import SearchImages from './SearchImages';
|
||||||
import SearchVideos from './SearchVideos';
|
import SearchVideos from './SearchVideos';
|
||||||
import { useSpeech } from 'react-text-to-speech';
|
import { useSpeech } from 'react-text-to-speech';
|
||||||
|
import ThinkBox from './ThinkBox';
|
||||||
|
|
||||||
|
const ThinkTagProcessor = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return <ThinkBox content={children as string} />;
|
||||||
|
};
|
||||||
|
|
||||||
const MessageBox = ({
|
const MessageBox = ({
|
||||||
message,
|
message,
|
||||||
@ -44,27 +49,48 @@ const MessageBox = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const regex = /\[(\d+)\]/g;
|
const regex = /\[(\d+)\]/g;
|
||||||
|
let processedMessage = message.content;
|
||||||
|
|
||||||
|
if (message.role === 'assistant' && message.content.includes('<think>')) {
|
||||||
|
const openThinkTag = processedMessage.match(/<think>/g)?.length || 0;
|
||||||
|
const closeThinkTag = processedMessage.match(/<\/think>/g)?.length || 0;
|
||||||
|
|
||||||
|
if (openThinkTag > closeThinkTag) {
|
||||||
|
processedMessage += '</think> <a> </a>'; // The extra <a> </a> is to prevent the the think component from looking bad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
message.role === 'assistant' &&
|
message.role === 'assistant' &&
|
||||||
message?.sources &&
|
message?.sources &&
|
||||||
message.sources.length > 0
|
message.sources.length > 0
|
||||||
) {
|
) {
|
||||||
return setParsedMessage(
|
setParsedMessage(
|
||||||
message.content.replace(
|
processedMessage.replace(
|
||||||
regex,
|
regex,
|
||||||
(_, number) =>
|
(_, number) =>
|
||||||
`<a href="${message.sources?.[number - 1]?.metadata?.url}" target="_blank" className="bg-light-secondary dark:bg-dark-secondary px-1 rounded ml-1 no-underline text-xs text-black/70 dark:text-white/70 relative">${number}</a>`,
|
`<a href="${
|
||||||
|
message.sources?.[number - 1]?.metadata?.url
|
||||||
|
}" target="_blank" className="bg-light-secondary dark:bg-dark-secondary px-1 rounded ml-1 no-underline text-xs text-black/70 dark:text-white/70 relative">${number}</a>`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSpeechMessage(message.content.replace(regex, ''));
|
setSpeechMessage(message.content.replace(regex, ''));
|
||||||
setParsedMessage(message.content);
|
setParsedMessage(processedMessage);
|
||||||
}, [message.content, message.sources, message.role]);
|
}, [message.content, message.sources, message.role]);
|
||||||
|
|
||||||
const { speechStatus, start, stop } = useSpeech({ text: speechMessage });
|
const { speechStatus, start, stop } = useSpeech({ text: speechMessage });
|
||||||
|
|
||||||
|
const markdownOverrides: MarkdownToJSX.Options = {
|
||||||
|
overrides: {
|
||||||
|
think: {
|
||||||
|
component: ThinkTagProcessor,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{message.role === 'user' && (
|
{message.role === 'user' && (
|
||||||
@ -111,11 +137,13 @@ const MessageBox = ({
|
|||||||
Answer
|
Answer
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Markdown
|
<Markdown
|
||||||
className={cn(
|
className={cn(
|
||||||
'prose prose-h1:mb-3 prose-h2:mb-2 prose-h2:mt-6 prose-h2:font-[800] prose-h3:mt-4 prose-h3:mb-1.5 prose-h3:font-[600] dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 font-[400]',
|
'prose prose-h1:mb-3 prose-h2:mb-2 prose-h2:mt-6 prose-h2:font-[800] prose-h3:mt-4 prose-h3:mb-1.5 prose-h3:font-[600] dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 font-[400]',
|
||||||
'max-w-none break-words text-black dark:text-white',
|
'max-w-none break-words text-black dark:text-white',
|
||||||
)}
|
)}
|
||||||
|
options={markdownOverrides}
|
||||||
>
|
>
|
||||||
{parsedMessage}
|
{parsedMessage}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
|
43
src/components/ThinkBox.tsx
Normal file
43
src/components/ThinkBox.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { ChevronDown, ChevronUp, BrainCircuit } from 'lucide-react';
|
||||||
|
|
||||||
|
interface ThinkBoxProps {
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThinkBox = ({ content }: ThinkBoxProps) => {
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="my-4 bg-light-secondary/50 dark:bg-dark-secondary/50 rounded-xl border border-light-200 dark:border-dark-200 overflow-hidden">
|
||||||
|
<button
|
||||||
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
|
className="w-full flex items-center justify-between px-4 py-1 text-black/90 dark:text-white/90 hover:bg-light-200 dark:hover:bg-dark-200 transition duration-200"
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<BrainCircuit
|
||||||
|
size={20}
|
||||||
|
className="text-[#9C27B0] dark:text-[#CE93D8]"
|
||||||
|
/>
|
||||||
|
<p className="font-medium text-sm">Thinking Process</p>
|
||||||
|
</div>
|
||||||
|
{isExpanded ? (
|
||||||
|
<ChevronUp size={18} className="text-black/70 dark:text-white/70" />
|
||||||
|
) : (
|
||||||
|
<ChevronDown size={18} className="text-black/70 dark:text-white/70" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{isExpanded && (
|
||||||
|
<div className="px-4 py-3 text-black/80 dark:text-white/80 text-sm border-t border-light-200 dark:border-dark-200 bg-light-100/50 dark:bg-dark-100/50 whitespace-pre-wrap">
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThinkBox;
|
Reference in New Issue
Block a user