From 802fabb20f74ef13d1370b0e33e4468a7d2b39a3 Mon Sep 17 00:00:00 2001 From: 19926533763 <19926533763@163.com> Date: Thu, 10 Sep 2026 19:24:33 +0800 Subject: [PATCH] Redesign Beryl Agent workspace --- agent-studio-mini/apps/web/src/App.tsx | 154 +++++++++++++++---------- 1 file changed, 91 insertions(+), 63 deletions(-) diff --git a/agent-studio-mini/apps/web/src/App.tsx b/agent-studio-mini/apps/web/src/App.tsx index 327ec47..ae36840 100644 --- a/agent-studio-mini/apps/web/src/App.tsx +++ b/agent-studio-mini/apps/web/src/App.tsx @@ -1,4 +1,6 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState, type ClipboardEvent } from "react"; +import { BarChart3, BookOpen, ChevronRight, FileChartColumn, FolderOpen, LayoutDashboard, Lightbulb, MessageSquareText, PackageSearch, Plus, Settings2, Sparkles } from "lucide-react"; +import { Monitoring } from "./Monitoring.js"; import { AssistantRuntimeProvider, ComposerPrimitive, @@ -18,12 +20,13 @@ type Msg = { role: "user" | "assistant"; text: string; images?: Array<{ src: string; alt: string }>; + download?: { href: string; title: string }; status?: "running" | "complete" | "incomplete"; }; type PendingImage = ImageAttachment & { previewUrl: string }; const demo = - "请使用 ads-analysis Skill,分析账户 demo-account 最近 7 天数据,找出异常 Campaign,并输出账户总结、关键指标和 3 条优化建议。最后使用 report-writer Skill 生成一份报告草稿。"; -const modelStorageKey = "agent-studio:model-config:v1"; + "请使用 demo-report Skill,读取 demo-account 最近 7 天演示数据,分析 Campaign,生成含 3 条建议的报告草稿。保存后读取报告并核对指标,明确验收是否通过。"; +import { modelStorageKey, readSavedModel, saveModel } from "./model-storage.js"; const defaultModelInput: ModelConnectionInput = { providerId: "openai", api: "openai-responses", @@ -33,19 +36,6 @@ const defaultModelInput: ModelConnectionInput = { supportsVideo: false, }; -function readSavedModel(): ModelConnectionInput | undefined { - try { - const value = localStorage.getItem(modelStorageKey); - return value ? (JSON.parse(value) as ModelConnectionInput) : undefined; - } catch { - return undefined; - } -} - -function saveModel(input: ModelConnectionInput) { - localStorage.setItem(modelStorageKey, JSON.stringify(input)); -} - async function fetchJsonWithRetry( input: RequestInfo | URL, init?: RequestInit, @@ -67,7 +57,9 @@ async function fetchJsonWithRetry( } export function App() { + const [monitorOpen,setMonitorOpen]=useState(false); const [session, setSession] = useState(""); + useEffect(() => { if (session) localStorage.setItem("beryl-session-id", session); }, [session]); const [skills, setSkills] = useState([]); const [events, setEvents] = useState([]); const [msgs, setMsgs] = useState([]); @@ -79,13 +71,14 @@ export function App() { const [attachmentError, setAttachmentError] = useState(""); const es = useRef(undefined); const messagesRef = useRef(null); - const create = useCallback(async () => { + const create = useCallback(async (resume = false) => { es.current?.close(); const d = await fetchJsonWithRetry<{ sessionId: string }>( "/api/sessions", - { method: "POST" }, + { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ resumeSessionId: resume ? localStorage.getItem("beryl-session-id") : undefined }) }, ); setSession(d.sessionId); + localStorage.setItem("beryl-session-id", d.sessionId); setEvents([]); setMsgs([]); setRunning(false); @@ -117,7 +110,7 @@ export function App() { useEffect(() => { void fetchJsonWithRetry<{ skills: SkillCatalogItem[] }>("/api/skills") .then((d) => setSkills(d.skills)); - void create(); + void create(true); return () => es.current?.close(); }, [create]); useEffect(() => { @@ -205,6 +198,20 @@ export function App() { ]; }); } + if (e.type === "tool.completed" && e.payload.toolName === "save_report_draft") { + setMsgs(old => old.some(m => m.id === e.eventId) ? old : [...old, { + id: e.eventId, role: "assistant", text: "报告草稿已保存,等待交付验收", status: "complete", + download: { href: `/api/sessions/${session}/reports/document`, title: "下载报告(Markdown)" }, + }]); + } + if (e.type === "run.completed") { + const details = (e.payload.delivery as { details?: { artifactId?: string; title?: string; deliveryAccepted?: boolean } })?.details; + if (details?.deliveryAccepted && details.artifactId && /^[0-9a-f-]{36}$/.test(details.artifactId)) + setMsgs(old => old.some(m => m.id === e.eventId) ? old : [...old, { + id: e.eventId, role: "assistant", text: "PPT 文件已通过自动交付检查(事实与视觉仍需复核)", status: "complete", + download: { href: `/api/sessions/${session}/presentations/${details.artifactId}`, title: `${details.title ?? "演示文稿"} · 下载 PPTX` }, + }]); + } if (e.type === "run.completed") setMsgs((old) => old.map((m) => @@ -233,7 +240,7 @@ export function App() { .filter((p) => p.type === "text") .map((p) => p.text) .join(""); - const response = await fetch(`/api/sessions/${session}/messages`, { + const send = (id: string) => fetch(`/api/sessions/${id}/messages`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ @@ -241,6 +248,22 @@ export function App() { attachmentIds: pendingImages.map((image) => image.attachmentId), }), }); + let response: Response; + try { + response = await send(session); + if (response.status === 404) { + if (pendingImages.length) throw new Error("会话已失效,请重置 Session 并重新粘贴图片。"); + const fresh = await create(); + response = await send(fresh); + } + if (!response.ok) { + const problem = await response.json(); + throw new Error(problem.error?.message ?? `发送失败(${response.status})`); + } + } catch (error) { + setAttachmentError(error instanceof Error ? error.message : "连接失败,请检查服务后重试。"); + throw error; + } if (response.ok) { pendingImages.forEach((image) => URL.revokeObjectURL(image.previewUrl)); setPendingImages([]); @@ -327,41 +350,34 @@ export function App() { ); return ( + {monitorOpen&&setMonitorOpen(false)}/>}
-
-
- Agent Studio - MINI -
-
- {running ? "● RUNNING" : "● READY"} - - {restoring - ? "MODEL RESTORING" - : configured - ? "MODEL CONNECTED" - : "MODEL MISSING"} - - - -
-
-