import { Check, Copy } from "lucide-react"; import { useMemo } from "react"; import { Button } from "../../button"; import { HoverCard, HoverCardContent, HoverCardTrigger, } from "../../hover-card"; import { useCopyToClipboard } from "../hooks/use-copy-to-clipboard"; import { SourceData } from "../index"; import PdfDialog from "../widgets/PdfDialog"; import { useClientConfig } from "../hooks/use-config"; const SCORE_THRESHOLD = 0.3; function truncateNumber(num: number | undefined, precision: number): number { if (num == undefined || num == 0) return 0; const factor = Math.pow(10, precision); return Math.trunc(num * factor) / factor; } function SourceNumberButton({ index, score }: { index: number, score: number | undefined }) { return (
{truncateNumber(score, 2)}
); } type NodeInfo = { id: string; score?: number; text: string; url?: string; }; export function ChatSources({ data }: { data: SourceData }) { const { backend } = useClientConfig(); const sources: NodeInfo[] = useMemo(() => { // aggregate nodes by url or file_path (get the highest one by score) const nodesByPath: { [path: string]: NodeInfo } = {}; data.nodes .filter((node) => (node.score ?? 1) > SCORE_THRESHOLD) .sort((a, b) => (b.score ?? 1) - (a.score ?? 1)) .forEach((node) => { const nodeInfo = { id: node.id, score: node.score, text: node.text, url: node.url, }; const key = nodeInfo.url ?? nodeInfo.id; // use id as key for UNKNOWN type if (!nodesByPath[key]) { nodesByPath[key] = nodeInfo; } }); return Object.values(nodesByPath); }, [data.nodes]); if (sources.length === 0) return null; return (
来源:
{sources.map((nodeInfo: NodeInfo, index: number) => { if (nodeInfo.url?.endsWith(".pdf")) { return ( } /> ); } return (
); })}
); } function NodeInfo({ nodeInfo }: { nodeInfo: NodeInfo }) { const { backend } = useClientConfig(); const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 1000 }); if (nodeInfo.url) { // this is a node generated by the web loader or file loader, // add a link to view its URL and a button to copy the URL to the clipboard return (
{nodeInfo.text}
); } // node generated by unknown loader, implement renderer by analyzing logged out metadata return (

对不起, 未知文件类型. 无法打开当前的来源文件。

); }