fix: 修复加载大消息会话时卡死的问题
This commit is contained in:
1 parent
5b69a63c06
commit
f437024a96
2 files changed
+83
-17
No files matched your search
@@ -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"];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user