// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { PythonOutlined } from "@ant-design/icons";
import { parse } from "best-effort-json-parser";
import { motion } from "framer-motion";
import { LRUCache } from "lru-cache";
import { BookOpenText, Search } from "lucide-react";
import { useMemo } from "react";
import SyntaxHighlighter from "react-syntax-highlighter";
import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs";
import { Skeleton } from "~/components/ui/skeleton";
import type { ToolCallRuntime } from "~/core/messages";
import { useMessage, useStore } from "~/core/store";
import { cn } from "~/lib/utils";
import { FavIcon } from "./fav-icon";
import Image from "./image";
import { LoadingAnimation } from "./loading-animation";
import { Markdown } from "./markdown";
import { RainbowText } from "./rainbow-text";
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 (
<>
{activityIds.map(
(activityId, i) =>
i !== 0 && (
{i !== activityIds.length - 1 &&
}
),
)}
{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.name === "web_search") {
return ;
} else if (toolCall.name === "crawl_tool") {
return ;
} else if (toolCall.name === "python_repl_tool") {
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 searching = useMemo(() => {
return toolCall.result === undefined;
}, [toolCall.result]);
const searchResults = useMemo(() => {
let results: SearchResult[] | undefined = undefined;
try {
results = toolCall.result ? parse(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 (
Searching for
{(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) => (
))}
)}
);
}
function CrawlToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
const url = useMemo(
() => (toolCall.args as { url: string }).url,
[toolCall.args],
);
const title = useMemo(() => __pageCache.get(url), [url]);
return (
);
}
function PythonToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
const code = useMemo(() => {
return (toolCall.args as { code: string }).code;
}, [toolCall.args]);
return (
);
}