fix: 修复加载大消息会话时卡死的问题

This commit is contained in:
llg-linwei committed 2026-02-09 14:29:58 +08:00
1 parent 5b69a63c06
commit f437024a96
2 files changed
+83 -17

No files matched your search

+40 -16
View File
@@ -18,18 +18,35 @@ import {
WrenchIcon,
XCircleIcon,
} from "lucide-react";
import { isValidElement } from "react";
import { createContext, isValidElement, useContext, useState } from "react";
import { CodeBlock } from "./code-block";
// Context to track if tool content has been opened (for lazy rendering)
const ToolOpenContext = createContext<boolean>(false);
export type ToolProps = ComponentProps<typeof Collapsible>;
export const Tool = ({ className, ...props }: ToolProps) => (
<Collapsible
className={cn("group not-prose mb-4 w-full rounded-md border", className)}
{...props}
/>
);
export const Tool = ({ className, defaultOpen = false, ...props }: ToolProps) => {
// Track if the tool has ever been opened (for lazy content rendering)
const [hasBeenOpened, setHasBeenOpened] = useState(defaultOpen);
return (
<ToolOpenContext.Provider value={hasBeenOpened}>
<Collapsible
className={cn("group not-prose mb-4 w-full rounded-md border", className)}
defaultOpen={defaultOpen}
onOpenChange={(open) => {
if (open && !hasBeenOpened) {
setHasBeenOpened(true);
}
props.onOpenChange?.(open);
}}
{...props}
/>
</ToolOpenContext.Provider>
);
};
export type ToolPart = ToolUIPart | DynamicToolUIPart;
@@ -103,15 +120,22 @@ export const ToolHeader = ({
export type ToolContentProps = ComponentProps<typeof CollapsibleContent>;
export const ToolContent = ({ className, ...props }: ToolContentProps) => (
<CollapsibleContent
className={cn(
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 space-y-4 p-4 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
className
)}
{...props}
/>
);
export const ToolContent = ({ className, children, ...props }: ToolContentProps) => {
// Only render children if the tool has been opened at least once
const hasBeenOpened = useContext(ToolOpenContext);
return (
<CollapsibleContent
className={cn(
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 space-y-4 p-4 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
className
)}
{...props}
>
{hasBeenOpened ? children : null}
</CollapsibleContent>
);
};
export type ToolInputProps = ComponentProps<"div"> & {
input: ToolPart["input"];
+43 -1
View File
@@ -33,8 +33,50 @@ interface ToolBlock {
function parseToolBlocks(content: string): { text: string; tools: ToolBlock[] } {
const tools: ToolBlock[] = [];
let text = content;
let text = '';
// Try to parse as JSON array (new format from chat API)
if (content.startsWith('[')) {
try {
const blocks = JSON.parse(content) as Array<{
type: string;
text?: string;
id?: string;
name?: string;
input?: unknown;
tool_use_id?: string;
content?: string;
is_error?: boolean;
}>;
for (const block of blocks) {
if (block.type === 'text' && block.text) {
text += block.text;
} else if (block.type === 'tool_use') {
tools.push({
type: 'tool_use',
id: block.id,
name: block.name,
input: block.input,
});
} else if (block.type === 'tool_result') {
tools.push({
type: 'tool_result',
id: block.tool_use_id,
content: block.content,
is_error: block.is_error,
});
}
}
return { text: text.trim(), tools };
} catch {
// Not valid JSON, fall through to legacy parsing
}
}
// Legacy format: HTML comments
text = content;
const toolUseRegex = /<!--tool_use:([\s\S]*?)-->/g;
let match;
while ((match = toolUseRegex.exec(content)) !== null) {