add reasoning block

This commit is contained in:
duanfuxiang
2025-02-18 11:02:24 +08:00
parent dc520535fc
commit d15681b0d5
12 changed files with 285 additions and 169 deletions

View File

@@ -44,10 +44,12 @@ import AssistantMessageActions from './AssistantMessageActions'
import PromptInputWithActions, { ChatUserInputRef } from './chat-input/PromptInputWithActions'
import { editorStateToPlainText } from './chat-input/utils/editor-state-to-plain-text'
import { ChatHistory } from './ChatHistory'
import MarkdownReasoningBlock from './MarkdownReasoningBlock'
import QueryProgress, { QueryProgressState } from './QueryProgress'
import ReactMarkdown from './ReactMarkdown'
import ShortcutInfo from './ShortcutInfo'
import SimilaritySearchResults from './SimilaritySearchResults'
// Add an empty line here
const getNewInputMessage = (app: App): ChatUserMessage => {
return {
@@ -242,6 +244,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
{
role: 'assistant',
content: '',
reasoningContent: '',
id: responseMessageId,
metadata: {
usage: undefined,
@@ -269,6 +272,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
{
role: 'assistant',
content: '',
reasoningContent: '',
id: responseMessageId,
metadata: {
usage: undefined,
@@ -290,12 +294,14 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content ?? ''
const reasoning_content = chunk.choices[0]?.delta?.reasoning_content ?? ''
setChatMessages((prevChatHistory) =>
prevChatHistory.map((message) =>
message.role === 'assistant' && message.id === responseMessageId
? {
...message,
content: message.content + content,
reasoningContent: message.reasoningContent + reasoning_content,
metadata: {
...message.metadata,
usage: chunk.usage ?? message.metadata?.usage, // Keep existing usage if chunk has no usage data
@@ -584,13 +590,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
{
// If the chat is empty, show a message to start a new chat
chatMessages.length === 0 && (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
width: '100%'
}}>
<div className="infio-chat-empty-state">
<ShortcutInfo />
</div>
)
@@ -638,6 +638,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
</div>
) : (
<div key={message.id} className="infio-chat-messages-assistant">
<MarkdownReasoningBlock reasoningContent={message.reasoningContent} />
<ReactMarkdownItem
handleApply={handleApply}
isApplying={applyMutation.isPending}

View File

@@ -0,0 +1,56 @@
import { ChevronDown, ChevronRight } from 'lucide-react'
import { PropsWithChildren, useEffect, useRef, useState } from 'react'
import { useDarkModeContext } from '../../contexts/DarkModeContext'
import { MemoizedSyntaxHighlighterWrapper } from './SyntaxHighlighterWrapper'
export default function MarkdownReasoningBlock({
reasoningContent,
}: PropsWithChildren<{
reasoningContent: string
}>) {
const { isDarkMode } = useDarkModeContext()
const containerRef = useRef<HTMLDivElement>(null)
const [isOpen, setIsOpen] = useState(true)
useEffect(() => {
if (containerRef.current) {
containerRef.current.scrollTop = containerRef.current.scrollHeight
}
}, [reasoningContent])
return (
reasoningContent && (
<div
className={`infio-chat-code-block has-filename infio-reasoning-block`}
>
<div className={'infio-chat-code-block-header'}>
<div className={'infio-chat-code-block-header-filename'}>
Reasoning
</div>
<button
className="clickable-icon infio-chat-list-dropdown"
onClick={() => setIsOpen(!isOpen)}
>
{isOpen ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
</button>
</div>
<div
ref={containerRef}
className="infio-reasoning-content-wrapper"
>
<MemoizedSyntaxHighlighterWrapper
isDarkMode={isDarkMode}
language="markdown"
hasFilename={true}
wrapLines={true}
isOpen={isOpen}
>
{reasoningContent}
</MemoizedSyntaxHighlighterWrapper>
</div>
</div>
)
)
}

View File

@@ -11,13 +11,17 @@ function SyntaxHighlighterWrapper({
hasFilename,
wrapLines,
children,
isOpen = true,
}: {
isDarkMode: boolean
language: string | undefined
hasFilename: boolean
wrapLines: boolean
children: string
isOpen?: boolean
}) {
if (!isOpen) return null;
return (
<SyntaxHighlighter
language={language}