fix: improve assistant thinking and image progress

This commit is contained in:
boos
2026-06-06 22:02:45 +08:00
parent d7d14e077e
commit 3c6f061709
3 changed files with 217 additions and 14 deletions
+95 -10
View File
@@ -112,6 +112,9 @@ const QUICK_PROMPTS = [
'根据已有角色和场景,继续生成 ComfyUI 工作流 JSON',
];
const STREAMING_REASONING_PLACEHOLDER = '正在等待模型返回真实思考过程,收到后会实时替换这里。';
const STREAMING_BODY_PLACEHOLDER = '模型正在组织正文和素材步骤...';
const SEEDANCE_PROMPT_TEMPLATES = [
{
title: '文生视频',
@@ -651,7 +654,7 @@ function renderMarkdown(content: string): string {
}
function processThinkTags(content: string, reasoningContent = '') {
if (!content.includes('<think>')) {
if (!content || !/<think>/i.test(content)) {
return { content, reasoningContent };
}
const thoughts: string[] = [];
@@ -665,7 +668,18 @@ function processThinkTags(content: string, reasoningContent = '') {
lastIndex = match.index + match[0].length;
}
replyParts.push(content.substring(lastIndex));
const cleanContent = replyParts.join('').replace(/<\/?think>/gi, '').trim();
let cleanContent = replyParts.join('');
const lastOpenThinkIndex = cleanContent.toLowerCase().lastIndexOf('<think>');
if (lastOpenThinkIndex !== -1) {
const fragmentAfterLastOpen = cleanContent.substring(lastOpenThinkIndex + '<think>'.length);
if (!/<\/think>/i.test(fragmentAfterLastOpen)) {
thoughts.push(fragmentAfterLastOpen.trim());
cleanContent = cleanContent.substring(0, lastOpenThinkIndex);
}
}
cleanContent = cleanContent.replace(/<\/?think>/gi, '').trim();
const thoughtsText = thoughts.filter(Boolean).join('\n\n---\n\n');
return {
content: cleanContent,
@@ -821,7 +835,7 @@ export default function ComfyUIAgent() {
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });
}, [messages, streaming?.content]);
}, [messages, streaming?.content, streaming?.reasoningContent, streaming?.images.length, streaming?.scenes.length, streaming?.tts.length]);
useEffect(() => {
activeConversationIdRef.current = activeConversationId;
@@ -1349,16 +1363,30 @@ export default function ComfyUIAgent() {
const renderReasoning = (message: AgentMessage | StreamAccumulator, isStreaming = false) => {
const processed = processThinkTags(message.content || '', message.reasoningContent || '');
const hasReasoning = processed.reasoningContent.trim().length > 0;
if (!hasReasoning) return null;
if (!hasReasoning && !isStreaming) return null;
return (
<details open={isStreaming} style={reasoningBoxStyle}>
<details
open={isStreaming}
style={{
...reasoningBoxStyle,
...(!hasReasoning && isStreaming ? reasoningPendingBoxStyle : {}),
}}
>
<summary style={reasoningSummaryStyle}>
思考过程
<span>思考过程</span>
{isStreaming ? <em style={reasoningLiveBadgeStyle}>实时</em> : null}
</summary>
<div
style={reasoningContentStyle}
dangerouslySetInnerHTML={{ __html: renderMarkdown(processed.reasoningContent) }}
/>
{hasReasoning ? (
<div
style={reasoningContentStyle}
dangerouslySetInnerHTML={{ __html: renderMarkdown(processed.reasoningContent) }}
/>
) : (
<div style={reasoningPendingStyle}>
<span style={reasoningPulseDotStyle} />
<span>{STREAMING_REASONING_PLACEHOLDER}</span>
</div>
)}
</details>
);
};
@@ -1711,6 +1739,9 @@ export default function ComfyUIAgent() {
{renderReasoning(streaming, true)}
{streaming.content ? renderMessageContent(streaming) : null}
{streaming.content ? <span style={cursorStyle}>|</span> : null}
{!streaming.content && !streaming.images.length && !streaming.scenes.length && !streaming.tts.length ? (
<div style={streamingBodyStatusStyle}>{STREAMING_BODY_PLACEHOLDER}</div>
) : null}
{renderAssets(streaming)}
<div style={messageActionsStyle}>
<button
@@ -2121,6 +2152,9 @@ const reasoningSummaryStyle: CSSProperties = {
fontSize: 12,
fontWeight: 700,
color: 'var(--color-primary)',
display: 'flex',
alignItems: 'center',
gap: 6,
};
const reasoningContentStyle: CSSProperties = {
@@ -2130,6 +2164,57 @@ const reasoningContentStyle: CSSProperties = {
color: 'var(--color-text-secondary)',
};
const reasoningPendingBoxStyle: CSSProperties = {
borderStyle: 'dashed',
background:
'linear-gradient(135deg, color-mix(in srgb, var(--color-primary) 7%, var(--color-bg)), color-mix(in srgb, var(--color-info) 5%, var(--color-bg)))',
};
const reasoningLiveBadgeStyle: CSSProperties = {
marginLeft: 8,
padding: '1px 6px',
border: '1px solid color-mix(in srgb, var(--color-primary) 22%, var(--color-border-light))',
borderRadius: 999,
color: 'var(--color-primary)',
background: 'color-mix(in srgb, var(--color-primary) 8%, transparent)',
fontSize: 11,
fontStyle: 'normal',
fontWeight: 700,
};
const reasoningPendingStyle: CSSProperties = {
display: 'flex',
alignItems: 'center',
gap: 8,
padding: '0 10px 10px',
color: 'var(--color-text-muted)',
fontSize: 12,
lineHeight: 1.6,
};
const reasoningPulseDotStyle: CSSProperties = {
width: 7,
height: 7,
flexShrink: 0,
borderRadius: 999,
background: 'var(--color-primary)',
boxShadow: '0 0 0 5px color-mix(in srgb, var(--color-primary) 12%, transparent)',
};
const streamingBodyStatusStyle: CSSProperties = {
display: 'inline-flex',
alignItems: 'center',
width: 'fit-content',
maxWidth: '100%',
marginTop: 4,
padding: '7px 10px',
border: '1px solid var(--color-border-light)',
borderRadius: 999,
background: 'color-mix(in srgb, var(--color-bg) 74%, var(--color-surface))',
color: 'var(--color-text-muted)',
fontSize: 12,
};
const inputBarStyle: CSSProperties = {
padding: 16,
borderTop: '1px solid var(--color-border)',