fix: keep streamed reasoning visible
This commit is contained in:
@@ -1325,6 +1325,36 @@ describe('App', () => {
|
|||||||
.closest('details')
|
.closest('details')
|
||||||
expect(streamingReasoning).toHaveAttribute('open')
|
expect(streamingReasoning).toHaveAttribute('open')
|
||||||
expect(screen.getByText('先检查项目结构')).toBeInTheDocument()
|
expect(screen.getByText('先检查项目结构')).toBeInTheDocument()
|
||||||
|
const reasoningContent =
|
||||||
|
streamingReasoning!.querySelector<HTMLElement>(
|
||||||
|
'.message-reasoning__content'
|
||||||
|
)
|
||||||
|
if (!reasoningContent) {
|
||||||
|
throw new Error('Missing reasoning content')
|
||||||
|
}
|
||||||
|
const reasoningScrollTo = vi.fn()
|
||||||
|
reasoningContent.scrollTo = reasoningScrollTo
|
||||||
|
Object.defineProperty(reasoningContent, 'scrollHeight', {
|
||||||
|
configurable: true,
|
||||||
|
value: 640
|
||||||
|
})
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
if (!request) {
|
||||||
|
throw new Error('Missing request')
|
||||||
|
}
|
||||||
|
agentListener?.({
|
||||||
|
requestId: request.requestId,
|
||||||
|
type: 'reasoning',
|
||||||
|
delta: ',再确认依赖'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(reasoningScrollTo).toHaveBeenLastCalledWith({
|
||||||
|
top: 640,
|
||||||
|
behavior: 'auto'
|
||||||
|
})
|
||||||
|
expect(screen.getByText('先检查项目结构,再确认依赖')).toBeInTheDocument()
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
if (!request) {
|
if (!request) {
|
||||||
|
|||||||
+45
-30
@@ -524,6 +524,43 @@ function groupMessageBlocks(
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MessageReasoning({
|
||||||
|
content,
|
||||||
|
streaming
|
||||||
|
}: {
|
||||||
|
content: string
|
||||||
|
streaming: boolean
|
||||||
|
}): React.JSX.Element {
|
||||||
|
const { t } = useTranslation('app')
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!streaming || !contentRef.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
contentRef.current.scrollTo({
|
||||||
|
top: contentRef.current.scrollHeight,
|
||||||
|
behavior: 'auto'
|
||||||
|
})
|
||||||
|
}, [content, streaming])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<details className="message-reasoning" open={streaming}>
|
||||||
|
<summary>
|
||||||
|
{streaming
|
||||||
|
? t('chat.reasoning.streaming')
|
||||||
|
: t('chat.reasoning.complete')}
|
||||||
|
</summary>
|
||||||
|
<div
|
||||||
|
className="markdown-content message-reasoning__content"
|
||||||
|
ref={contentRef}
|
||||||
|
>
|
||||||
|
<MarkdownRenderer>{content}</MarkdownRenderer>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function ToolExecutionList({
|
function ToolExecutionList({
|
||||||
tools
|
tools
|
||||||
}: {
|
}: {
|
||||||
@@ -5380,22 +5417,11 @@ function App(): React.JSX.Element {
|
|||||||
tools={item.tools}
|
tools={item.tools}
|
||||||
/>
|
/>
|
||||||
) : item.block.type === 'reasoning' ? (
|
) : item.block.type === 'reasoning' ? (
|
||||||
<details
|
<MessageReasoning
|
||||||
className="message-reasoning"
|
content={item.block.content}
|
||||||
key={item.block.id}
|
key={item.block.id}
|
||||||
open={message.state === 'streaming'}
|
streaming={message.state === 'streaming'}
|
||||||
>
|
/>
|
||||||
<summary>
|
|
||||||
{message.state === 'streaming'
|
|
||||||
? t('chat.reasoning.streaming')
|
|
||||||
: t('chat.reasoning.complete')}
|
|
||||||
</summary>
|
|
||||||
<div className="markdown-content message-reasoning__content">
|
|
||||||
<MarkdownRenderer>
|
|
||||||
{item.block.content}
|
|
||||||
</MarkdownRenderer>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="markdown-content message__content"
|
className="markdown-content message__content"
|
||||||
@@ -5411,22 +5437,11 @@ function App(): React.JSX.Element {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{message.reasoning && (
|
{message.reasoning && (
|
||||||
<details
|
<MessageReasoning
|
||||||
className="message-reasoning"
|
content={message.reasoning}
|
||||||
key={`${message.id}-${message.state}`}
|
key={`${message.id}-${message.state}`}
|
||||||
open={message.state === 'streaming'}
|
streaming={message.state === 'streaming'}
|
||||||
>
|
/>
|
||||||
<summary>
|
|
||||||
{message.state === 'streaming'
|
|
||||||
? t('chat.reasoning.streaming')
|
|
||||||
: t('chat.reasoning.complete')}
|
|
||||||
</summary>
|
|
||||||
<div className="markdown-content message-reasoning__content">
|
|
||||||
<MarkdownRenderer>
|
|
||||||
{message.reasoning}
|
|
||||||
</MarkdownRenderer>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
)}
|
)}
|
||||||
{message.content && (
|
{message.content && (
|
||||||
<div className="markdown-content message__content">
|
<div className="markdown-content message__content">
|
||||||
|
|||||||
Reference in New Issue
Block a user