From cf2e4bdd9c3e402766034e42063fb214d21b4ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E9=91=AB=E5=8D=87?= Date: Wed, 13 May 2026 11:36:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=99=BA=E8=83=BD=E4=BD=93=E5=88=86=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend-web/src/App.tsx | 4 +- .../workspace/agents/agent-card.tsx | 170 +++++- .../workspace/agents/agent-gallery.tsx | 2 +- .../components/workspace/recent-chat-list.tsx | 8 +- frontend-web/src/core/agents/api.ts | 14 +- frontend-web/src/core/agents/hooks.ts | 20 +- frontend-web/src/core/agents/types.ts | 23 +- frontend-web/src/core/threads/hooks.ts | 9 +- frontend-web/src/core/threads/types.ts | 2 + frontend-web/src/core/threads/utils.ts | 39 +- frontend-web/src/pages/AgentChatPage.tsx | 21 +- frontend-web/src/pages/NewAgentPage.tsx | 44 +- offline-backend-20260512/backend/CLAUDE.md | 5 + offline-backend-20260512/backend/README.md | 6 + .../backend/app/gateway/deps.py | 13 + .../backend/app/gateway/routers/agents.py | 524 +++++++++--------- .../app/gateway/routers/assistants_compat.py | 84 ++- .../backend/app/gateway/services.py | 96 +++- .../deerflow/agents/lead_agent/agent.py | 17 +- .../deerflow/agents/lead_agent/prompt.py | 13 +- .../harness/deerflow/config/agents_config.py | 69 ++- .../packages/harness/deerflow/config/paths.py | 8 +- .../deerflow/persistence/agents/__init__.py | 22 + .../deerflow/persistence/agents/base.py | 40 ++ .../deerflow/persistence/agents/memory.py | 99 ++++ .../deerflow/persistence/agents/model.py | 24 + .../deerflow/persistence/agents/sql.py | 117 ++++ .../versions/20260513_01_agents_table.py | 47 ++ .../deerflow/persistence/models/__init__.py | 3 + .../tools/builtins/setup_agent_tool.py | 8 +- .../backend/tests/test_agents_backend.py | 122 ++++ 31 files changed, 1237 insertions(+), 436 deletions(-) create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/__init__.py create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/base.py create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/memory.py create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/model.py create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/agents/sql.py create mode 100644 offline-backend-20260512/backend/packages/harness/deerflow/persistence/migrations/versions/20260513_01_agents_table.py create mode 100644 offline-backend-20260512/backend/tests/test_agents_backend.py diff --git a/frontend-web/src/App.tsx b/frontend-web/src/App.tsx index 01168597..5b907bf4 100644 --- a/frontend-web/src/App.tsx +++ b/frontend-web/src/App.tsx @@ -134,11 +134,11 @@ export function App() { } /> } /> diff --git a/frontend-web/src/components/workspace/agents/agent-card.tsx b/frontend-web/src/components/workspace/agents/agent-card.tsx index 25358b1f..27dda218 100644 --- a/frontend-web/src/components/workspace/agents/agent-card.tsx +++ b/frontend-web/src/components/workspace/agents/agent-card.tsx @@ -1,8 +1,15 @@ "use client"; -import { BotIcon, MessageSquareIcon, SquarePenIcon, Trash2Icon } from "lucide-react"; +import { + BotIcon, + EyeIcon, + EyeOffIcon, + MessageSquareIcon, + SquarePenIcon, + Trash2Icon, +} from "lucide-react"; import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; @@ -24,9 +31,11 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { useDeleteAgent, useUpdateAgent } from "@/core/agents"; import type { Agent } from "@/core/agents"; +import { getStoredAuth } from "@/core/auth"; import { useI18n } from "@/core/i18n/hooks"; import { useSkills } from "@/core/skills/hooks"; @@ -42,28 +51,45 @@ export function AgentCard({ agent }: AgentCardProps) { const { skills, isLoading: isSkillsLoading } = useSkills(); const [deleteOpen, setDeleteOpen] = useState(false); const [editOpen, setEditOpen] = useState(false); + const [editName, setEditName] = useState(agent.name); const [editDescription, setEditDescription] = useState(agent.description ?? ""); const [editSoul, setEditSoul] = useState(agent.soul ?? ""); + const [editPublished, setEditPublished] = useState(agent.published); const [selectedSkills, setSelectedSkills] = useState( agent.skills ?? [], ); + const currentUserId = useMemo(() => getStoredAuth()?.user_id ?? null, []); + const isBuiltin = agent.builtin; + const isOwned = + !isBuiltin && agent.user_id !== null && agent.user_id === currentUserId; + const isFromOthers = !isBuiltin && !isOwned; + useEffect(() => { if (!editOpen) { return; } + setEditName(agent.name); setEditDescription(agent.description ?? ""); setEditSoul(agent.soul ?? ""); + setEditPublished(agent.published); setSelectedSkills(agent.skills ?? []); - }, [agent.description, agent.skills, agent.soul, editOpen]); + }, [ + agent.description, + agent.name, + agent.published, + agent.skills, + agent.soul, + editOpen, + ]); function handleChat() { - router.push(`/workspace/agents/${agent.name}/chats/new`); + router.push(`/workspace/agents/${agent.id}/chats/new`); } async function handleDelete() { try { - await deleteAgent.mutateAsync(agent.name); + await deleteAgent.mutateAsync(agent.id); toast.success(t.agents.deleteSuccess); setDeleteOpen(false); } catch (err) { @@ -72,12 +98,19 @@ export function AgentCard({ agent }: AgentCardProps) { } async function handleSaveEdit() { + const trimmedName = editName.trim(); + if (!trimmedName) { + toast.error(t.agents.nameStepInvalidError ?? "名称不能为空"); + return; + } try { await updateAgent.mutateAsync({ - name: agent.name, + id: agent.id, request: { + name: trimmedName, description: editDescription, soul: editSoul, + published: editPublished, skills: selectedSkills, }, }); @@ -88,6 +121,18 @@ export function AgentCard({ agent }: AgentCardProps) { } } + async function handleTogglePublished() { + try { + await updateAgent.mutateAsync({ + id: agent.id, + request: { published: !agent.published }, + }); + toast.success(agent.published ? "已取消发布" : "已发布"); + } catch (err) { + toast.error(err instanceof Error ? err.message : String(err)); + } + } + function toggleSkill(skillName: string) { setSelectedSkills((prev) => { if (prev.includes(skillName)) { @@ -119,6 +164,39 @@ export function AgentCard({ agent }: AgentCardProps) { )} +
+ {isBuiltin && ( + + 内置 + + )} + {isOwned && ( + + 我的 + + )} + {isFromOthers && ( + + 来自他人 + + )} + {isOwned && agent.published && ( + + 已发布 + + )} +
{agent.description && ( @@ -157,41 +235,61 @@ export function AgentCard({ agent }: AgentCardProps) { {t.agents.chat} -
- - -
+ {isOwned && ( +
+ + + +
+ )} - 编辑智能体:{agent.name} + 编辑智能体:{agent.name} - 可以修改描述、技能白名单和 SOUL 内容。 + 可以修改名称、描述、技能白名单、SOUL 内容与发布状态。
- + setEditName(e.target.value)} + placeholder="输入智能体名称(允许中文)" + />
@@ -238,7 +336,7 @@ export function AgentCard({ agent }: AgentCardProps) { )}

- 不选任何技能会保存为无技能(skills = [])。 + 不选任何技能会保存为无技能(skills = [])。

@@ -250,6 +348,18 @@ export function AgentCard({ agent }: AgentCardProps) { className="min-h-40 font-mono text-xs" />
+
+
+

公开发布

+

+ 打开后,该智能体对所有用户可见。 +

+
+ +