// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates // SPDX-License-Identifier: MIT import { PythonOutlined } from "@ant-design/icons"; import { motion } from "framer-motion"; import { LRUCache } from "lru-cache"; import { BookOpenText, FileText, PencilRuler, Search } from "lucide-react"; import { useTranslations } from "next-intl"; import { useTheme } from "next-themes"; import { useMemo } from "react"; import SyntaxHighlighter from "react-syntax-highlighter"; import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs"; import { dark } from "react-syntax-highlighter/dist/esm/styles/prism"; import { FavIcon } from "~/components/deer-flow/fav-icon"; import Image from "~/components/deer-flow/image"; import { LoadingAnimation } from "~/components/deer-flow/loading-animation"; import { Markdown } from "~/components/deer-flow/markdown"; import { RainbowText } from "~/components/deer-flow/rainbow-text"; import { Tooltip } from "~/components/deer-flow/tooltip"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "~/components/ui/accordion"; import { Skeleton } from "~/components/ui/skeleton"; import { findMCPTool } from "~/core/mcp"; import type { ToolCallRuntime } from "~/core/messages"; import { useMessage, useStore } from "~/core/store"; import { parseJSON } from "~/core/utils"; import { cn } from "~/lib/utils"; export function ResearchActivitiesBlock({ className, researchId, }: { className?: string; researchId: string; }) { const activityIds = useStore((state) => state.researchActivityIds.get(researchId), )!; const ongoing = useStore((state) => state.ongoingResearchId === researchId); return ( <> {ongoing && } ); } function ActivityMessage({ messageId }: { messageId: string }) { const message = useMessage(messageId); if (message?.agent && message.content) { if (message.agent !== "reporter" && message.agent !== "planner") { return (
{message.content}
); } } return null; } function ActivityListItem({ messageId }: { messageId: string }) { const message = useMessage(messageId); if (message) { if (!message.isStreaming && message.toolCalls?.length) { for (const toolCall of message.toolCalls) { if (toolCall.result?.startsWith("Error")) { return null; } if (toolCall.name === "web_search") { return ; } else if (toolCall.name === "crawl_tool") { return ; } else if (toolCall.name === "python_repl_tool") { return ; } else if (toolCall.name === "local_search_tool") { return ; } else { return ; } } } } return null; } const __pageCache = new LRUCache({ max: 100 }); type SearchResult = | { type: "page"; title: string; url: string; content: string; } | { type: "image"; image_url: string; image_description: string; }; function WebSearchToolCall({ toolCall }: { toolCall: ToolCallRuntime }) { const t = useTranslations("chat.research"); const searching = useMemo(() => { return toolCall.result === undefined; }, [toolCall.result]); const searchResults = useMemo(() => { let results: SearchResult[] | undefined = undefined; try { results = toolCall.result ? parseJSON(toolCall.result, []) : undefined; } catch { results = undefined; } if (Array.isArray(results)) { results.forEach((result) => { if (result.type === "page") { __pageCache.set(result.url, result.title); } }); } else { results = []; } return results; }, [toolCall.result]); const pageResults = useMemo( () => searchResults?.filter((result) => result.type === "page"), [searchResults], ); const imageResults = useMemo( () => searchResults?.filter((result) => result.type === "image"), [searchResults], ); return (
{t("searchingFor")}  {(toolCall.args as { query: string }).query}
{pageResults && (
    {searching && [...Array(6)].map((_, i) => (
  • ))} {pageResults .filter((result) => result.type === "page") .map((searchResult, i) => ( {searchResult.title} ))} {imageResults.map((searchResult, i) => ( {searchResult.image_description} ))}
)}
); } function CrawlToolCall({ toolCall }: { toolCall: ToolCallRuntime }) { const t = useTranslations("chat.research"); const url = useMemo( () => (toolCall.args as { url: string }).url, [toolCall.args], ); const title = useMemo(() => __pageCache.get(url), [url]); return (
{t("reading")}
); } function RetrieverToolCall({ toolCall }: { toolCall: ToolCallRuntime }) { const t = useTranslations("chat.research"); const searching = useMemo(() => { return toolCall.result === undefined; }, [toolCall.result]); const documents = useMemo< Array<{ id: string; title: string; content: string }> >(() => { return toolCall.result ? parseJSON(toolCall.result, []) : []; }, [toolCall.result]); return (
{t("retrievingDocuments")}  {(toolCall.args as { keywords: string }).keywords}
{documents && (
    {searching && [...Array(2)].map((_, i) => (
  • ))} {documents?.map((doc, i) => ( {doc.title} (chunk-{i},size-{doc.content.length}) ))}
)}
); } function PythonToolCall({ toolCall }: { toolCall: ToolCallRuntime }) { const t = useTranslations("chat.research"); const code = useMemo(() => { return (toolCall.args as { code?: string }).code; }, [toolCall.args]); const { resolvedTheme } = useTheme(); return (
{t("runningPythonCode")}
{code?.trim() ?? ""}
{toolCall.result && }
); } function PythonToolCallResult({ result }: { result: string }) { const t = useTranslations("chat.research"); const { resolvedTheme } = useTheme(); const hasError = useMemo( () => result.includes("Error executing code:\n"), [result], ); const error = useMemo(() => { if (hasError) { const parts = result.split("```\nError: "); if (parts.length > 1) { return parts[1]!.trim(); } } return null; }, [result, hasError]); const stdout = useMemo(() => { if (!hasError) { const parts = result.split("```\nStdout: "); if (parts.length > 1) { return parts[1]!.trim(); } } return null; }, [result, hasError]); return ( <>
{hasError ? t("errorExecutingCode") : t("executionOutput")}
{error ?? stdout ?? "(empty)"}
); } function MCPToolCall({ toolCall }: { toolCall: ToolCallRuntime }) { const tool = useMemo(() => findMCPTool(toolCall.name), [toolCall.name]); const { resolvedTheme } = useTheme(); return (
Running {toolCall.name ? toolCall.name + "()" : "MCP tool"}
{toolCall.result && (
{toolCall.result.trim()}
)}
); }