feat(frontend): 上线报告协作与多轮 RAG 消息聚合体验

功能说明:\n- 新增报告协作页(Report Collaboration)核心状态层、消息适配与会话面板,支持按节点/阶段选择和协作上下文展示\n- 补齐 report-collaboration API 与类型定义,并接入页面路由/构建路径,形成可运行的协作链路\n- 重构多轮 RAG 与 write/artefact 消息分组逻辑,优化 AgentChat 与 DeepResearch 场景下的消息展示顺序与可读性\n- 新增/更新 DeepResearch 前端页面(Workbench、报告配置、沙箱联动)以承接报告生成与复用能力\n- 增加报表协作相关单测,覆盖事件归并、消息适配与关键 reducer 行为
This commit is contained in:
董宏凯 committed 2026-09-04 23:18:27 +08:00
1 parent e58da3fbdf
commit 438772f0b7
25 files changed
+1740 -399

No files matched your search

+6 -1
View File
@@ -3,6 +3,7 @@
import { ChevronRightIcon } from "lucide-react";
import Link from "next/link";
import deerMaskUrl from "@/assets/deer.svg";
import { Button } from "@/components/ui/button";
import { FlickeringGrid } from "@/components/ui/flickering-grid";
import Galaxy from "@/components/ui/galaxy";
@@ -29,7 +30,11 @@ export function Hero({ className }: { className?: string }) {
/>
</div>
<FlickeringGrid
className="absolute inset-0 z-0 translate-y-8 mask-[url(/images/deer.svg)] mask-size-[100vw] mask-center mask-no-repeat md:mask-size-[72vh]"
className="absolute inset-0 z-0 translate-y-8 mask-size-[100vw] mask-center mask-no-repeat md:mask-size-[72vh]"
style={{
WebkitMaskImage: `url("${deerMaskUrl}")`,
maskImage: `url("${deerMaskUrl}")`,
}}
squareSize={4}
gridGap={4}
color={"white"}
@@ -6,7 +6,7 @@ import {
SearchIcon,
SparklesIcon,
} from "lucide-react";
import { useEffect, useState } from "react";
import { useState } from "react";
import {
Conversation,
@@ -165,9 +165,6 @@ function MultiRoundRagTraceView({
onSelectRetrieval: (item: MultiRoundRagRetrieval) => void;
}) {
const [open, setOpen] = useState(!trace.thinkingComplete);
useEffect(() => {
setOpen(!trace.thinkingComplete);
}, [trace.thinkingComplete]);
return (
<Collapsible
@@ -208,6 +205,7 @@ function MultiRoundRagTraceView({
index={index + 1}
round={round}
isLoading={isLoading}
isActiveRound={isLoading && index === trace.rounds.length - 1}
rehypePlugins={rehypePlugins}
onSelectRetrieval={onSelectRetrieval}
/>
@@ -222,20 +220,22 @@ function MultiRoundRagRoundCard({
index,
round,
isLoading,
isActiveRound,
rehypePlugins,
onSelectRetrieval,
}: {
index: number;
round: MultiRoundRagRound;
isLoading: boolean;
isActiveRound: boolean;
rehypePlugins: MessageResponseProps["rehypePlugins"];
onSelectRetrieval: (item: MultiRoundRagRetrieval) => void;
}) {
const searching = Boolean(round.queryLoading);
const query = round.query.trim();
const searching = Boolean(query && round.queryLoading);
const showQuery = Boolean(query);
const showRetrievals = showQuery || round.retrievals.length > 0;
const [open, setOpen] = useState(true);
useEffect(() => {
setOpen(searching || !round.query);
}, [round.id, round.query, searching]);
return (
<Collapsible open={open} onOpenChange={setOpen}>
@@ -254,7 +254,7 @@ function MultiRoundRagRoundCard({
<Loader2Icon className="size-3 animate-spin" />
检索中
</span>
) : (
) : isActiveRound ? null : (
<span className="text-muted-foreground text-[11px]">已完成</span>
)}
<ChevronDownIcon
@@ -277,7 +277,7 @@ function MultiRoundRagRoundCard({
/>
</section>
)}
{round.queryShow && (round.query || searching) && (
{showQuery && (
<div
className="border-primary/20 bg-primary/5 text-primary inline-flex max-w-full items-center gap-2 rounded-full border px-3 py-1.5"
aria-live={searching ? "polite" : undefined}
@@ -291,11 +291,11 @@ function MultiRoundRagRoundCard({
{round.queryTitle || "检索词"}
</span>
<span className="min-w-0 truncate text-xs font-medium">
{round.query || "正在生成检索词…"}
{query}
</span>
</div>
)}
{round.queryShow && (
{showRetrievals && (
<section>
<div className="mb-2 flex items-center gap-2 text-sm font-medium">
<FileTextIcon className="text-muted-foreground size-4" />