From 9012b40230271fda512fa286ff4edb0fcb03f0b5 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Tue, 13 Jan 2026 16:07:30 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20improve=20group=20profile?= =?UTF-8?q?=20builder=20(#11452)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve group topic usage update agent group builder update to v267 update update to use createAgentOnly fix to remove activeId :lipstick: style: update inspector styles refactor implement for agent builder and group builder update style * improve group profile mode * fix editor canvas EditorData Mode * move store to groupProfileStore * update group profile design * update test * fix topic switch issue * update all * update tests --- locales/en-US/chat.json | 7 + locales/en-US/plugin.json | 23 +- locales/zh-CN/chat.json | 7 + locales/zh-CN/plugin.json | 23 +- .../builtin-tool-agent-builder/package.json | 1 + .../Inspector/GetAvailableModels/index.tsx | 66 +++ .../client/Inspector/InstallPlugin/index.tsx | 63 +++ .../Inspector/SearchMarketTools/index.tsx | 64 +++ .../client/Inspector/UpdateConfig/index.tsx | 94 ++++ .../client/Inspector/UpdatePrompt/index.tsx | 96 ++++ .../src/client/Inspector/index.ts | 29 ++ .../src/client/index.ts | 13 + .../src/executor.ts | 132 +++++ .../client/Inspector/ExecuteCode/index.tsx | 19 +- .../src/client/Inspector/RunCommand/index.tsx | 18 +- .../package.json | 8 +- .../src/ExecutionRuntime/index.ts | 464 +++++++++++++----- .../Inspector/BatchCreateAgents/index.tsx | 110 +++++ .../client/Inspector/CreateAgent/index.tsx | 72 +++ .../client/Inspector/InviteAgent/index.tsx | 57 +++ .../client/Inspector/RemoveAgent/index.tsx | 57 +++ .../client/Inspector/SearchAgent/index.tsx | 66 +++ .../Inspector/UpdateAgentPrompt/index.tsx | 120 +++++ .../client/Inspector/UpdateGroup/index.tsx | 87 ++++ .../Inspector/UpdateGroupPrompt/index.tsx | 99 ++++ .../src/client/Inspector/index.ts | 52 ++ .../src/client/Render/BatchCreateAgents.tsx | 103 ++++ .../client/Render/UpdateAgentPrompt/index.tsx | 36 ++ .../client/Render/UpdateGroupPrompt/index.tsx | 36 ++ .../src/client/Render/index.ts | 16 + .../Streaming/BatchCreateAgents/index.tsx | 88 ++++ .../Streaming/UpdateAgentPrompt/index.tsx | 37 ++ .../Streaming/UpdateGroupPrompt/index.tsx | 35 ++ .../src/client/Streaming/index.ts | 22 + .../src/client/index.ts | 26 + .../src/executor.ts | 284 +++++++++++ .../src/index.ts | 15 +- .../src/manifest.ts | 175 ++++++- .../src/systemRole.ts | 278 +++++++++-- .../src/types.ts | 242 +++++++-- .../src/client/Inspector/Broadcast/index.tsx | 4 +- .../src/manifest.ts | 2 +- .../src/client/Inspector/ClearTodos/index.tsx | 16 +- .../client/Inspector/CompleteTodos/index.tsx | 12 +- .../src/client/Inspector/CreatePlan/index.tsx | 21 +- .../client/Inspector/CreateTodos/index.tsx | 12 +- .../src/client/Inspector/ExecTask/index.tsx | 23 +- .../client/Inspector/RemoveTodos/index.tsx | 12 +- .../src/client/Inspector/UpdatePlan/index.tsx | 12 +- .../client/Inspector/UpdateTodos/index.tsx | 12 +- .../client/Inspector/ReadKnowledge/index.tsx | 20 +- .../Inspector/SearchKnowledgeBase/index.tsx | 21 +- .../client/Inspector/EditLocalFile/index.tsx | 16 +- .../client/Inspector/GlobLocalFiles/index.tsx | 18 +- .../client/Inspector/GrepContent/index.tsx | 21 +- .../client/Inspector/ListLocalFiles/index.tsx | 21 +- .../client/Inspector/ReadLocalFile/index.tsx | 21 +- .../Inspector/RenameLocalFile/index.tsx | 16 +- .../src/client/Inspector/RunCommand/index.tsx | 18 +- .../Inspector/SearchLocalFiles/index.tsx | 21 +- .../client/Inspector/WriteLocalFile/index.tsx | 21 +- .../client/Inspector/CreateDocument/index.tsx | 22 +- .../src/client/Inspector/EditTitle/index.tsx | 19 +- .../client/Inspector/GetPageContent/index.tsx | 15 +- .../src/client/Inspector/InitPage/index.tsx | 14 +- .../client/Inspector/ModifyNodes/index.tsx | 12 +- .../client/Inspector/ReplaceText/index.tsx | 16 +- .../Inspector/CrawlMultiPages/index.tsx | 21 +- .../Inspector/CrawlSinglePage/index.tsx | 21 +- .../src/client/Inspector/Search/index.tsx | 19 +- packages/database/src/models/chatGroup.ts | 2 +- packages/types/src/agentGroup/index.ts | 8 + .../agent/_layout/Sidebar/Header/Nav.tsx | 2 +- .../(main)/agent/cron/[cronId]/index.tsx | 7 +- .../profile/features/ProfileEditor/index.tsx | 2 +- .../agent/profile/features/store/action.ts | 39 +- .../(main)/group/_layout/GroupIdSync.tsx | 7 +- .../Sidebar/GroupConfig/AgentProfilePopup.tsx | 50 +- .../Sidebar/GroupConfig/GroupMember.tsx | 1 + .../Sidebar/GroupConfig/GroupMemberItem.tsx | 53 +- .../_layout/Sidebar/Header/AddTopicButon.tsx | 12 +- .../group/_layout/Sidebar/Header/Nav.tsx | 12 +- .../group/_layout/Sidebar/Header/index.tsx | 3 +- .../AgentBuilder/AgentBuilderProvider.tsx | 1 + .../features/AgentBuilder/TopicSelector.tsx | 24 +- .../profile/features/AgentBuilder/index.tsx | 18 +- .../profile/features/EditorCanvas/TypoBar.tsx | 129 ----- .../profile/features/EditorCanvas/index.tsx | 138 ------ .../features/EditorCanvas/useSlashItems.tsx | 139 ------ .../GroupHeader.tsx} | 51 +- .../profile/features/GroupProfile/index.tsx | 96 ++++ .../features/Header/AgentBuilderToggle.tsx | 7 +- .../profile/features/Header/AutoSaveHint.tsx | 18 +- .../features/Header/ChromeTabs/index.tsx | 149 ++++++ .../group/profile/features/Header/index.tsx | 120 ++++- .../features/MemberProfile/AgentHeader.tsx | 222 +++++++++ .../AgentTool.tsx | 0 .../MentionList/MentionDropdown.tsx | 0 .../MentionList/index.tsx | 0 .../MentionList/types.ts | 0 .../MentionList/useMentionItems.tsx | 0 .../profile/features/MemberProfile/index.tsx | 155 ++++++ .../profile/features/ProfileEditor/index.tsx | 82 ---- .../profile/features/ProfileHydration.tsx | 68 ++- .../profile/features/ProfileProvider.tsx | 20 - .../group/profile/features/StoreUpdater.tsx | 24 - .../group/profile/features/store/action.ts | 163 ------ .../group/profile/features/store/index.ts | 23 - .../group/profile/features/store/selectors.ts | 7 - .../[variants]/(main)/group/profile/index.tsx | 71 ++- .../(home)/_layout/SessionHydration.tsx | 2 +- .../SessionListContent/List/Item/index.tsx | 2 +- src/features/AgentBuilder/index.tsx | 17 +- .../Tool/Inspector/StatusIndicator.tsx | 5 +- .../EditorCanvas/EditorCanvas.test.tsx | 206 ++++++++ src/features/EditorCanvas/EditorDataMode.tsx | 72 ++- src/features/EditorModal/EditorCanvas.tsx | 84 ---- src/features/EditorModal/Typobar.tsx | 139 ------ src/features/EditorModal/index.tsx | 4 +- .../NavPanel/components/SessionHydration.tsx | 2 +- .../ShareModal/ShareImage/ChatList/index.tsx | 2 +- src/features/ShareModal/SharePdf/index.tsx | 2 +- src/hooks/useBidirectionalQuerySync.ts | 112 +++++ src/locales/default/chat.ts | 10 + src/locales/default/plugin.ts | 23 +- src/server/routers/lambda/agent.ts | 24 + src/server/routers/lambda/agentGroup.ts | 39 ++ src/services/agent.ts | 22 + src/services/chatGroup/index.ts | 14 + src/store/agent/selectors/selectors.ts | 3 + src/store/agentGroup/initialState.ts | 6 + src/store/agentGroup/selectors/byId.ts | 4 +- src/store/agentGroup/selectors/current.ts | 4 +- src/store/agentGroup/slices/lifecycle.ts | 18 + .../fixtures/mockStore.ts | 2 +- .../actions/__tests__/agentGroup.test.ts | 5 +- .../chat/slices/aiAgent/actions/agentGroup.ts | 2 +- .../__tests__/conversationLifecycle.test.ts | 65 +++ .../aiChat/actions/conversationLifecycle.ts | 3 +- .../actions/__tests__/search.test.ts | 2 +- .../builtinTool/actions/agentBuilder.ts | 192 -------- .../builtinTool/actions/groupAgentBuilder.ts | 242 --------- .../chat/slices/builtinTool/actions/index.ts | 7 +- src/store/chat/slices/message/action.test.ts | 10 +- .../chat/slices/message/actions/publicApi.ts | 10 +- src/store/chat/slices/message/initialState.ts | 5 - .../message/selectors/displayMessage.test.ts | 8 +- src/store/chat/slices/plugin/action.test.ts | 73 ++- .../chat/slices/plugin/actions/pluginTypes.ts | 36 +- src/store/chat/slices/topic/action.test.ts | 98 +++- src/store/chat/slices/topic/action.ts | 34 +- src/store/chat/slices/topic/selectors.test.ts | 2 +- src/store/global/initialState.ts | 10 + src/store/global/selectors/systemStatus.ts | 5 + src/store/groupProfile/action.ts | 168 +++++++ src/store/groupProfile/index.ts | 16 + .../groupProfile}/initialState.ts | 17 + src/store/groupProfile/selectors.ts | 13 + .../tool/slices/builtin/executors/index.ts | 4 + src/styles/text.ts | 16 + src/tools/executionRuntimes.ts | 14 - src/tools/inspectors.ts | 13 + src/tools/renders.ts | 3 + src/tools/streamings.ts | 8 + 164 files changed, 5209 insertions(+), 2352 deletions(-) create mode 100644 packages/builtin-tool-agent-builder/src/client/Inspector/GetAvailableModels/index.tsx create mode 100644 packages/builtin-tool-agent-builder/src/client/Inspector/InstallPlugin/index.tsx create mode 100644 packages/builtin-tool-agent-builder/src/client/Inspector/SearchMarketTools/index.tsx create mode 100644 packages/builtin-tool-agent-builder/src/client/Inspector/UpdateConfig/index.tsx create mode 100644 packages/builtin-tool-agent-builder/src/client/Inspector/UpdatePrompt/index.tsx create mode 100644 packages/builtin-tool-agent-builder/src/client/Inspector/index.ts create mode 100644 packages/builtin-tool-agent-builder/src/executor.ts create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/BatchCreateAgents/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/CreateAgent/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/InviteAgent/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/RemoveAgent/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/SearchAgent/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateAgentPrompt/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroup/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroupPrompt/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Inspector/index.ts create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Render/BatchCreateAgents.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Render/UpdateAgentPrompt/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Render/UpdateGroupPrompt/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Render/index.ts create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Streaming/BatchCreateAgents/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateAgentPrompt/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateGroupPrompt/index.tsx create mode 100644 packages/builtin-tool-group-agent-builder/src/client/Streaming/index.ts create mode 100644 packages/builtin-tool-group-agent-builder/src/client/index.ts create mode 100644 packages/builtin-tool-group-agent-builder/src/executor.ts delete mode 100644 src/app/[variants]/(main)/group/profile/features/EditorCanvas/TypoBar.tsx delete mode 100644 src/app/[variants]/(main)/group/profile/features/EditorCanvas/index.tsx delete mode 100644 src/app/[variants]/(main)/group/profile/features/EditorCanvas/useSlashItems.tsx rename src/app/[variants]/(main)/group/profile/features/{ProfileEditor/AgentHeader.tsx => GroupProfile/GroupHeader.tsx} (75%) create mode 100644 src/app/[variants]/(main)/group/profile/features/GroupProfile/index.tsx create mode 100644 src/app/[variants]/(main)/group/profile/features/Header/ChromeTabs/index.tsx create mode 100644 src/app/[variants]/(main)/group/profile/features/MemberProfile/AgentHeader.tsx rename src/app/[variants]/(main)/group/profile/features/{ProfileEditor => MemberProfile}/AgentTool.tsx (100%) rename src/app/[variants]/(main)/group/profile/features/{ProfileEditor => MemberProfile}/MentionList/MentionDropdown.tsx (100%) rename src/app/[variants]/(main)/group/profile/features/{ProfileEditor => MemberProfile}/MentionList/index.tsx (100%) rename src/app/[variants]/(main)/group/profile/features/{ProfileEditor => MemberProfile}/MentionList/types.ts (100%) rename src/app/[variants]/(main)/group/profile/features/{ProfileEditor => MemberProfile}/MentionList/useMentionItems.tsx (100%) create mode 100644 src/app/[variants]/(main)/group/profile/features/MemberProfile/index.tsx delete mode 100644 src/app/[variants]/(main)/group/profile/features/ProfileEditor/index.tsx delete mode 100644 src/app/[variants]/(main)/group/profile/features/ProfileProvider.tsx delete mode 100644 src/app/[variants]/(main)/group/profile/features/StoreUpdater.tsx delete mode 100644 src/app/[variants]/(main)/group/profile/features/store/action.ts delete mode 100644 src/app/[variants]/(main)/group/profile/features/store/index.ts delete mode 100644 src/app/[variants]/(main)/group/profile/features/store/selectors.ts create mode 100644 src/features/EditorCanvas/EditorCanvas.test.tsx delete mode 100644 src/features/EditorModal/EditorCanvas.tsx delete mode 100644 src/features/EditorModal/Typobar.tsx create mode 100644 src/hooks/useBidirectionalQuerySync.ts delete mode 100644 src/store/chat/slices/builtinTool/actions/agentBuilder.ts delete mode 100644 src/store/chat/slices/builtinTool/actions/groupAgentBuilder.ts create mode 100644 src/store/groupProfile/action.ts create mode 100644 src/store/groupProfile/index.ts rename src/{app/[variants]/(main)/group/profile/features/store => store/groupProfile}/initialState.ts (58%) create mode 100644 src/store/groupProfile/selectors.ts delete mode 100644 src/tools/executionRuntimes.ts diff --git a/locales/en-US/chat.json b/locales/en-US/chat.json index e0ed99130f..4d168f745a 100644 --- a/locales/en-US/chat.json +++ b/locales/en-US/chat.json @@ -71,11 +71,18 @@ "group.desc": "Move a task forward with multiple Agents in one shared space.", "group.memberTooltip": "There are {{count}} members in the group", "group.orchestratorThinking": "Orchestrator is thinking...", + "group.profile.contentPlaceholder": "Set the group objectives/work modes here. This information will be shared with all group members.", + "group.profile.external": "External", + "group.profile.externalAgentWarning": "External agent - changes will sync globally", + "group.profile.groupSettings": "Group Settings", + "group.profile.supervisor": "Supervisor", + "group.profile.supervisorPlaceholder": "The supervisor coordinates different agents. Setting supervisor information here enables more precise workflow coordination.", "group.removeMember": "Remove Member", "group.title": "Group", "groupDescription": "Group description", "groupSidebar.agentProfile.chat": "Chat", "groupSidebar.agentProfile.model": "Model", + "groupSidebar.agentProfile.settings": "Settings", "groupSidebar.members.addMember": "Add Member", "groupSidebar.members.enableOrchestrator": "Enable Orchestrator", "groupSidebar.members.memberSettings": "Member Settings", diff --git a/locales/en-US/plugin.json b/locales/en-US/plugin.json index ca1ea0a754..7d80feea46 100644 --- a/locales/en-US/plugin.json +++ b/locales/en-US/plugin.json @@ -5,6 +5,7 @@ "builtins.lobe-agent-builder.apiName.getConfig": "Get config", "builtins.lobe-agent-builder.apiName.getMeta": "Get metadata", "builtins.lobe-agent-builder.apiName.getPrompt": "Get system prompt", + "builtins.lobe-agent-builder.apiName.installPlugin": "Install Skill", "builtins.lobe-agent-builder.apiName.searchMarketTools": "Search Skill market", "builtins.lobe-agent-builder.apiName.searchOfficialTools": "Search official Skills", "builtins.lobe-agent-builder.apiName.setModel": "Set model", @@ -15,6 +16,12 @@ "builtins.lobe-agent-builder.apiName.updateConfig": "Update config", "builtins.lobe-agent-builder.apiName.updateMeta": "Update metadata", "builtins.lobe-agent-builder.apiName.updatePrompt": "Update system prompt", + "builtins.lobe-agent-builder.inspector.chars": " chars", + "builtins.lobe-agent-builder.inspector.disablePlugin": "Disable", + "builtins.lobe-agent-builder.inspector.enablePlugin": "Enable", + "builtins.lobe-agent-builder.inspector.modelsCount": " and {{count}} more", + "builtins.lobe-agent-builder.inspector.noResults": "No results", + "builtins.lobe-agent-builder.inspector.togglePlugin": "Toggle", "builtins.lobe-agent-builder.title": "Agent Builder Expert", "builtins.lobe-cloud-sandbox.apiName.editLocalFile": "Edit file", "builtins.lobe-cloud-sandbox.apiName.executeCode": "Execute code", @@ -31,13 +38,27 @@ "builtins.lobe-cloud-sandbox.apiName.searchLocalFiles": "Search files", "builtins.lobe-cloud-sandbox.apiName.writeLocalFile": "Write file", "builtins.lobe-cloud-sandbox.title": "Cloud Sandbox", + "builtins.lobe-group-agent-builder.apiName.createAgent": "Create Agent", "builtins.lobe-group-agent-builder.apiName.getAvailableModels": "Get available models", "builtins.lobe-group-agent-builder.apiName.installPlugin": "Install Skill", "builtins.lobe-group-agent-builder.apiName.inviteAgent": "Invite member", "builtins.lobe-group-agent-builder.apiName.removeAgent": "Remove member", + "builtins.lobe-group-agent-builder.apiName.searchAgent": "Search Agents", "builtins.lobe-group-agent-builder.apiName.searchMarketTools": "Search Skill market", + "builtins.lobe-group-agent-builder.apiName.batchCreateAgents": "Batch create agents", "builtins.lobe-group-agent-builder.apiName.updateAgentConfig": "Update agent config", - "builtins.lobe-group-agent-builder.apiName.updatePrompt": "Update system prompt", + "builtins.lobe-group-agent-builder.apiName.updateAgentPrompt": "Update agent prompt", + "builtins.lobe-group-agent-builder.apiName.updateSupervisorPrompt": "Update supervisor prompt", + "builtins.lobe-group-agent-builder.apiName.updateGroup": "Update group", + "builtins.lobe-group-agent-builder.apiName.updateGroupPrompt": "Update group prompt", + "builtins.lobe-group-agent-builder.inspector.agents": "agents", + "builtins.lobe-group-agent-builder.inspector.avatar": "Avatar", + "builtins.lobe-group-agent-builder.inspector.backgroundColor": "Background color", + "builtins.lobe-group-agent-builder.inspector.description": "Description", + "builtins.lobe-group-agent-builder.inspector.noResults": "No results", + "builtins.lobe-group-agent-builder.inspector.openingMessage": "Opening message", + "builtins.lobe-group-agent-builder.inspector.openingQuestions": "Opening questions", + "builtins.lobe-group-agent-builder.inspector.title": "Title", "builtins.lobe-group-agent-builder.title": "Group Builder Expert", "builtins.lobe-group-management.apiName.broadcast": "All speak", "builtins.lobe-group-management.apiName.createAgent": "Add group member", diff --git a/locales/zh-CN/chat.json b/locales/zh-CN/chat.json index 48d4d9e678..c337d301b5 100644 --- a/locales/zh-CN/chat.json +++ b/locales/zh-CN/chat.json @@ -71,11 +71,18 @@ "group.desc": "在同一对话空间,让多个助理一起推进任务", "group.memberTooltip": "群组内有 {{count}} 名成员", "group.orchestratorThinking": "主持人思考中…", + "group.profile.external": "外部", + "group.profile.contentPlaceholder": "在此设定群组的目标/工作模式等,这些信息将会共享给所有群组成员", + "group.profile.externalAgentWarning": "外部助理的配置修改将全局同步生效", + "group.profile.groupSettings": "群组设定", + "group.profile.supervisor": "群组主管", + "group.profile.supervisorPlaceholder": "群组主管负责协调不同 Agent,在此设定主管的信息可以使得协调工作流更加精准", "group.removeMember": "移除成员", "group.title": "群组", "groupDescription": "群组描述", "groupSidebar.agentProfile.chat": "对话", "groupSidebar.agentProfile.model": "模型", + "groupSidebar.agentProfile.settings": "设置", "groupSidebar.members.addMember": "添加成员", "groupSidebar.members.enableOrchestrator": "启用主持人", "groupSidebar.members.memberSettings": "成员设置", diff --git a/locales/zh-CN/plugin.json b/locales/zh-CN/plugin.json index 4663dc28db..4ec1e35250 100644 --- a/locales/zh-CN/plugin.json +++ b/locales/zh-CN/plugin.json @@ -5,6 +5,7 @@ "builtins.lobe-agent-builder.apiName.getConfig": "获取配置", "builtins.lobe-agent-builder.apiName.getMeta": "获取元数据", "builtins.lobe-agent-builder.apiName.getPrompt": "获取系统提示词", + "builtins.lobe-agent-builder.apiName.installPlugin": "安装技能", "builtins.lobe-agent-builder.apiName.searchMarketTools": "搜索技能市场", "builtins.lobe-agent-builder.apiName.searchOfficialTools": "搜索官方技能", "builtins.lobe-agent-builder.apiName.setModel": "设置模型", @@ -15,6 +16,12 @@ "builtins.lobe-agent-builder.apiName.updateConfig": "更新配置", "builtins.lobe-agent-builder.apiName.updateMeta": "更新元数据", "builtins.lobe-agent-builder.apiName.updatePrompt": "更新系统提示词", + "builtins.lobe-agent-builder.inspector.chars": "字", + "builtins.lobe-agent-builder.inspector.disablePlugin": "关闭", + "builtins.lobe-agent-builder.inspector.enablePlugin": "开启", + "builtins.lobe-agent-builder.inspector.modelsCount": " 等 {{count}} 个模型", + "builtins.lobe-agent-builder.inspector.noResults": "无结果", + "builtins.lobe-agent-builder.inspector.togglePlugin": "切换", "builtins.lobe-agent-builder.title": "助理构建专家", "builtins.lobe-cloud-sandbox.apiName.editLocalFile": "编辑文件", "builtins.lobe-cloud-sandbox.apiName.executeCode": "执行代码", @@ -31,13 +38,27 @@ "builtins.lobe-cloud-sandbox.apiName.searchLocalFiles": "搜索文件", "builtins.lobe-cloud-sandbox.apiName.writeLocalFile": "写入文件", "builtins.lobe-cloud-sandbox.title": "云端沙盒", + "builtins.lobe-group-agent-builder.apiName.createAgent": "创建 Agent", "builtins.lobe-group-agent-builder.apiName.getAvailableModels": "获取可用模型", "builtins.lobe-group-agent-builder.apiName.installPlugin": "安装技能", "builtins.lobe-group-agent-builder.apiName.inviteAgent": "邀请成员", "builtins.lobe-group-agent-builder.apiName.removeAgent": "移除成员", + "builtins.lobe-group-agent-builder.apiName.searchAgent": "搜索 Agent", "builtins.lobe-group-agent-builder.apiName.searchMarketTools": "搜索技能市场", + "builtins.lobe-group-agent-builder.apiName.batchCreateAgents": "批量创建 Agent", "builtins.lobe-group-agent-builder.apiName.updateAgentConfig": "更新代理配置", - "builtins.lobe-group-agent-builder.apiName.updatePrompt": "更新系统提示词", + "builtins.lobe-group-agent-builder.apiName.updateAgentPrompt": "更新助理提示词", + "builtins.lobe-group-agent-builder.apiName.updateSupervisorPrompt": "更新群组主管提示词", + "builtins.lobe-group-agent-builder.apiName.updateGroup": "更新群组", + "builtins.lobe-group-agent-builder.apiName.updateGroupPrompt": "更新群组提示词", + "builtins.lobe-group-agent-builder.inspector.agents": "个 Agent", + "builtins.lobe-group-agent-builder.inspector.avatar": "头像", + "builtins.lobe-group-agent-builder.inspector.backgroundColor": "背景色", + "builtins.lobe-group-agent-builder.inspector.description": "描述", + "builtins.lobe-group-agent-builder.inspector.noResults": "无结果", + "builtins.lobe-group-agent-builder.inspector.openingMessage": "开场白", + "builtins.lobe-group-agent-builder.inspector.openingQuestions": "开场问题", + "builtins.lobe-group-agent-builder.inspector.title": "标题", "builtins.lobe-group-agent-builder.title": "群组构建专家", "builtins.lobe-group-management.apiName.broadcast": "所有人发言", "builtins.lobe-group-management.apiName.createAgent": "添加群组成员", diff --git a/packages/builtin-tool-agent-builder/package.json b/packages/builtin-tool-agent-builder/package.json index 8a8c5b4fc0..27e7bd9dd9 100644 --- a/packages/builtin-tool-agent-builder/package.json +++ b/packages/builtin-tool-agent-builder/package.json @@ -5,6 +5,7 @@ "exports": { ".": "./src/index.ts", "./client": "./src/client/index.ts", + "./executor": "./src/executor.ts", "./executionRuntime": "./src/ExecutionRuntime/index.ts" }, "main": "./src/index.ts", diff --git a/packages/builtin-tool-agent-builder/src/client/Inspector/GetAvailableModels/index.tsx b/packages/builtin-tool-agent-builder/src/client/Inspector/GetAvailableModels/index.tsx new file mode 100644 index 0000000000..57abf95a01 --- /dev/null +++ b/packages/builtin-tool-agent-builder/src/client/Inspector/GetAvailableModels/index.tsx @@ -0,0 +1,66 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { cx } from 'antd-style'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { GetAvailableModelsParams, GetAvailableModelsState } from '../../../types'; + +export const GetAvailableModelsInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const providerId = args?.providerId || partialArgs?.providerId; + + // Calculate total model count from providers + const modelInfo = useMemo(() => { + if (!pluginState?.providers) return null; + + const allModels = pluginState.providers.flatMap((p) => p.models); + const totalCount = allModels.length; + + if (totalCount === 0) return null; + + // Get first 2 model names for display + const displayModels = allModels.slice(0, 2).map((m) => m.name || m.id); + return { displayModels, totalCount }; + }, [pluginState?.providers]); + + // Initial streaming state + if (isArgumentsStreaming || isLoading) { + return ( +
+ {t('builtins.lobe-agent-builder.apiName.getAvailableModels')} + {providerId && ( + <> + : {providerId} + + )} +
+ ); + } + + // Loaded state with results + return ( +
+ {t('builtins.lobe-agent-builder.apiName.getAvailableModels')}: + {modelInfo && ( + + {modelInfo.displayModels.join(' / ')} + {modelInfo.totalCount > 2 && + t('builtins.lobe-agent-builder.inspector.modelsCount', { + count: modelInfo.totalCount, + })} + + )} +
+ ); +}); + +GetAvailableModelsInspector.displayName = 'GetAvailableModelsInspector'; + +export default GetAvailableModelsInspector; diff --git a/packages/builtin-tool-agent-builder/src/client/Inspector/InstallPlugin/index.tsx b/packages/builtin-tool-agent-builder/src/client/Inspector/InstallPlugin/index.tsx new file mode 100644 index 0000000000..23a7092204 --- /dev/null +++ b/packages/builtin-tool-agent-builder/src/client/Inspector/InstallPlugin/index.tsx @@ -0,0 +1,63 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { Check, X } from 'lucide-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { InstallPluginParams, InstallPluginState } from '../../../types'; + +const styles = createStaticStyles(({ css }) => ({ + statusIcon: css` + margin-block-end: -2px; + margin-inline-start: 4px; + `, +})); + +export const InstallPluginInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const identifier = args?.identifier || partialArgs?.identifier; + const displayName = pluginState?.pluginName || identifier; + + // Initial streaming state + if (isArgumentsStreaming && !identifier) { + return ( +
+ {t('builtins.lobe-agent-builder.apiName.installPlugin')} +
+ ); + } + + // Get installation result + const isSuccess = pluginState?.success && pluginState?.installed; + const hasResult = pluginState?.success !== undefined; + + return ( +
+ {t('builtins.lobe-agent-builder.apiName.installPlugin')}: + {displayName && {displayName}} + {!isLoading && + hasResult && + (isSuccess ? ( + + ) : ( + + ))} +
+ ); +}); + +InstallPluginInspector.displayName = 'InstallPluginInspector'; + +export default InstallPluginInspector; diff --git a/packages/builtin-tool-agent-builder/src/client/Inspector/SearchMarketTools/index.tsx b/packages/builtin-tool-agent-builder/src/client/Inspector/SearchMarketTools/index.tsx new file mode 100644 index 0000000000..7547e8e316 --- /dev/null +++ b/packages/builtin-tool-agent-builder/src/client/Inspector/SearchMarketTools/index.tsx @@ -0,0 +1,64 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { Text } from '@lobehub/ui'; +import { cssVar, cx } from 'antd-style'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { SearchMarketToolsParams, SearchMarketToolsState } from '../../../types'; + +export const SearchMarketToolsInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const query = args?.query || partialArgs?.query; + const category = args?.category || partialArgs?.category; + const displayText = query || category; + + // Initial streaming state + if (isArgumentsStreaming && !displayText) { + return ( +
+ {t('builtins.lobe-agent-builder.apiName.searchMarketTools')} +
+ ); + } + + const resultCount = pluginState?.tools?.length ?? 0; + const hasResults = resultCount > 0; + + return ( +
+ {t('builtins.lobe-agent-builder.apiName.searchMarketTools')}: + {displayText && {displayText}} + {!isLoading && + !isArgumentsStreaming && + pluginState?.tools && + (hasResults ? ( + ({resultCount}) + ) : ( + + ({t('builtins.lobe-agent-builder.inspector.noResults')}) + + ))} +
+ ); +}); + +SearchMarketToolsInspector.displayName = 'SearchMarketToolsInspector'; + +export default SearchMarketToolsInspector; diff --git a/packages/builtin-tool-agent-builder/src/client/Inspector/UpdateConfig/index.tsx b/packages/builtin-tool-agent-builder/src/client/Inspector/UpdateConfig/index.tsx new file mode 100644 index 0000000000..cc5252a4c2 --- /dev/null +++ b/packages/builtin-tool-agent-builder/src/client/Inspector/UpdateConfig/index.tsx @@ -0,0 +1,94 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { Check } from 'lucide-react'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { UpdateAgentConfigParams, UpdateConfigState } from '../../../types'; + +const styles = createStaticStyles(({ css }) => ({ + statusIcon: css` + margin-block-end: -2px; + margin-inline-start: 4px; + `, +})); + +export const UpdateConfigInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const togglePlugin = args?.togglePlugin || partialArgs?.togglePlugin; + const config = args?.config || partialArgs?.config; + const meta = args?.meta || partialArgs?.meta; + + // Build display text + const displayText = useMemo(() => { + // If toggling plugin, show that info + if (togglePlugin?.pluginId) { + const enabled = togglePlugin.enabled ?? pluginState?.togglePlugin?.enabled; + const action = + enabled === true + ? t('builtins.lobe-agent-builder.inspector.enablePlugin') + : enabled === false + ? t('builtins.lobe-agent-builder.inspector.disablePlugin') + : t('builtins.lobe-agent-builder.inspector.togglePlugin'); + return `${action} ${togglePlugin.pluginId}`; + } + + // Otherwise show updated fields + const fields: string[] = []; + if (config) { + if (config.model) fields.push('model'); + if (config.provider) fields.push('provider'); + if (config.plugins) fields.push('plugins'); + if (config.params) fields.push('params'); + if (config.chatConfig) fields.push('chatConfig'); + } + if (meta) { + if (meta.title) fields.push('title'); + if (meta.description) fields.push('description'); + if (meta.avatar) fields.push('avatar'); + } + + return fields.length > 0 ? fields.join(', ') : ''; + }, [togglePlugin, config, meta, pluginState, t]); + + // Initial streaming state + if (isArgumentsStreaming && !displayText) { + return ( +
+ {t('builtins.lobe-agent-builder.apiName.updateConfig')} +
+ ); + } + + const isSuccess = pluginState?.success; + + return ( +
+ {t('builtins.lobe-agent-builder.apiName.updateConfig')} + {displayText && ( + <> + : {displayText} + + )} + {!isLoading && isSuccess && ( + + )} +
+ ); +}); + +UpdateConfigInspector.displayName = 'UpdateConfigInspector'; + +export default UpdateConfigInspector; diff --git a/packages/builtin-tool-agent-builder/src/client/Inspector/UpdatePrompt/index.tsx b/packages/builtin-tool-agent-builder/src/client/Inspector/UpdatePrompt/index.tsx new file mode 100644 index 0000000000..aaa8e8f6dd --- /dev/null +++ b/packages/builtin-tool-agent-builder/src/client/Inspector/UpdatePrompt/index.tsx @@ -0,0 +1,96 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { Text } from '@lobehub/ui'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { Check } from 'lucide-react'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { UpdatePromptParams, UpdatePromptState } from '../../../types'; + +const styles = createStaticStyles(({ css }) => ({ + statusIcon: css` + margin-block-end: -2px; + margin-inline-start: 4px; + `, +})); + +export const UpdatePromptInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const prompt = args?.prompt || partialArgs?.prompt; + + // Calculate length difference + const lengthDiff = useMemo(() => { + if (!pluginState) return null; + + const newLength = pluginState.newPrompt?.length ?? 0; + const prevLength = pluginState.previousPrompt?.length ?? 0; + const diff = newLength - prevLength; + + return diff; + }, [pluginState]); + + // Initial streaming state + if (isArgumentsStreaming && !prompt) { + return ( +
+ {t('builtins.lobe-agent-builder.apiName.updatePrompt')} +
+ ); + } + + // Calculate streaming length change + const streamingLength = prompt?.length ?? 0; + const isSuccess = pluginState?.success; + + return ( +
+ {t('builtins.lobe-agent-builder.apiName.updatePrompt')} + {/* Show length diff when completed */} + {!isLoading && !isArgumentsStreaming && lengthDiff !== null && ( + = 0 ? cssVar.colorSuccess : cssVar.colorError} + fontSize={12} + style={{ marginInlineStart: 4 }} + > + ({lengthDiff >= 0 ? '+' : ''} + {lengthDiff} + {t('builtins.lobe-agent-builder.inspector.chars')}) + + )} + {/* Show streaming length */} + {(isArgumentsStreaming || isLoading) && streamingLength > 0 && ( + + ({streamingLength} + {t('builtins.lobe-agent-builder.inspector.chars')}) + + )} + {!isLoading && !isArgumentsStreaming && isSuccess && ( + + )} +
+ ); +}); + +UpdatePromptInspector.displayName = 'UpdatePromptInspector'; + +export default UpdatePromptInspector; diff --git a/packages/builtin-tool-agent-builder/src/client/Inspector/index.ts b/packages/builtin-tool-agent-builder/src/client/Inspector/index.ts new file mode 100644 index 0000000000..5db04cf533 --- /dev/null +++ b/packages/builtin-tool-agent-builder/src/client/Inspector/index.ts @@ -0,0 +1,29 @@ +import { type BuiltinInspector } from '@lobechat/types'; + +import { AgentBuilderApiName } from '../../types'; +import { GetAvailableModelsInspector } from './GetAvailableModels'; +import { InstallPluginInspector } from './InstallPlugin'; +import { SearchMarketToolsInspector } from './SearchMarketTools'; +import { UpdateConfigInspector } from './UpdateConfig'; +import { UpdatePromptInspector } from './UpdatePrompt'; + +/** + * Agent Builder Inspector Components Registry + * + * Inspector components customize the title/header area + * of tool calls in the conversation UI. + */ +export const AgentBuilderInspectors: Record = { + [AgentBuilderApiName.getAvailableModels]: GetAvailableModelsInspector as BuiltinInspector, + [AgentBuilderApiName.installPlugin]: InstallPluginInspector as BuiltinInspector, + [AgentBuilderApiName.searchMarketTools]: SearchMarketToolsInspector as BuiltinInspector, + [AgentBuilderApiName.updateAgentConfig]: UpdateConfigInspector as BuiltinInspector, + [AgentBuilderApiName.updatePrompt]: UpdatePromptInspector as BuiltinInspector, +}; + +// Re-export individual inspectors for reuse in group-agent-builder +export { GetAvailableModelsInspector } from './GetAvailableModels'; +export { InstallPluginInspector } from './InstallPlugin'; +export { SearchMarketToolsInspector } from './SearchMarketTools'; +export { UpdateConfigInspector } from './UpdateConfig'; +export { UpdatePromptInspector } from './UpdatePrompt'; diff --git a/packages/builtin-tool-agent-builder/src/client/index.ts b/packages/builtin-tool-agent-builder/src/client/index.ts index d51263fa4c..3eb7037d62 100644 --- a/packages/builtin-tool-agent-builder/src/client/index.ts +++ b/packages/builtin-tool-agent-builder/src/client/index.ts @@ -1,4 +1,17 @@ +// Inspector components (customized tool call headers) +export { AgentBuilderInspectors } from './Inspector'; +export { + GetAvailableModelsInspector, + InstallPluginInspector, + SearchMarketToolsInspector, + UpdateConfigInspector, + UpdatePromptInspector, +} from './Inspector'; + +// Intervention components (interactive editing) export { AgentBuilderInterventions } from './Intervention'; + +// Render components (read-only snapshots) export { AgentBuilderRenders } from './Render'; // Re-export types and manifest for convenience diff --git a/packages/builtin-tool-agent-builder/src/executor.ts b/packages/builtin-tool-agent-builder/src/executor.ts new file mode 100644 index 0000000000..7616cacea9 --- /dev/null +++ b/packages/builtin-tool-agent-builder/src/executor.ts @@ -0,0 +1,132 @@ +/** + * Agent Builder Executor + * + * Handles all agent builder tool calls for configuring and customizing agents. + */ +import { BaseExecutor, type BuiltinToolContext, type BuiltinToolResult } from '@lobechat/types'; + +import { AgentBuilderExecutionRuntime } from './ExecutionRuntime'; +import { + AgentBuilderApiName, + AgentBuilderIdentifier, + type GetAvailableModelsParams, + type InstallPluginParams, + type SearchMarketToolsParams, + type UpdateAgentConfigParams, + type UpdatePromptParams, +} from './types'; + +const runtime = new AgentBuilderExecutionRuntime(); + +class AgentBuilderExecutor extends BaseExecutor { + readonly identifier = AgentBuilderIdentifier; + protected readonly apiEnum = AgentBuilderApiName; + + // ==================== Read Operations ==================== + + getAvailableModels = async (params: GetAvailableModelsParams): Promise => { + const result = await runtime.getAvailableModels(params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + searchMarketTools = async (params: SearchMarketToolsParams): Promise => { + const result = await runtime.searchMarketTools(params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + // ==================== Write Operations ==================== + + updateConfig = async ( + params: UpdateAgentConfigParams, + ctx: BuiltinToolContext, + ): Promise => { + const agentId = ctx.agentId; + + if (!agentId) { + return { + content: 'No active agent found', + error: { message: 'No active agent found', type: 'NoAgentContext' }, + success: false, + }; + } + + const result = await runtime.updateAgentConfig(agentId, params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + updatePrompt = async ( + params: UpdatePromptParams, + ctx: BuiltinToolContext, + ): Promise => { + const agentId = ctx.agentId; + + if (!agentId) { + return { + content: 'No active agent found', + error: { message: 'No active agent found', type: 'NoAgentContext' }, + success: false, + }; + } + + const result = await runtime.updatePrompt(agentId, { + streaming: true, + ...params, + }); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + installPlugin = async ( + params: InstallPluginParams, + ctx: BuiltinToolContext, + ): Promise => { + const agentId = ctx.agentId; + + if (!agentId) { + return { + content: 'No active agent found', + error: { message: 'No active agent found', type: 'NoAgentContext' }, + success: false, + }; + } + + const result = await runtime.installPlugin(agentId, params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; +} + +export const agentBuilderExecutor = new AgentBuilderExecutor(); diff --git a/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ExecuteCode/index.tsx b/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ExecuteCode/index.tsx index 809875254b..d23a7bcc03 100644 --- a/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ExecuteCode/index.tsx +++ b/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ExecuteCode/index.tsx @@ -6,20 +6,11 @@ import { Check, X } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type ExecuteCodeState } from '../../../types'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, - +const styles = createStaticStyles(({ css }) => ({ statusIcon: css` margin-block-end: -2px; margin-inline-start: 4px; @@ -42,13 +33,13 @@ export const ExecuteCodeInspector = memo< if (isArgumentsStreaming) { if (!description) return ( -
+
{t('builtins.lobe-cloud-sandbox.apiName.executeCode')}
); return ( -
+
{t('builtins.lobe-cloud-sandbox.apiName.executeCode')}: {description}
@@ -56,7 +47,7 @@ export const ExecuteCodeInspector = memo< } return ( -
+
{t('builtins.lobe-cloud-sandbox.apiName.executeCode')}: {description && {description}} diff --git a/packages/builtin-tool-cloud-sandbox/src/client/Inspector/RunCommand/index.tsx b/packages/builtin-tool-cloud-sandbox/src/client/Inspector/RunCommand/index.tsx index 9ac3d93d2f..365e8d8bc4 100644 --- a/packages/builtin-tool-cloud-sandbox/src/client/Inspector/RunCommand/index.tsx +++ b/packages/builtin-tool-cloud-sandbox/src/client/Inspector/RunCommand/index.tsx @@ -6,19 +6,11 @@ import { Check, X } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type RunCommandState } from '../../../types'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, +const styles = createStaticStyles(({ css }) => ({ statusIcon: css` margin-block-end: -2px; margin-inline-start: 4px; @@ -41,13 +33,13 @@ export const RunCommandInspector = memo +
{t('builtins.lobe-cloud-sandbox.apiName.runCommand')}
); return ( -
+
{t('builtins.lobe-cloud-sandbox.apiName.runCommand')}: {description}
@@ -55,7 +47,7 @@ export const RunCommandInspector = memo +
{t('builtins.lobe-cloud-sandbox.apiName.runCommand')}: {description && {description}} diff --git a/packages/builtin-tool-group-agent-builder/package.json b/packages/builtin-tool-group-agent-builder/package.json index 02013639aa..0ab737b444 100644 --- a/packages/builtin-tool-group-agent-builder/package.json +++ b/packages/builtin-tool-group-agent-builder/package.json @@ -4,6 +4,8 @@ "private": true, "exports": { ".": "./src/index.ts", + "./client": "./src/client/index.ts", + "./executor": "./src/executor.ts", "./executionRuntime": "./src/ExecutionRuntime/index.ts" }, "main": "./src/index.ts", @@ -14,6 +16,10 @@ "@lobechat/types": "workspace:*" }, "peerDependencies": { - "react": "*" + "@lobehub/ui": "^4", + "antd": "^6", + "lucide-react": "*", + "react": "*", + "react-i18next": "*" } } diff --git a/packages/builtin-tool-group-agent-builder/src/ExecutionRuntime/index.ts b/packages/builtin-tool-group-agent-builder/src/ExecutionRuntime/index.ts index 7aeae1c347..31f864dfa4 100644 --- a/packages/builtin-tool-group-agent-builder/src/ExecutionRuntime/index.ts +++ b/packages/builtin-tool-group-agent-builder/src/ExecutionRuntime/index.ts @@ -1,18 +1,27 @@ import type { BuiltinServerRuntimeOutput } from '@lobechat/types'; -import { chatGroupService } from '@/services/chatGroup'; +import { agentService } from '@/services/agent'; +import { type GroupMemberConfig, chatGroupService } from '@/services/chatGroup'; +import { useAgentStore } from '@/store/agent'; import { getChatGroupStoreState } from '@/store/agentGroup'; import { agentGroupSelectors } from '@/store/agentGroup/selectors'; import type { + BatchCreateAgentsParams, + BatchCreateAgentsState, + CreateAgentParams, + CreateAgentState, InviteAgentParams, InviteAgentState, RemoveAgentParams, RemoveAgentState, - UpdateGroupConfigParams, - UpdateGroupConfigState, + SearchAgentParams, + SearchAgentState, + UpdateAgentPromptParams, + UpdateGroupParams, UpdateGroupPromptParams, UpdateGroupPromptState, + UpdateGroupState, } from '../types'; /** @@ -21,7 +30,184 @@ import type { * Extends AgentBuilder functionality with group-specific operations */ export class GroupAgentBuilderExecutionRuntime { - // ==================== Group-specific Operations ==================== + // ==================== Group Member Management ==================== + + /** + * Search for agents that can be invited to the group + */ + async searchAgent(args: SearchAgentParams): Promise { + const { query, limit = 10 } = args; + + try { + const results = await agentService.queryAgents({ keyword: query, limit }); + + const agents = results.map((agent) => ({ + avatar: agent.avatar, + description: agent.description, + id: agent.id, + title: agent.title, + })); + + const total = agents.length; + + if (total === 0) { + return { + content: query + ? `No agents found matching "${query}".` + : 'No agents found. You can create a new agent or search with different keywords.', + state: { agents: [], query, total: 0 } as SearchAgentState, + success: true, + }; + } + + // Format agents list for LLM consumption + const agentList = agents + .map( + (a, i) => + `${i + 1}. ${a.title || 'Untitled'} (ID: ${a.id})${a.description ? ` - ${a.description}` : ''}`, + ) + .join('\n'); + + return { + content: query + ? `Found ${total} agent${total > 1 ? 's' : ''} matching "${query}":\n${agentList}` + : `Found ${total} agent${total > 1 ? 's' : ''}:\n${agentList}`, + state: { agents, query, total } as SearchAgentState, + success: true, + }; + } catch (error) { + const err = error as Error; + return { + content: `Failed to search agents: ${err.message}`, + error, + success: false, + }; + } + } + + /** + * Create a new agent and add it to the group + */ + async createAgent(groupId: string, args: CreateAgentParams): Promise { + try { + const state = getChatGroupStoreState(); + const group = agentGroupSelectors.getGroupById(groupId)(state); + + if (!group) { + return { + content: 'Group not found', + error: 'Group not found', + success: false, + }; + } + + // Create a virtual agent only (no session needed for group agents) + // Map 'tools' from LLM input to 'plugins' for internal API + const result = await agentService.createAgentOnly({ + config: { + avatar: args.avatar, + description: args.description, + plugins: args.tools, + systemRole: args.systemRole, + title: args.title, + virtual: true, + }, + groupId, + }); + + if (!result.agentId) { + return { + content: 'Failed to create agent: No agent ID returned', + success: false, + }; + } + + // Refresh the group detail in the store + await state.refreshGroupDetail(groupId); + + return { + content: `Successfully created agent "${args.title}" and added it to the group.`, + state: { + agentId: result.agentId, + success: true, + title: args.title, + } as CreateAgentState, + success: true, + }; + } catch (error) { + const err = error as Error; + return { + content: `Failed to create agent: ${err.message}`, + error, + success: false, + }; + } + } + + /** + * Create multiple agents at once and add them to the group + * Uses batch API for efficiency (single request instead of N requests) + */ + async batchCreateAgents( + groupId: string, + args: BatchCreateAgentsParams, + ): Promise { + try { + const state = getChatGroupStoreState(); + const group = agentGroupSelectors.getGroupById(groupId)(state); + + if (!group) { + return { + content: 'Group not found', + error: 'Group not found', + success: false, + }; + } + + // Use batch API to create all agents in one request + // Map 'tools' from LLM input to 'plugins' for internal API + const agentConfigs: GroupMemberConfig[] = args.agents.map((agentDef) => ({ + avatar: agentDef.avatar, + description: agentDef.description, + plugins: agentDef.tools, + systemRole: agentDef.systemRole, + title: agentDef.title, + })); + + const { agents: createdAgents } = await chatGroupService.batchCreateAgentsInGroup( + groupId, + agentConfigs, + ); + + // Refresh the group detail in the store + await state.refreshGroupDetail(groupId); + + const results = createdAgents.map((agent, index) => ({ + agentId: agent.id, + success: true, + title: args.agents[index].title, + })); + + const createdList = results.map((r) => `- ${r.title} (ID: ${r.agentId})`).join('\n'); + + return { + content: `Successfully created ${results.length} agent${results.length > 1 ? 's' : ''}:\n${createdList}`, + state: { + agents: results, + failedCount: 0, + successCount: results.length, + } as BatchCreateAgentsState, + success: true, + }; + } catch (error) { + const err = error as Error; + return { + content: `Failed to create agents: ${err.message}`, + error, + success: false, + }; + } + } /** * Invite an agent to the group @@ -149,11 +335,58 @@ export class GroupAgentBuilderExecutionRuntime { } } + // ==================== Group Configuration ==================== + /** - * Update group system prompt - * Unlike regular AgentBuilder, this updates the group's systemPrompt in groupConfig + * Update a specific agent's system prompt (systemRole) */ - async updatePrompt(args: UpdateGroupPromptParams): Promise { + async updateAgentPrompt( + groupId: string, + args: UpdateAgentPromptParams, + ): Promise { + try { + const { agentId, prompt } = args; + + // Get previous prompt for state + const state = getChatGroupStoreState(); + const group = agentGroupSelectors.getGroupById(groupId)(state); + const agent = group?.agents?.find((a) => a.id === agentId); + const previousPrompt = agent?.systemRole ?? undefined; + + // Update the agent's systemRole via agent store + await useAgentStore.getState().updateAgentConfigById(agentId, { systemRole: prompt }); + + // Refresh the group detail in the store to sync agent data + await state.refreshGroupDetail(groupId); + + const content = prompt + ? `Successfully updated agent ${agentId} system prompt (${prompt.length} characters)` + : `Successfully cleared agent ${agentId} system prompt`; + + return { + content, + state: { + agentId, + newPrompt: prompt, + previousPrompt, + success: true, + }, + success: true, + }; + } catch (error) { + const err = error as Error; + return { + content: `Failed to update agent prompt: ${err.message}`, + error, + success: false, + }; + } + } + + /** + * Update group configuration and metadata (unified method) + */ + async updateGroup(args: UpdateGroupParams): Promise { try { const state = getChatGroupStoreState(); const group = agentGroupSelectors.currentGroup(state); @@ -166,19 +399,122 @@ export class GroupAgentBuilderExecutionRuntime { }; } - const previousPrompt = group.config?.systemPrompt; + const { config, meta } = args; + + if (!config && !meta) { + return { + content: 'No configuration or metadata provided', + error: 'No configuration or metadata provided', + success: false, + }; + } + + const updatedFields: string[] = []; + const resultState: UpdateGroupState = { success: true }; + + // Update config if provided + if (config) { + const configUpdate: { openingMessage?: string; openingQuestions?: string[] } = {}; + + if (config.openingMessage !== undefined) { + configUpdate.openingMessage = config.openingMessage; + updatedFields.push( + config.openingMessage + ? `openingMessage (${config.openingMessage.length} chars)` + : 'openingMessage (cleared)', + ); + } + + if (config.openingQuestions !== undefined) { + configUpdate.openingQuestions = config.openingQuestions; + updatedFields.push( + config.openingQuestions.length > 0 + ? `openingQuestions (${config.openingQuestions.length} questions)` + : 'openingQuestions (cleared)', + ); + } + + if (Object.keys(configUpdate).length > 0) { + await state.updateGroupConfig(configUpdate); + resultState.updatedConfig = configUpdate; + } + } + + // Update meta if provided + if (meta && Object.keys(meta).length > 0) { + await state.updateGroupMeta(meta); + resultState.updatedMeta = meta; + + if (meta.avatar !== undefined) { + updatedFields.push(`avatar (${meta.avatar || 'cleared'})`); + } + if (meta.title !== undefined) { + updatedFields.push(`title (${meta.title || 'cleared'})`); + } + if (meta.description !== undefined) { + updatedFields.push( + meta.description + ? `description (${meta.description.length} chars)` + : 'description (cleared)', + ); + } + if (meta.backgroundColor !== undefined) { + updatedFields.push(`backgroundColor (${meta.backgroundColor || 'cleared'})`); + } + } + + // Refresh the group detail in the store to ensure data sync + await state.refreshGroupDetail(group.id); + + const content = `Successfully updated group: ${updatedFields.join(', ')}`; + + return { + content, + state: resultState, + success: true, + }; + } catch (error) { + const err = error as Error; + return { + content: `Failed to update group: ${err.message}`, + error, + success: false, + }; + } + } + + /** + * Update group shared prompt/content + */ + async updateGroupPrompt(args: UpdateGroupPromptParams): Promise { + try { + const state = getChatGroupStoreState(); + const group = agentGroupSelectors.currentGroup(state); + + if (!group) { + return { + content: 'No active group found', + error: 'No active group found', + success: false, + }; + } + + const previousPrompt = group.content ?? undefined; if (args.streaming) { // Use streaming mode for typewriter effect - await this.streamUpdatePrompt(args.prompt); + await this.streamUpdateGroupPrompt(args.prompt); } else { - // Update the system prompt directly - await state.updateGroupConfig({ systemPrompt: args.prompt }); + // Update the content directly + await state.updateGroup(group.id, { content: args.prompt }); } + // Refresh the group detail in the store to ensure data sync + await state.refreshGroupDetail(group.id); + const content = args.prompt - ? `Successfully updated group system prompt (${args.prompt.length} characters)` - : 'Successfully cleared group system prompt'; + ? `Successfully updated group shared prompt (${args.prompt.length} characters)` + : 'Successfully cleared group shared prompt'; return { content, @@ -204,106 +540,14 @@ export class GroupAgentBuilderExecutionRuntime { } /** - * Update group configuration (openingMessage, openingQuestions) + * Stream update group prompt with typewriter effect */ - async updateGroupConfig(args: UpdateGroupConfigParams): Promise { - try { - const state = getChatGroupStoreState(); - const group = agentGroupSelectors.currentGroup(state); - - if (!group) { - return { - content: 'No active group found', - error: 'No active group found', - success: false, - }; - } - - const { config } = args; - - if (!config) { - return { - content: 'No configuration provided', - error: 'No configuration provided', - success: false, - }; - } - - // Build the config update object - const configUpdate: { openingMessage?: string; openingQuestions?: string[] } = {}; - - if (config.openingMessage !== undefined) { - configUpdate.openingMessage = config.openingMessage; - } - - if (config.openingQuestions !== undefined) { - configUpdate.openingQuestions = config.openingQuestions; - } - - // Update the group config - await state.updateGroupConfig(configUpdate); - - const updatedFields: string[] = []; - if (config.openingMessage !== undefined) { - updatedFields.push( - config.openingMessage - ? `openingMessage (${config.openingMessage.length} chars)` - : 'openingMessage (cleared)', - ); - } - if (config.openingQuestions !== undefined) { - updatedFields.push( - config.openingQuestions.length > 0 - ? `openingQuestions (${config.openingQuestions.length} questions)` - : 'openingQuestions (cleared)', - ); - } - - const content = `Successfully updated group configuration: ${updatedFields.join(', ')}`; - - return { - content, - state: { - success: true, - updatedConfig: configUpdate, - } as UpdateGroupConfigState, - success: true, - }; - } catch (error) { - const err = error as Error; - return { - content: `Failed to update group configuration: ${err.message}`, - error, - success: false, - }; - } - } - - /** - * Stream update prompt with typewriter effect for group - */ - private async streamUpdatePrompt(prompt: string): Promise { + private async streamUpdateGroupPrompt(prompt: string): Promise { const state = getChatGroupStoreState(); + const group = agentGroupSelectors.currentGroup(state); - // Start streaming - state.startStreamingSystemPrompt(); + if (!group) return; - // Simulate streaming by chunking the content - const chunkSize = 5; // Characters per chunk - const delay = 10; // Milliseconds between chunks - - for (let i = 0; i < prompt.length; i += chunkSize) { - const chunk = prompt.slice(i, i + chunkSize); - getChatGroupStoreState().appendStreamingSystemPrompt(chunk); - - // Small delay for typewriter effect - if (i + chunkSize < prompt.length) { - // eslint-disable-next-line no-promise-executor-return - await new Promise((resolve) => setTimeout(resolve, delay)); - } - } - - // Finish streaming - EditorCanvas will handle save when streaming ends - await getChatGroupStoreState().finishStreamingSystemPrompt(); + await state.updateGroup(group.id, { content: prompt }); } } diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/BatchCreateAgents/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Inspector/BatchCreateAgents/index.tsx new file mode 100644 index 0000000000..2985e04e22 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/BatchCreateAgents/index.tsx @@ -0,0 +1,110 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { Avatar, Flexbox } from '@lobehub/ui'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { Check } from 'lucide-react'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { shinyTextStyles } from '@/styles'; + +import type { BatchCreateAgentsParams, BatchCreateAgentsState } from '../../../types'; + +const styles = createStaticStyles(({ css, cssVar: cv }) => ({ + avatarGroup: css` + display: flex; + gap: 2px; + align-items: center; + `, + count: css` + color: ${cv.colorTextSecondary}; + font-size: 12px; + `, + root: css` + overflow: hidden; + display: flex; + gap: 8px; + align-items: center; + `, + statusIcon: css` + flex-shrink: 0; + margin-block-end: -2px; + `, + title: css` + flex-shrink: 0; + color: ${cv.colorTextSecondary}; + white-space: nowrap; + `, +})); + +export const BatchCreateAgentsInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const agents = args?.agents || partialArgs?.agents; + + // Get display info from agents + const displayInfo = useMemo(() => { + if (!agents || agents.length === 0) return null; + + const count = agents.length; + const displayAgents = agents.slice(0, 3); // Show up to 3 avatars + + return { count, displayAgents }; + }, [agents]); + + // Initial streaming state + if (isArgumentsStreaming && !displayInfo) { + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.batchCreateAgents')} +
+ ); + } + + const isSuccess = pluginState?.successCount === pluginState?.agents?.length; + const successCount = pluginState?.successCount ?? 0; + const totalCount = displayInfo?.count ?? 0; + + return ( + + + {t('builtins.lobe-group-agent-builder.apiName.batchCreateAgents')}: + + {displayInfo && ( + <> +
+ {displayInfo.displayAgents.map((agent, index) => ( + + ))} +
+ + {pluginState + ? `${successCount}/${totalCount}` + : `${totalCount} ${t('builtins.lobe-group-agent-builder.inspector.agents')}`} + + + )} + {!isLoading && isSuccess && ( + + )} +
+ ); +}); + +BatchCreateAgentsInspector.displayName = 'BatchCreateAgentsInspector'; + +export default BatchCreateAgentsInspector; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/CreateAgent/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Inspector/CreateAgent/index.tsx new file mode 100644 index 0000000000..0d7faf4b2c --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/CreateAgent/index.tsx @@ -0,0 +1,72 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { Avatar, Flexbox } from '@lobehub/ui'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { Check } from 'lucide-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { shinyTextStyles } from '@/styles'; + +import type { CreateAgentParams, CreateAgentState } from '../../../types'; + +const styles = createStaticStyles(({ css, cssVar: cv }) => ({ + root: css` + overflow: hidden; + display: flex; + gap: 8px; + align-items: center; + `, + statusIcon: css` + flex-shrink: 0; + margin-block-end: -2px; + `, + title: css` + flex-shrink: 0; + color: ${cv.colorTextSecondary}; + white-space: nowrap; + `, +})); + +export const CreateAgentInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const title = args?.title || partialArgs?.title; + const avatar = args?.avatar || partialArgs?.avatar; + + // Initial streaming state + if (isArgumentsStreaming && !title) { + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.createAgent')} +
+ ); + } + + const isSuccess = pluginState?.success; + + return ( + + + {t('builtins.lobe-group-agent-builder.apiName.createAgent')}: + + {avatar && } + {title && {title}} + {!isLoading && isSuccess && ( + + )} + + ); +}); + +CreateAgentInspector.displayName = 'CreateAgentInspector'; + +export default CreateAgentInspector; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/InviteAgent/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Inspector/InviteAgent/index.tsx new file mode 100644 index 0000000000..a4db77708e --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/InviteAgent/index.tsx @@ -0,0 +1,57 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { Check } from 'lucide-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { InviteAgentParams, InviteAgentState } from '../../../types'; + +const styles = createStaticStyles(({ css }) => ({ + statusIcon: css` + margin-block-end: -2px; + margin-inline-start: 4px; + `, +})); + +export const InviteAgentInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const agentId = args?.agentId || partialArgs?.agentId; + const displayName = pluginState?.agentName || agentId; + + // Initial streaming state + if (isArgumentsStreaming && !agentId) { + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.inviteAgent')} +
+ ); + } + + const isSuccess = pluginState?.success; + + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.inviteAgent')}: + {displayName && {displayName}} + {!isLoading && isSuccess && ( + + )} +
+ ); +}); + +InviteAgentInspector.displayName = 'InviteAgentInspector'; + +export default InviteAgentInspector; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/RemoveAgent/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Inspector/RemoveAgent/index.tsx new file mode 100644 index 0000000000..bdfc6547dc --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/RemoveAgent/index.tsx @@ -0,0 +1,57 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { Check } from 'lucide-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { RemoveAgentParams, RemoveAgentState } from '../../../types'; + +const styles = createStaticStyles(({ css }) => ({ + statusIcon: css` + margin-block-end: -2px; + margin-inline-start: 4px; + `, +})); + +export const RemoveAgentInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const agentId = args?.agentId || partialArgs?.agentId; + const displayName = pluginState?.agentName || agentId; + + // Initial streaming state + if (isArgumentsStreaming && !agentId) { + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.removeAgent')} +
+ ); + } + + const isSuccess = pluginState?.success; + + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.removeAgent')}: + {displayName && {displayName}} + {!isLoading && isSuccess && ( + + )} +
+ ); +}); + +RemoveAgentInspector.displayName = 'RemoveAgentInspector'; + +export default RemoveAgentInspector; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/SearchAgent/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Inspector/SearchAgent/index.tsx new file mode 100644 index 0000000000..18b6455246 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/SearchAgent/index.tsx @@ -0,0 +1,66 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { Text } from '@lobehub/ui'; +import { cssVar, cx } from 'antd-style'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { SearchAgentParams, SearchAgentState } from '../../../types'; + +export const SearchAgentInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const query = args?.query || partialArgs?.query; + + // Initial streaming state + if (isArgumentsStreaming && !query) { + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.searchAgent')} +
+ ); + } + + const resultCount = pluginState?.total ?? pluginState?.agents?.length ?? 0; + const hasResults = resultCount > 0; + + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.searchAgent')} + {query && ( + <> + : {query} + + )} + {!isLoading && + !isArgumentsStreaming && + pluginState?.agents && + (hasResults ? ( + ({resultCount}) + ) : ( + + ({t('builtins.lobe-group-agent-builder.inspector.noResults')}) + + ))} +
+ ); +}); + +SearchAgentInspector.displayName = 'SearchAgentInspector'; + +export default SearchAgentInspector; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateAgentPrompt/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateAgentPrompt/index.tsx new file mode 100644 index 0000000000..12e830201f --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateAgentPrompt/index.tsx @@ -0,0 +1,120 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { Avatar, Flexbox, Text } from '@lobehub/ui'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useAgentGroupStore } from '@/store/agentGroup'; +import { agentGroupSelectors } from '@/store/agentGroup/selectors'; +import { inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { UpdateAgentPromptParams, UpdateAgentPromptState } from '../../../types'; + +const styles = createStaticStyles(({ css, cssVar: cv }) => ({ + agentName: css` + overflow: hidden; + + max-width: 120px; + + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + `, + label: css` + flex-shrink: 0; + color: ${cv.colorTextSecondary}; + white-space: nowrap; + `, + root: css` + overflow: hidden; + display: flex; + gap: 6px; + align-items: center; + `, +})); + +export const UpdateAgentPromptInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const agentId = args?.agentId || partialArgs?.agentId; + const prompt = args?.prompt || partialArgs?.prompt; + + // Get agent info from the current group + const agent = useAgentGroupStore((s) => { + const agents = agentGroupSelectors.currentGroupAgents(s); + return agents.find((a) => a.id === agentId); + }); + + // Calculate length difference + const lengthDiff = useMemo(() => { + if (!pluginState) return null; + + const newLength = pluginState.newPrompt?.length ?? 0; + const prevLength = pluginState.previousPrompt?.length ?? 0; + return newLength - prevLength; + }, [pluginState]); + + // Initial streaming state + if (isArgumentsStreaming && !agentId) { + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.updateAgentPrompt')} +
+ ); + } + + const streamingLength = prompt?.length ?? 0; + + const isSupervisor = agent?.isSupervisor ?? false; + + // Use different i18n key for supervisor + const labelKey = isSupervisor + ? 'builtins.lobe-group-agent-builder.apiName.updateSupervisorPrompt' + : 'builtins.lobe-group-agent-builder.apiName.updateAgentPrompt'; + + return ( + + {t(labelKey)} + {/* Only show avatar and title for non-supervisor agents */} + {agent && !isSupervisor && ( + <> + + {agent.title} + + )} + {/* Show length diff when completed */} + {!isLoading && !isArgumentsStreaming && lengthDiff !== null && ( + = 0 ? cssVar.colorSuccess : cssVar.colorError} + fontSize={12} + > + {lengthDiff >= 0 ? '+' : ''} + {lengthDiff} + {t('builtins.lobe-agent-builder.inspector.chars')} + + )} + {/* Show streaming length */} + {(isArgumentsStreaming || isLoading) && streamingLength > 0 && ( + + ({streamingLength} + {t('builtins.lobe-agent-builder.inspector.chars')}) + + )} + + ); +}); + +UpdateAgentPromptInspector.displayName = 'UpdateAgentPromptInspector'; + +export default UpdateAgentPromptInspector; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroup/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroup/index.tsx new file mode 100644 index 0000000000..e1baf8d91c --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroup/index.tsx @@ -0,0 +1,87 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { Check } from 'lucide-react'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { UpdateGroupParams, UpdateGroupState } from '../../../types'; + +const styles = createStaticStyles(({ css }) => ({ + statusIcon: css` + margin-block-end: -2px; + margin-inline-start: 4px; + `, +})); + +export const UpdateGroupInspector = memo>( + ({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const config = args?.config || partialArgs?.config; + const meta = args?.meta || partialArgs?.meta; + + // Build display text from updated fields + const displayText = useMemo(() => { + const fields: string[] = []; + // Config fields + if (config?.openingMessage !== undefined) { + fields.push(t('builtins.lobe-group-agent-builder.inspector.openingMessage')); + } + if (config?.openingQuestions !== undefined) { + fields.push(t('builtins.lobe-group-agent-builder.inspector.openingQuestions')); + } + // Meta fields + if (meta?.title !== undefined) { + fields.push(t('builtins.lobe-group-agent-builder.inspector.title')); + } + if (meta?.description !== undefined) { + fields.push(t('builtins.lobe-group-agent-builder.inspector.description')); + } + if (meta?.avatar !== undefined) { + fields.push(t('builtins.lobe-group-agent-builder.inspector.avatar')); + } + if (meta?.backgroundColor !== undefined) { + fields.push(t('builtins.lobe-group-agent-builder.inspector.backgroundColor')); + } + return fields.length > 0 ? fields.join(', ') : ''; + }, [config, meta, t]); + + // Initial streaming state + if (isArgumentsStreaming && !displayText) { + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.updateGroup')} +
+ ); + } + + const isSuccess = pluginState?.success; + + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.updateGroup')} + {displayText && ( + <> + : {displayText} + + )} + {!isLoading && isSuccess && ( + + )} +
+ ); + }, +); + +UpdateGroupInspector.displayName = 'UpdateGroupInspector'; + +export default UpdateGroupInspector; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroupPrompt/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroupPrompt/index.tsx new file mode 100644 index 0000000000..460fb8a0eb --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroupPrompt/index.tsx @@ -0,0 +1,99 @@ +'use client'; + +import type { BuiltinInspectorProps } from '@lobechat/types'; +import { Flexbox, Text } from '@lobehub/ui'; +import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { inspectorTextStyles, shinyTextStyles } from '@/styles'; + +import type { UpdateGroupPromptParams, UpdateGroupPromptState } from '../../../types'; + +const styles = createStaticStyles(({ css, cssVar: cv }) => ({ + groupName: css` + overflow: hidden; + + max-width: 120px; + + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + `, + label: css` + flex-shrink: 0; + color: ${cv.colorTextSecondary}; + white-space: nowrap; + `, + root: css` + overflow: hidden; + display: flex; + gap: 6px; + align-items: center; + `, +})); + +export const UpdateGroupPromptInspector = memo< + BuiltinInspectorProps +>(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { + const { t } = useTranslation('plugin'); + + const prompt = args?.prompt || partialArgs?.prompt; + + // Calculate length difference + const lengthDiff = useMemo(() => { + if (!pluginState) return null; + + const newLength = pluginState.newPrompt?.length ?? 0; + const prevLength = pluginState.previousPrompt?.length ?? 0; + return newLength - prevLength; + }, [pluginState]); + + // Initial streaming state + if (isArgumentsStreaming && !prompt) { + return ( +
+ {t('builtins.lobe-group-agent-builder.apiName.updateGroupPrompt')} +
+ ); + } + + const streamingLength = prompt?.length ?? 0; + + return ( + + + {t('builtins.lobe-group-agent-builder.apiName.updateGroupPrompt')} + + {/* Show length diff when completed */} + {!isLoading && !isArgumentsStreaming && lengthDiff !== null && ( + = 0 ? cssVar.colorSuccess : cssVar.colorError} + fontSize={12} + > + {lengthDiff >= 0 ? '+' : ''} + {lengthDiff} + {t('builtins.lobe-agent-builder.inspector.chars')} + + )} + {/* Show streaming length */} + {(isArgumentsStreaming || isLoading) && streamingLength > 0 && ( + + ({streamingLength} + {t('builtins.lobe-agent-builder.inspector.chars')}) + + )} + + ); +}); + +UpdateGroupPromptInspector.displayName = 'UpdateGroupPromptInspector'; + +export default UpdateGroupPromptInspector; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Inspector/index.ts b/packages/builtin-tool-group-agent-builder/src/client/Inspector/index.ts new file mode 100644 index 0000000000..c5488e74e5 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Inspector/index.ts @@ -0,0 +1,52 @@ +// Import shared inspectors from agent-builder +import { + GetAvailableModelsInspector, + InstallPluginInspector, + SearchMarketToolsInspector, + UpdateConfigInspector, +} from '@lobechat/builtin-tool-agent-builder/client'; +import { type BuiltinInspector } from '@lobechat/types'; + +import { GroupAgentBuilderApiName } from '../../types'; +import { BatchCreateAgentsInspector } from './BatchCreateAgents'; +import { CreateAgentInspector } from './CreateAgent'; +import { InviteAgentInspector } from './InviteAgent'; +import { RemoveAgentInspector } from './RemoveAgent'; +import { SearchAgentInspector } from './SearchAgent'; +import { UpdateAgentPromptInspector } from './UpdateAgentPrompt'; +import { UpdateGroupInspector } from './UpdateGroup'; +import { UpdateGroupPromptInspector } from './UpdateGroupPrompt'; + +/** + * Group Agent Builder Inspector Components Registry + * + * Inspector components customize the title/header area + * of tool calls in the conversation UI. + */ +export const GroupAgentBuilderInspectors: Record = { + // Group-specific inspectors + [GroupAgentBuilderApiName.batchCreateAgents]: BatchCreateAgentsInspector as BuiltinInspector, + [GroupAgentBuilderApiName.createAgent]: CreateAgentInspector as BuiltinInspector, + [GroupAgentBuilderApiName.inviteAgent]: InviteAgentInspector as BuiltinInspector, + [GroupAgentBuilderApiName.removeAgent]: RemoveAgentInspector as BuiltinInspector, + [GroupAgentBuilderApiName.searchAgent]: SearchAgentInspector as BuiltinInspector, + [GroupAgentBuilderApiName.updateAgentPrompt]: UpdateAgentPromptInspector as BuiltinInspector, + [GroupAgentBuilderApiName.updateGroup]: UpdateGroupInspector as BuiltinInspector, + [GroupAgentBuilderApiName.updateGroupPrompt]: UpdateGroupPromptInspector as BuiltinInspector, + + // Shared inspectors from agent-builder (reused for group context) + [GroupAgentBuilderApiName.getAvailableModels]: GetAvailableModelsInspector as BuiltinInspector, + [GroupAgentBuilderApiName.installPlugin]: InstallPluginInspector as BuiltinInspector, + [GroupAgentBuilderApiName.searchMarketTools]: SearchMarketToolsInspector as BuiltinInspector, + [GroupAgentBuilderApiName.updateAgentConfig]: UpdateConfigInspector as BuiltinInspector, +}; + +// Re-export individual inspectors +export { BatchCreateAgentsInspector } from './BatchCreateAgents'; +export { CreateAgentInspector } from './CreateAgent'; +export { InviteAgentInspector } from './InviteAgent'; +export { RemoveAgentInspector } from './RemoveAgent'; +export { SearchAgentInspector } from './SearchAgent'; +export { UpdateAgentPromptInspector } from './UpdateAgentPrompt'; +export { UpdateGroupInspector } from './UpdateGroup'; +export { UpdateGroupPromptInspector } from './UpdateGroupPrompt'; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Render/BatchCreateAgents.tsx b/packages/builtin-tool-group-agent-builder/src/client/Render/BatchCreateAgents.tsx new file mode 100644 index 0000000000..28ce7b52cb --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Render/BatchCreateAgents.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { BuiltinRenderProps } from '@lobechat/types'; +import { Avatar, Flexbox } from '@lobehub/ui'; +import { createStaticStyles } from 'antd-style'; +import { Users } from 'lucide-react'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import type { BatchCreateAgentsParams, BatchCreateAgentsState } from '../../types'; + +const styles = createStaticStyles(({ css, cssVar }) => ({ + container: css` + padding: 4px 16px; + background: ${cssVar.colorFillQuaternary}; + border-radius: 8px; + `, + description: css` + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + + font-size: 12px; + line-height: 1.5; + color: ${cssVar.colorTextDescription}; + text-overflow: ellipsis; + `, + empty: css` + padding: 16px; + color: ${cssVar.colorTextTertiary}; + `, + item: css` + padding-block: 12px; + + &:not(:last-child) { + border-block-end: 1px solid ${cssVar.colorBorderSecondary}; + } + `, + title: css` + overflow: hidden; + + font-size: 13px; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + `, +})); + +interface AgentItemProps { + agent: { + agentId: string; + success: boolean; + title: string; + }; + definition?: { + avatar?: string; + description?: string; + title: string; + }; +} + +const AgentItem = memo(({ agent, definition }) => { + const avatar = definition?.avatar; + const description = definition?.description; + + return ( + + + + {agent.title} + {description && {description}} + + + ); +}); + +const BatchCreateAgentsRender = memo< + BuiltinRenderProps +>(({ args, pluginState }) => { + const { t } = useTranslation('plugin'); + const { agents: resultAgents } = pluginState || {}; + const definitions = args?.agents || []; + + if (!resultAgents || resultAgents.length === 0) { + return ( + + + {t('builtins.lobe-group-agent-builder.inspector.noResults')} + + ); + } + + return ( + + {resultAgents.map((agent, index) => ( + + ))} + + ); +}); + +export default BatchCreateAgentsRender; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Render/UpdateAgentPrompt/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Render/UpdateAgentPrompt/index.tsx new file mode 100644 index 0000000000..e39e39651e --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Render/UpdateAgentPrompt/index.tsx @@ -0,0 +1,36 @@ +'use client'; + +import type { BuiltinRenderProps } from '@lobechat/types'; +import { Markdown } from '@lobehub/ui'; +import { createStaticStyles } from 'antd-style'; +import { memo } from 'react'; + +import type { UpdateAgentPromptParams, UpdateAgentPromptState } from '../../../types'; + +const styles = createStaticStyles(({ css, cssVar }) => ({ + container: css` + padding: 12px; + border-radius: 8px; + background: ${cssVar.colorFillQuaternary}; + `, +})); + +export const UpdateAgentPromptRender = memo< + BuiltinRenderProps +>(({ pluginState }) => { + const prompt = pluginState?.newPrompt; + + if (!prompt) return null; + + return ( +
+
+ {prompt} +
+
+ ); +}); + +UpdateAgentPromptRender.displayName = 'UpdateAgentPromptRender'; + +export default UpdateAgentPromptRender; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Render/UpdateGroupPrompt/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Render/UpdateGroupPrompt/index.tsx new file mode 100644 index 0000000000..6006478495 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Render/UpdateGroupPrompt/index.tsx @@ -0,0 +1,36 @@ +'use client'; + +import type { BuiltinRenderProps } from '@lobechat/types'; +import { Markdown } from '@lobehub/ui'; +import { createStaticStyles } from 'antd-style'; +import { memo } from 'react'; + +import type { UpdateGroupPromptParams, UpdateGroupPromptState } from '../../../types'; + +const styles = createStaticStyles(({ css, cssVar }) => ({ + container: css` + padding: 12px; + border-radius: 8px; + background: ${cssVar.colorFillQuaternary}; + `, +})); + +export const UpdateGroupPromptRender = memo< + BuiltinRenderProps +>(({ pluginState }) => { + const prompt = pluginState?.newPrompt; + + if (!prompt) return null; + + return ( +
+
+ {prompt} +
+
+ ); +}); + +UpdateGroupPromptRender.displayName = 'UpdateGroupPromptRender'; + +export default UpdateGroupPromptRender; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Render/index.ts b/packages/builtin-tool-group-agent-builder/src/client/Render/index.ts new file mode 100644 index 0000000000..3f8f7d00ab --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Render/index.ts @@ -0,0 +1,16 @@ +import { GroupAgentBuilderApiName } from '../../types'; +import BatchCreateAgents from './BatchCreateAgents'; +import UpdateAgentPrompt from './UpdateAgentPrompt'; +import UpdateGroupPrompt from './UpdateGroupPrompt'; + +/** + * Group Agent Builder Render Components Registry + * + * Render components display the results of tool calls + * in a user-friendly format. + */ +export const GroupAgentBuilderRenders = { + [GroupAgentBuilderApiName.batchCreateAgents]: BatchCreateAgents, + [GroupAgentBuilderApiName.updateAgentPrompt]: UpdateAgentPrompt, + [GroupAgentBuilderApiName.updateGroupPrompt]: UpdateGroupPrompt, +}; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Streaming/BatchCreateAgents/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Streaming/BatchCreateAgents/index.tsx new file mode 100644 index 0000000000..9dde1ed271 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Streaming/BatchCreateAgents/index.tsx @@ -0,0 +1,88 @@ +'use client'; + +import type { BuiltinStreamingProps } from '@lobechat/types'; +import { Avatar, Block, Flexbox, Markdown } from '@lobehub/ui'; +import { createStaticStyles } from 'antd-style'; +import { memo } from 'react'; + +import type { BatchCreateAgentsParams } from '../../../types'; + +const styles = createStaticStyles(({ css, cssVar }) => ({ + description: css` + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + + font-size: 12px; + line-height: 1.5; + color: ${cssVar.colorTextDescription}; + text-overflow: ellipsis; + `, + index: css` + flex-shrink: 0; + font-size: 12px; + color: ${cssVar.colorTextQuaternary}; + `, + item: css` + padding-block: 10px; + padding-inline: 12px; + + &:not(:last-child) { + border-block-end: 1px dashed ${cssVar.colorBorderSecondary}; + } + `, + systemRole: css` + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + + font-size: 12px; + line-height: 1.5; + color: ${cssVar.colorTextTertiary}; + text-overflow: ellipsis; + `, + title: css` + overflow: hidden; + + font-size: 13px; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + `, +})); + +export const BatchCreateAgentsStreaming = memo>( + ({ args }) => { + const { agents } = args || {}; + + if (!agents || agents.length === 0) return null; + + return ( + + {agents.map((agent, index) => ( + +
{index + 1}.
+ + + {agent.title} + {agent.description && {agent.description}} + {agent.systemRole && ( +
+ + {agent.systemRole} + +
+ )} +
+
+ ))} +
+ ); + }, +); + +BatchCreateAgentsStreaming.displayName = 'BatchCreateAgentsStreaming'; + +export default BatchCreateAgentsStreaming; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateAgentPrompt/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateAgentPrompt/index.tsx new file mode 100644 index 0000000000..9de9c5b53c --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateAgentPrompt/index.tsx @@ -0,0 +1,37 @@ +'use client'; + +import type { BuiltinStreamingProps } from '@lobechat/types'; +import { Block, Markdown } from '@lobehub/ui'; +import { memo, useEffect } from 'react'; + +import { useGroupProfileStore } from '@/store/groupProfile'; + +import type { UpdateAgentPromptParams } from '../../../types'; + +export const UpdateAgentPromptStreaming = memo>( + ({ args }) => { + const { agentId, prompt } = args || {}; + const setActiveTabId = useGroupProfileStore((s) => s.setActiveTabId); + + // Switch to agent tab when streaming agent prompt + useEffect(() => { + if (agentId) { + setActiveTabId(agentId); + } + }, [agentId, setActiveTabId]); + + if (!prompt) return null; + + return ( + + + {prompt} + + + ); + }, +); + +UpdateAgentPromptStreaming.displayName = 'UpdateAgentPromptStreaming'; + +export default UpdateAgentPromptStreaming; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateGroupPrompt/index.tsx b/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateGroupPrompt/index.tsx new file mode 100644 index 0000000000..e9a4a9b516 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateGroupPrompt/index.tsx @@ -0,0 +1,35 @@ +'use client'; + +import type { BuiltinStreamingProps } from '@lobechat/types'; +import { Block, Markdown } from '@lobehub/ui'; +import { memo, useEffect } from 'react'; + +import { useGroupProfileStore } from '@/store/groupProfile'; + +import type { UpdateGroupPromptParams } from '../../../types'; + +export const UpdateGroupPromptStreaming = memo>( + ({ args }) => { + const { prompt } = args || {}; + const setActiveTabId = useGroupProfileStore((s) => s.setActiveTabId); + + // Switch to group tab when streaming group prompt + useEffect(() => { + setActiveTabId('group'); + }, []); + + if (!prompt) return null; + + return ( + + + {prompt} + + + ); + }, +); + +UpdateGroupPromptStreaming.displayName = 'UpdateGroupPromptStreaming'; + +export default UpdateGroupPromptStreaming; diff --git a/packages/builtin-tool-group-agent-builder/src/client/Streaming/index.ts b/packages/builtin-tool-group-agent-builder/src/client/Streaming/index.ts new file mode 100644 index 0000000000..0c34e1f979 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/Streaming/index.ts @@ -0,0 +1,22 @@ +import type { BuiltinStreaming } from '@lobechat/types'; + +import { GroupAgentBuilderApiName } from '../../types'; +import { BatchCreateAgentsStreaming } from './BatchCreateAgents'; +import { UpdateAgentPromptStreaming } from './UpdateAgentPrompt'; +import { UpdateGroupPromptStreaming } from './UpdateGroupPrompt'; + +/** + * Group Agent Builder Streaming Components Registry + * + * Streaming components render tool calls while they are + * still executing, allowing real-time feedback to users. + */ +export const GroupAgentBuilderStreamings: Record = { + [GroupAgentBuilderApiName.batchCreateAgents]: BatchCreateAgentsStreaming as BuiltinStreaming, + [GroupAgentBuilderApiName.updateAgentPrompt]: UpdateAgentPromptStreaming as BuiltinStreaming, + [GroupAgentBuilderApiName.updateGroupPrompt]: UpdateGroupPromptStreaming as BuiltinStreaming, +}; + +export { BatchCreateAgentsStreaming } from './BatchCreateAgents'; +export { UpdateAgentPromptStreaming } from './UpdateAgentPrompt'; +export { UpdateGroupPromptStreaming } from './UpdateGroupPrompt'; diff --git a/packages/builtin-tool-group-agent-builder/src/client/index.ts b/packages/builtin-tool-group-agent-builder/src/client/index.ts new file mode 100644 index 0000000000..5e87d07271 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/client/index.ts @@ -0,0 +1,26 @@ +// Inspector components (customized tool call headers) +export { GroupAgentBuilderInspectors } from './Inspector'; +export { + BatchCreateAgentsInspector, + CreateAgentInspector, + InviteAgentInspector, + RemoveAgentInspector, + SearchAgentInspector, + UpdateAgentPromptInspector, + UpdateGroupInspector, + UpdateGroupPromptInspector, +} from './Inspector'; + +// Render components (read-only result display) +export { GroupAgentBuilderRenders } from './Render'; + +// Streaming components (real-time tool execution feedback) +export { + BatchCreateAgentsStreaming, + GroupAgentBuilderStreamings, + UpdateGroupPromptStreaming, +} from './Streaming'; + +// Re-export types and manifest for convenience +export { GroupAgentBuilderManifest } from '../manifest'; +export * from '../types'; diff --git a/packages/builtin-tool-group-agent-builder/src/executor.ts b/packages/builtin-tool-group-agent-builder/src/executor.ts new file mode 100644 index 0000000000..49be4b0ce4 --- /dev/null +++ b/packages/builtin-tool-group-agent-builder/src/executor.ts @@ -0,0 +1,284 @@ +/** + * Group Agent Builder Executor + * + * Handles all group agent builder tool calls for configuring groups and their agents. + * Extends AgentBuilder functionality with group-specific operations. + */ +import type { + GetAvailableModelsParams, + InstallPluginParams, + SearchMarketToolsParams, +} from '@lobechat/builtin-tool-agent-builder'; +import { AgentBuilderExecutionRuntime } from '@lobechat/builtin-tool-agent-builder/executionRuntime'; +import { BaseExecutor, type BuiltinToolContext, type BuiltinToolResult } from '@lobechat/types'; + +import { GroupAgentBuilderExecutionRuntime } from './ExecutionRuntime'; +import { + type BatchCreateAgentsParams, + type CreateAgentParams, + GroupAgentBuilderApiName, + GroupAgentBuilderIdentifier, + type InviteAgentParams, + type RemoveAgentParams, + type SearchAgentParams, + type UpdateAgentConfigWithIdParams, + type UpdateAgentPromptParams, + type UpdateGroupParams, + type UpdateGroupPromptParams, +} from './types'; + +const agentBuilderRuntime = new AgentBuilderExecutionRuntime(); +const groupAgentBuilderRuntime = new GroupAgentBuilderExecutionRuntime(); + +class GroupAgentBuilderExecutor extends BaseExecutor { + readonly identifier = GroupAgentBuilderIdentifier; + protected readonly apiEnum = GroupAgentBuilderApiName; + + // ==================== Group Member Management ==================== + + searchAgent = async (params: SearchAgentParams): Promise => { + const result = await groupAgentBuilderRuntime.searchAgent(params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + createAgent = async ( + params: CreateAgentParams, + ctx: BuiltinToolContext, + ): Promise => { + const groupId = ctx.groupId; + + if (!groupId) { + return { + content: 'No active group found', + error: { message: 'No active group found', type: 'NoGroupContext' }, + success: false, + }; + } + + const result = await groupAgentBuilderRuntime.createAgent(groupId, params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + batchCreateAgents = async ( + params: BatchCreateAgentsParams, + ctx: BuiltinToolContext, + ): Promise => { + const groupId = ctx.groupId; + + if (!groupId) { + return { + content: 'No active group found', + error: { message: 'No active group found', type: 'NoGroupContext' }, + success: false, + }; + } + + const result = await groupAgentBuilderRuntime.batchCreateAgents(groupId, params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + inviteAgent = async ( + params: InviteAgentParams, + ctx: BuiltinToolContext, + ): Promise => { + const groupId = ctx.groupId; + + if (!groupId) { + return { + content: 'No active group found', + error: { message: 'No active group found', type: 'NoGroupContext' }, + success: false, + }; + } + + const result = await groupAgentBuilderRuntime.inviteAgent(groupId, params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + removeAgent = async ( + params: RemoveAgentParams, + ctx: BuiltinToolContext, + ): Promise => { + const groupId = ctx.groupId; + + if (!groupId) { + return { + content: 'No active group found', + error: { message: 'No active group found', type: 'NoGroupContext' }, + success: false, + }; + } + + const result = await groupAgentBuilderRuntime.removeAgent(groupId, params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + // ==================== Group Configuration ==================== + + updateAgentPrompt = async ( + params: UpdateAgentPromptParams, + ctx: BuiltinToolContext, + ): Promise => { + const groupId = ctx.groupId; + + if (!groupId) { + return { + content: 'No active group found', + error: { message: 'No active group found', type: 'NoGroupContext' }, + success: false, + }; + } + + const result = await groupAgentBuilderRuntime.updateAgentPrompt(groupId, params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + updateGroup = async (params: UpdateGroupParams): Promise => { + const result = await groupAgentBuilderRuntime.updateGroup(params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + updateGroupPrompt = async (params: UpdateGroupPromptParams): Promise => { + const result = await groupAgentBuilderRuntime.updateGroupPrompt({ + streaming: true, + ...params, + }); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + // ==================== Inherited Operations (for supervisor agent) ==================== + + getAvailableModels = async (params: GetAvailableModelsParams): Promise => { + const result = await agentBuilderRuntime.getAvailableModels(params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + searchMarketTools = async (params: SearchMarketToolsParams): Promise => { + const result = await agentBuilderRuntime.searchMarketTools(params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + updateConfig = async ( + params: UpdateAgentConfigWithIdParams, + ctx: BuiltinToolContext, + ): Promise => { + // Use provided agentId or fall back to supervisor agent from context + const { agentId: paramAgentId, ...restParams } = params; + const agentId = paramAgentId ?? ctx.agentId; + + if (!agentId) { + return { + content: 'No agent found. Please provide an agentId or ensure supervisor context is available.', + error: { message: 'No agent found', type: 'NoAgentContext' }, + success: false, + }; + } + + const result = await agentBuilderRuntime.updateAgentConfig(agentId, restParams); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; + + installPlugin = async ( + params: InstallPluginParams, + ctx: BuiltinToolContext, + ): Promise => { + const agentId = ctx.agentId; + + if (!agentId) { + return { + content: 'No supervisor agent found', + error: { message: 'No supervisor agent found', type: 'NoAgentContext' }, + success: false, + }; + } + + const result = await agentBuilderRuntime.installPlugin(agentId, params); + return { + content: result.content, + error: result.error + ? { body: result.error, message: String(result.error), type: 'RuntimeError' } + : undefined, + state: result.state, + success: result.success, + }; + }; +} + +export const groupAgentBuilderExecutor = new GroupAgentBuilderExecutor(); diff --git a/packages/builtin-tool-group-agent-builder/src/index.ts b/packages/builtin-tool-group-agent-builder/src/index.ts index 8ded2146b6..31124147ca 100644 --- a/packages/builtin-tool-group-agent-builder/src/index.ts +++ b/packages/builtin-tool-group-agent-builder/src/index.ts @@ -1,16 +1,3 @@ export { GroupAgentBuilderManifest } from './manifest'; export { systemPrompt } from './systemRole'; -export { - GroupAgentBuilderApiName, - type GroupAgentBuilderApiNameType, - GroupAgentBuilderIdentifier, - type InviteAgentParams, - type InviteAgentState, - type RemoveAgentParams, - type RemoveAgentState, - type UpdateGroupConfigParams, - type UpdateGroupConfigState, - type UpdateGroupMetaParams, - type UpdateGroupPromptParams, - type UpdateGroupPromptState, -} from './types'; +export * from './types'; diff --git a/packages/builtin-tool-group-agent-builder/src/manifest.ts b/packages/builtin-tool-group-agent-builder/src/manifest.ts index 05bca5c937..739dd10b56 100644 --- a/packages/builtin-tool-group-agent-builder/src/manifest.ts +++ b/packages/builtin-tool-group-agent-builder/src/manifest.ts @@ -6,6 +6,111 @@ import { GroupAgentBuilderApiName, GroupAgentBuilderIdentifier } from './types'; export const GroupAgentBuilderManifest: BuiltinToolManifest = { api: [ // ==================== Group Member Management ==================== + { + description: + "Search for agents that can be invited to the group. Returns agents from the user's collection. Use this to find suitable agents before inviting them.", + name: GroupAgentBuilderApiName.searchAgent, + parameters: { + properties: { + limit: { + default: 10, + description: 'Maximum number of results to return (default: 10, max: 20).', + maximum: 20, + minimum: 1, + type: 'number', + }, + query: { + description: + 'Search query to find agents by name, description, or capabilities. Leave empty to browse all available agents.', + type: 'string', + }, + }, + required: [], + type: 'object', + }, + }, + { + description: + 'Create a new agent dynamically based on user requirements and add it to the group. Use this when no existing agent matches the needed expertise.', + humanIntervention: 'required', + name: GroupAgentBuilderApiName.createAgent, + parameters: { + properties: { + avatar: { + description: "An emoji or image URL for the agent's avatar (optional).", + type: 'string', + }, + description: { + description: 'A brief description of what this agent does and its expertise.', + type: 'string', + }, + systemRole: { + description: + "The system prompt that defines the agent's behavior, personality, and capabilities.", + type: 'string', + }, + title: { + description: 'The display name for the new agent.', + type: 'string', + }, + tools: { + description: + 'Array of tool identifiers to enable for this agent. Use identifiers from official_tools context (e.g., "lobe-cloud-sandbox", "web-crawler").', + items: { type: 'string' }, + type: 'array', + }, + }, + required: ['title', 'systemRole'], + type: 'object', + }, + }, + { + description: + 'Create multiple agents at once and add them to the group. Use this to efficiently set up a team of agents with different expertise.', + humanIntervention: 'required', + name: GroupAgentBuilderApiName.batchCreateAgents, + parameters: { + properties: { + agents: { + description: 'Array of agent definitions to create', + items: { + properties: { + /* eslint-disable sort-keys-fix/sort-keys-fix */ + avatar: { + description: "An emoji or image URL for the agent's avatar (optional).", + type: 'string', + }, + title: { + description: 'The display name for the new agent.', + type: 'string', + }, + description: { + description: 'A brief description of what this agent does and its expertise.', + type: 'string', + }, + systemRole: { + description: + "The system prompt that defines the agent's behavior, personality, and capabilities.", + type: 'string', + }, + tools: { + description: + 'Array of tool identifiers to enable for this agent. Use identifiers from official_tools context (e.g., "lobe-cloud-sandbox", "web-crawler").', + items: { type: 'string' }, + type: 'array', + }, + /* eslint-enable sort-keys-fix/sort-keys-fix */ + }, + required: ['avatar', 'title', 'description', 'systemRole'], + type: 'object', + }, + type: 'array', + }, + }, + required: ['agents'], + type: 'object', + }, + }, { description: 'Invite an existing agent to join the group. The agent will become a member and participate in group conversations.', @@ -104,10 +209,14 @@ export const GroupAgentBuilderManifest: BuiltinToolManifest = { }, { description: - 'Update supervisor agent configuration (model, provider, plugins, etc.). Only include fields you want to update.', + 'Update agent configuration (model, provider, plugins, etc.). If agentId is not provided, updates the supervisor agent.', name: GroupAgentBuilderApiName.updateAgentConfig, parameters: { properties: { + agentId: { + description: 'The agent ID to update. If not provided, updates the supervisor agent.', + type: 'string', + }, config: { description: 'Partial agent configuration object. Only include fields you want to update.', @@ -139,7 +248,7 @@ export const GroupAgentBuilderManifest: BuiltinToolManifest = { type: 'object', }, togglePlugin: { - description: 'Toggle a specific plugin on/off for the supervisor agent.', + description: 'Toggle a specific plugin on/off for the agent.', properties: { enabled: { description: 'Whether to enable (true) or disable (false) the plugin.', @@ -159,29 +268,27 @@ export const GroupAgentBuilderManifest: BuiltinToolManifest = { }, }, { - description: - "Update the group's system prompt. This is the instruction that defines how agents in the group should collaborate and interact.", - name: GroupAgentBuilderApiName.updatePrompt, + description: "Update a specific agent's system prompt (systemRole).", + name: GroupAgentBuilderApiName.updateAgentPrompt, parameters: { properties: { - prompt: { - description: 'The new group system prompt content. Supports markdown formatting.', + agentId: { + description: 'The agent ID to update.', type: 'string', }, - streaming: { - description: - 'Whether to use streaming mode for typewriter effect in the editor. Defaults to true.', - type: 'boolean', + prompt: { + description: 'The new system prompt content. Supports markdown formatting.', + type: 'string', }, }, - required: ['prompt'], + required: ['agentId', 'prompt'], type: 'object', }, }, { description: - "Update the group's configuration including opening message and opening questions. Use this to set the welcome experience when users start a new conversation with the group.", - name: GroupAgentBuilderApiName.updateGroupConfig, + "Update the group's configuration and metadata. Use this to customize the group's appearance and welcome experience.", + name: GroupAgentBuilderApiName.updateGroup, parameters: { properties: { config: { @@ -202,8 +309,46 @@ export const GroupAgentBuilderManifest: BuiltinToolManifest = { }, type: 'object', }, + meta: { + description: 'Partial metadata object. Only include fields you want to update.', + properties: { + avatar: { + description: "An emoji or image URL for the group's avatar.", + type: 'string', + }, + backgroundColor: { + description: 'Background color for the group avatar (hex color code).', + type: 'string', + }, + description: { + description: 'A brief description of the group.', + type: 'string', + }, + title: { + description: 'The display name for the group.', + type: 'string', + }, + }, + type: 'object', + }, }, - required: ['config'], + required: [], + type: 'object', + }, + }, + { + description: + "Update the group's shared prompt/content. This content is shared with all group members and defines the group's goals, workflow, or other shared information.", + name: GroupAgentBuilderApiName.updateGroupPrompt, + parameters: { + properties: { + prompt: { + description: + "The new shared prompt/content for the group. Supports markdown formatting. This content will be visible to all group members and helps define the group's working context.", + type: 'string', + }, + }, + required: ['prompt'], type: 'object', }, }, diff --git a/packages/builtin-tool-group-agent-builder/src/systemRole.ts b/packages/builtin-tool-group-agent-builder/src/systemRole.ts index 27d428358d..d04d75add4 100644 --- a/packages/builtin-tool-group-agent-builder/src/systemRole.ts +++ b/packages/builtin-tool-group-agent-builder/src/systemRole.ts @@ -11,9 +11,9 @@ export const systemPrompt = `You are a Group Configuration Assistant integrated The injected context includes: - **group_meta**: title, description -- **group_config**: systemPrompt (group-level instruction), orchestratorModel, orchestratorProvider, responseOrder, responseSpeed +- **group_config**: systemPrompt (group-level shared content) - **group_members**: List of agents in the group with their names, avatars, and roles (including the supervisor agent) -- **supervisor_agent**: The supervisor agent's configuration (model, provider, plugins) +- **supervisor_agent**: The supervisor agent's configuration (model, provider, plugins, systemRole) - **official_tools**: List of available official tools including built-in tools and Klavis integrations You should use this context to understand the current state of the group and its members before making any modifications. @@ -23,7 +23,10 @@ You should use this context to understand the current state of the group and its You have access to tools that can modify group configurations: **Group Member Management:** +- **searchAgent**: Search for agents that can be invited to the group from the user's collection - **inviteAgent**: Invite an existing agent to join the group by their agent ID +- **createAgent**: Create a new agent dynamically and add it to the group. **IMPORTANT**: Always include appropriate tools based on the agent's role. +- **batchCreateAgents**: Create multiple agents at once and add them to the group. **IMPORTANT**: Each agent should have role-appropriate tools. - **removeAgent**: Remove an agent from the group (cannot remove the supervisor agent) **Read Operations:** @@ -31,37 +34,151 @@ You have access to tools that can modify group configurations: - **searchMarketTools**: Search for tools (MCP plugins) in the marketplace for the supervisor agent **Write Operations (for Group):** -- **updatePrompt**: Update the group's system prompt (the instruction that guides how agents collaborate) -- **updateGroupConfig**: Update group configuration including opening message and opening questions +- **updateGroupPrompt**: Update the group's shared prompt (content shared by ALL group members) +- **updateGroup**: Update group metadata and configuration including opening message and opening questions -**Write Operations (for Supervisor Agent):** -- **updateConfig**: Update supervisor agent configuration (model, provider, plugins, etc.) -- **togglePlugin**: Enable or disable a specific plugin for the supervisor agent +**Write Operations (for Agent):** +- **updateAgentPrompt**: Update any agent's system prompt (requires agentId). Can be used for both supervisor and member agents. +- **updateConfig**: Update agent configuration (model, provider, plugins, etc.). If agentId is not provided, updates the supervisor agent. - **installPlugin**: Install and enable a plugin for the supervisor agent + +**IMPORTANT: There are TWO types of prompts in a group:** + +1. **Group Prompt** (updated via \`updateGroupPrompt\`): + - Shared content that ALL group members (including supervisor and sub-agents) can access + - Contains background knowledge, project context, shared guidelines, or reference materials + - **DO NOT include member information** - the system automatically injects group member details into the context + - Think of this as a "shared document" or "knowledge base" for the entire group + +2. **Agent Prompt** (updated via \`updateAgentPrompt\` with any agent's agentId): + - The system role/instruction for a specific agent (can be supervisor OR any member agent) + - For **supervisor agent**: defines orchestration logic, delegation strategy, coordination behavior + - For **member agents**: defines their expertise, personality, response style, and capabilities + - Each agent's prompt is private to that agent, NOT shared with other agents + +**When to use which:** +- User wants to add shared context/knowledge → use \`updateGroupPrompt\` +- User wants to change how a specific agent behaves → use \`updateAgentPrompt\` with that agent's ID +- User mentions "group prompt", "shared content", "background info" → use \`updateGroupPrompt\` +- User mentions "agent behavior", "agent prompt", specific agent name → use \`updateAgentPrompt\` + + + +**CRITICAL: Auto-generate Supervisor Prompt After Member Changes** + +After ANY member change (createAgent, batchCreateAgents, inviteAgent, removeAgent), you MUST automatically update the supervisor's prompt. Use the following template structure: + +**Supervisor Prompt Template:** +\`\`\` +You are the Supervisor of this group, responsible for coordinating and orchestrating conversations among team members. + +## Orchestration Strategy + +1. **Task Analysis**: When receiving a user request, first analyze what type of expertise is needed. + +2. **Delegation Rules**: + {Generate specific rules based on the actual members, for example:} + - For coding/technical questions → delegate to [Developer Agent] + - For design/UI discussions → delegate to [Designer Agent] + - For general questions or coordination → handle yourself + +3. **Collaboration Patterns**: + - For complex tasks requiring multiple expertise → coordinate sequential or parallel involvement + - Summarize and synthesize responses from multiple agents when needed + +4. **Fallback Handling**: + - If no specific agent fits → handle the request yourself + - If clarification needed → ask the user before delegating + +## Response Guidelines + +- Always acknowledge which agent(s) will handle the request +- Provide context when delegating to help the agent understand the task +- Synthesize multi-agent responses into coherent answers for the user +\`\`\` + +**Generation Rules:** +1. Analyze each member's title, description, and systemRole to understand their expertise +2. Create specific delegation rules based on actual member capabilities +3. Identify potential collaboration scenarios between members +4. Keep the prompt concise but comprehensive +5. Use the same language as the user's conversation + + + +**CRITICAL: Assign Appropriate Tools When Creating Agents** + +When creating agents (via \`createAgent\` or \`batchCreateAgents\`), you MUST analyze the agent's role and assign relevant tools from the \`official_tools\` context. Agents without proper tools cannot perform their specialized tasks effectively. + +**Tool Assignment Strategy:** +1. **Analyze the agent's role**: What tasks will this agent perform? +2. **Match tools to capabilities**: Select tools that enable those tasks +3. **Include the tools array**: Always specify the \`tools\` parameter with appropriate tool identifiers + +**Common Tool Mappings (reference the actual \`official_tools\` context for available tools):** + +| Agent Role | Recommended Tools | Rationale | +|------------|-------------------|-----------| +| Researcher / Analyst | web-crawler, search tools | Need to gather and analyze information | +| Developer / Coder | lobe-cloud-sandbox, code execution tools | Need to write and run code | +| Data Scientist | lobe-cloud-sandbox, data analysis tools | Need computational environment | +| Writer / Editor | web-crawler (for research) | May need reference materials | +| Financial / Trading | relevant MCP integrations, sandbox | Need market data and calculations | +| Designer | image generation tools | Need to create visual assets | + +**Example - Quant Trading Team:** +- **Quant Researcher**: tools: ["web-crawler", "lobe-cloud-sandbox"] - for market research and data analysis +- **Execution Specialist**: tools: ["trading-mcp", "lobe-cloud-sandbox"] - for executing trades and backtesting +- **Risk Manager**: tools: ["lobe-cloud-sandbox"] - for risk calculations + +**Rules:** +1. NEVER create an agent without considering what tools it needs +2. Reference \`official_tools\` in the context to see available tool identifiers +3. If a specialized tool doesn't exist, note this limitation to the user +4. Tools enable agent capabilities - an agent without tools is limited to conversation only + + 1. **Understand the request**: Listen carefully to what the user wants to configure 2. **Reference injected context**: Use the \`\` to understand current state - no need to call read APIs -3. **Make targeted changes**: Use the appropriate API based on whether you're modifying the group or the supervisor agent -4. **Confirm changes**: Report what was changed and the new values +3. **Distinguish prompt types**: Determine if the user wants to modify shared content (group prompt) or a specific agent's behavior (agent prompt) +4. **Make targeted changes**: Use the appropriate API based on whether you're modifying the group or a specific agent +5. **Update supervisor prompt after member changes**: **IMPORTANT** - After ANY member change (create, invite, or remove agent), you MUST automatically update the supervisor's prompt using \`updateAgentPrompt\` with the supervisor's agentId. Generate an appropriate orchestration prompt based on the current members. +6. **Confirm changes**: Report what was changed and the new values 1. **Use injected context**: The current group's config and member list are already available. Reference them directly instead of calling read APIs. -2. **Distinguish group vs agent operations**: - - Group-level: updatePrompt (group systemPrompt), inviteAgent, removeAgent - - Supervisor agent-level: updateConfig, togglePlugin, installPlugin (for model, plugins, etc.) -3. **Explain your changes**: When modifying configurations, explain what you're changing and why it might benefit the group collaboration. -4. **Validate user intent**: For significant changes (like removing an agent), confirm with the user before proceeding. -5. **Provide recommendations**: When users ask for advice, consider how changes affect multi-agent collaboration. -6. **Use user's language**: Always respond in the same language the user is using. -7. **Cannot remove supervisor**: The supervisor agent cannot be removed from the group - it's the orchestrator. +2. **Distinguish group vs agent prompts**: + - Group prompt: Shared content for all members, NO member info needed (auto-injected) + - Agent prompt: Individual agent's system role (supervisor or member), requires agentId +3. **Distinguish group vs agent operations**: + - Group-level: updateGroupPrompt, updateGroup, inviteAgent, removeAgent, batchCreateAgents + - Agent-level: updateAgentPrompt (requires agentId), updateConfig (agentId optional, defaults to supervisor), installPlugin +4. **CRITICAL - Auto-update supervisor after member changes**: After ANY member change (create, invite, remove), you MUST automatically call \`updateAgentPrompt\` with supervisor's agentId to regenerate the orchestration prompt. This is NOT optional - the supervisor needs updated delegation rules to coordinate the team effectively. +5. **CRITICAL - Assign tools when creating agents**: When using \`createAgent\` or \`batchCreateAgents\`, ALWAYS include appropriate \`tools\` based on the agent's role. Reference \`official_tools\` in the context for available tool identifiers. An agent without proper tools cannot perform specialized tasks. +6. **Explain your changes**: When modifying configurations, explain what you're changing and why it might benefit the group collaboration. +7. **Validate user intent**: For significant changes (like removing an agent), confirm with the user before proceeding. +8. **Provide recommendations**: When users ask for advice, consider how changes affect multi-agent collaboration. +9. **Use user's language**: Always respond in the same language the user is using. +10. **Cannot remove supervisor**: The supervisor agent cannot be removed from the group - it's the orchestrator. +**Group Prompt (Shared Content):** +- Content that all group members can access and reference +- Suitable for: project background, domain knowledge, shared guidelines, reference materials +- NOT for: member lists (auto-injected), coordination rules (use agent prompt) + +**Agent Prompt (via updateAgentPrompt with agentId):** +- Updates any agent's system prompt - both supervisor and member agents +- **Supervisor agent**: defines orchestration logic, delegation strategy, coordination behavior +- **Member agents**: defines their expertise, personality, response style, and capabilities +- Each agent's prompt is private to that agent + **Group Configuration:** -- systemPrompt: The group-level instruction that defines how agents should collaborate and interact - orchestratorModel: The model used for orchestrating multi-agent conversations - orchestratorProvider: The provider for the orchestrator model - responseOrder: How agents respond ("sequential" or "natural") @@ -69,64 +186,133 @@ You have access to tools that can modify group configurations: - openingMessage: The welcome message shown when starting a new conversation with the group - openingQuestions: Suggested questions to help users get started with the group conversation -**Supervisor Agent Configuration:** -- model: The AI model for the supervisor agent +**Agent Configuration (via updateConfig):** +- model: The AI model for the agent - provider: The AI provider -- plugins: Tools enabled for the supervisor agent -- The supervisor orchestrates the conversation and coordinates other agents +- plugins: Tools enabled for the agent +- If agentId is not provided, updates the supervisor agent by default **Group Members:** - Each group has one supervisor agent and zero or more member agents - Member agents can be invited or removed - The supervisor agent cannot be removed (it's essential for group coordination) - -**System Prompt vs Agent Prompt:** -- Group systemPrompt: Defines collaboration rules for the entire group -- Agent systemRole: Individual agent's personality and expertise (not modified here) -User: "帮我邀请一个 Agent 到群组" -Action: Ask which agent they want to invite (need the agent ID), then use inviteAgent + +User: "Invite an agent to the group" +Action: +1. Use searchAgent to find available agents, show the results to user +2. Use inviteAgent with the selected agent ID +3. **Then automatically** use updateAgentPrompt with supervisor's agentId to update orchestration prompt with the newly invited agent's delegation rules + + +User: "Add a developer agent to help with coding" +Action: +1. Use searchAgent with query "developer" or "coding" to find relevant agents +2. Use inviteAgent or createAgent if no suitable agent exists. If creating, include tools: ["lobe-cloud-sandbox"] for code execution +3. **Then automatically** use updateAgentPrompt with supervisor's agentId to update orchestration prompt with the new developer agent's delegation rules + + + +User: "Create a marketing expert for this group" +Action: +1. Use createAgent with title "Marketing Expert", appropriate systemRole, description, and tools: ["web-crawler"] for research capabilities +2. **Then automatically** use updateAgentPrompt with supervisor's agentId to update orchestration prompt, adding delegation rules for marketing-related tasks + + + +User: "Create 3 expert agents for me" +Action: +1. Use batchCreateAgents to create multiple agents at once with their respective titles, systemRoles, descriptions, and **appropriate tools for each agent's role** +2. **Then automatically** use updateAgentPrompt with supervisor's agentId to generate orchestration prompt that includes delegation rules for all 3 new experts + + + +User: "Create a quant trading team" +Action: +1. Use batchCreateAgents with agents like: + - Quant Researcher: tools: ["web-crawler", "lobe-cloud-sandbox"] for market research and data analysis + - Execution Specialist: tools: ["lobe-cloud-sandbox"] for backtesting and trade simulation (note: if specific trading MCP is needed, check official_tools or recommend installing one) + - Risk Manager: tools: ["lobe-cloud-sandbox"] for risk calculations +2. **Then automatically** use updateAgentPrompt with supervisor's agentId to generate orchestration prompt with delegation rules for quant workflows + + + User: "Remove the coding assistant from the group" -Action: Check the group members in context, find the agent ID for "coding assistant", then use removeAgent +Action: +1. Check the group members in context, find the agent ID for "coding assistant" +2. Use removeAgent to remove the agent +3. **Then automatically** use updateAgentPrompt with supervisor's agentId to update orchestration prompt, removing the delegation rules for the removed agent + + User: "What agents are in this group?" Action: Reference the \`\` from the injected context and display the list + -User: "Change the group's system prompt to encourage more collaboration" -Action: Reference the current systemPrompt from context, then use updatePrompt to update it + +User: "Add some background information about our project to the group" +Action: Use updateGroupPrompt to add the project context as shared content for all members + -User: "帮我把主持人的模型改成 Claude" + +User: "Update the group's shared knowledge base" +Action: Use updateGroupPrompt - this is shared content, do NOT include member information (auto-injected) + + + +User: "Change how the supervisor coordinates the team" +Action: Use updateAgentPrompt with the supervisor's agentId to update orchestration logic + + + +User: "Make the supervisor more proactive in assigning tasks" +Action: Use updateAgentPrompt with supervisor's agentId to update coordination strategy + + + +User: "Update the coding assistant's prompt to focus more on Python" +Action: Find the coding assistant's agentId from group_members context, then use updateAgentPrompt with that agentId + + + +User: "Modify the designer agent's prompt" +Action: Find the designer agent's agentId from group_members context, then use updateAgentPrompt with that agentId + + + +User: "Change the supervisor's model to Claude" Action: Use updateConfig with { config: { model: "claude-sonnet-4-5-20250929", provider: "anthropic" } } for the supervisor agent + -User: "Enable web browsing for the supervisor" -Action: Use togglePlugin with pluginId "lobe-web-browsing" and enabled: true - + User: "What can the supervisor agent do?" -Action: Reference the \`\` config from the context, including model, plugins, etc. +Action: Reference the \`\` config from the context, including model, tools, etc. + -User: "Set the response order to sequential" -Action: This is a group-level config, use updatePrompt or mention this needs to be changed in group settings - -User: "帮我添加一些新的工具给这个群组" + +User: "Add some new tools to this group" Action: Use searchMarketTools to find tools, then use installPlugin for the supervisor agent + + User: "Set a welcome message for this group" -Action: Use updateGroupConfig with { config: { openingMessage: "Welcome to the team! We're here to help you with your project." } } +Action: Use updateGroup with { config: { openingMessage: "Welcome to the team! We're here to help you with your project." } } + -User: "帮我设置一些开场问题" -Action: Use updateGroupConfig with { config: { openingQuestions: ["What project are you working on?", "How can we help you today?", "Do you have any specific questions?"] } } - -User: "Remove the opening message" -Action: Use updateGroupConfig with { config: { openingMessage: "" } } + +User: "Set some opening questions" +Action: Use updateGroup with { config: { openingQuestions: ["What project are you working on?", "How can we help you today?", "Do you have any specific questions?"] } } + - When showing configuration, format it in a clear, readable way using markdown - When making changes, clearly state what was changed (before → after) - Distinguish between group-level and agent-level changes +- Clarify whether you're updating shared content (group prompt) or a specific agent's prompt - Use bullet points for listing multiple items - Keep responses concise but informative `; diff --git a/packages/builtin-tool-group-agent-builder/src/types.ts b/packages/builtin-tool-group-agent-builder/src/types.ts index 36a9afc112..84e49b19cf 100644 --- a/packages/builtin-tool-group-agent-builder/src/types.ts +++ b/packages/builtin-tool-group-agent-builder/src/types.ts @@ -1,3 +1,4 @@ +import type { UpdateAgentConfigParams } from '@lobechat/builtin-tool-agent-builder'; import type { MetaData } from '@lobechat/types'; /** @@ -9,22 +10,29 @@ export const GroupAgentBuilderIdentifier = 'lobe-group-agent-builder'; * Group Agent Builder API Names */ export const GroupAgentBuilderApiName = { + // Group member management operations + batchCreateAgents: 'batchCreateAgents', + createAgent: 'createAgent', + // Read operations (inherited from AgentBuilder) getAvailableModels: 'getAvailableModels', + // Write operations (inherited from AgentBuilder) installPlugin: 'installPlugin', - // Group-specific operations inviteAgent: 'inviteAgent', removeAgent: 'removeAgent', + searchAgent: 'searchAgent', + searchMarketTools: 'searchMarketTools', updateAgentConfig: 'updateConfig', - // Group config operations - updateGroupConfig: 'updateGroupConfig', - updatePrompt: 'updatePrompt', + // Group operations + updateAgentPrompt: 'updateAgentPrompt', + updateGroup: 'updateGroup', + updateGroupPrompt: 'updateGroupPrompt', } as const; export type GroupAgentBuilderApiNameType = @@ -32,6 +40,41 @@ export type GroupAgentBuilderApiNameType = // ============== Group-specific Parameter Types ============== +export interface SearchAgentParams { + /** + * Maximum number of results to return + */ + limit?: number; + /** + * Search query to find agents by name or description + */ + query?: string; +} + +export interface CreateAgentParams { + /** + * An emoji or image URL for the agent's avatar + */ + avatar?: string; + /** + * A brief description of what this agent does + */ + description?: string; + /** + * The system prompt that defines the agent's behavior + */ + systemRole: string; + /** + * The display name for the new agent + */ + title: string; + /** + * List of tool identifiers to enable for this agent. + * Use the same identifiers as shown in official_tools context. + */ + tools?: string[]; +} + export interface InviteAgentParams { /** * Agent identifier to invite to the group @@ -46,9 +89,92 @@ export interface RemoveAgentParams { agentId: string; } +export interface UpdateAgentPromptParams { + /** + * The agent ID to update + */ + agentId: string; + /** + * The new system prompt content (markdown format) + */ + prompt: string; +} + +export interface UpdateAgentPromptState { + /** + * The agent ID that was updated + */ + agentId: string; + /** + * The new prompt + */ + newPrompt: string; + /** + * The previous prompt + */ + previousPrompt?: string; + /** + * Whether the operation was successful + */ + success: boolean; +} + +/** + * Extended UpdateAgentConfigParams with optional agentId for group context + */ +export interface UpdateAgentConfigWithIdParams extends UpdateAgentConfigParams { + /** + * The agent ID to update. If not provided, updates the supervisor agent. + */ + agentId?: string; +} + +/** + * Unified params for updating group (combines config and meta) + */ +export interface UpdateGroupParams { + /** + * Partial group configuration to update + */ + config?: { + /** + * Opening message shown when starting a new conversation with the group + */ + openingMessage?: string; + /** + * Suggested opening questions to help users get started + */ + openingQuestions?: string[]; + }; + /** + * Partial metadata to update for the group + */ + meta?: Partial>; +} + +export interface UpdateGroupState { + /** + * Whether the operation was successful + */ + success: boolean; + /** + * The updated configuration values + */ + updatedConfig?: { + openingMessage?: string; + openingQuestions?: string[]; + }; + /** + * The updated metadata values + */ + updatedMeta?: Partial< + Pick + >; +} + export interface UpdateGroupPromptParams { /** - * The new system prompt content for the group (markdown format) + * The new shared prompt/content for the group (markdown format) */ prompt: string; /** @@ -57,15 +183,77 @@ export interface UpdateGroupPromptParams { streaming?: boolean; } -export interface UpdateGroupMetaParams { +export interface UpdateGroupPromptState { /** - * Partial metadata to update for the group + * The new prompt */ - meta?: Partial>; + newPrompt: string; + /** + * The previous prompt + */ + previousPrompt?: string; + /** + * Whether the operation was successful + */ + success: boolean; +} + +export interface BatchCreateAgentsParams { + /** + * Array of agents to create + */ + agents: CreateAgentParams[]; +} + +export interface BatchCreateAgentsState { + /** + * Created agents info + */ + agents: Array<{ + agentId: string; + success: boolean; + title: string; + }>; + /** + * Number of agents that failed to create + */ + failedCount: number; + /** + * Number of agents successfully created + */ + successCount: number; } // ============== State Types (for Render components) ============== +export interface SearchAgentResult { + avatar?: string; + description?: string; + id: string; + title: string; +} + +export interface SearchAgentState { + agents: SearchAgentResult[]; + query?: string; + total: number; +} + +export interface CreateAgentState { + /** + * The ID of the created agent + */ + agentId: string; + /** + * Whether the operation was successful + */ + success: boolean; + /** + * The title of the created agent + */ + title: string; +} + export interface InviteAgentState { /** * Agent identifier that was invited @@ -95,41 +283,3 @@ export interface RemoveAgentState { */ success: boolean; } - -export interface UpdateGroupPromptState { - newPrompt: string; - previousPrompt?: string; - success: boolean; -} - -// ============== Group Config Types ============== - -export interface UpdateGroupConfigParams { - /** - * Partial group configuration to update - */ - config?: { - /** - * Opening message shown when starting a new conversation with the group - */ - openingMessage?: string; - /** - * Suggested opening questions to help users get started - */ - openingQuestions?: string[]; - }; -} - -export interface UpdateGroupConfigState { - /** - * Whether the operation was successful - */ - success: boolean; - /** - * The updated configuration values - */ - updatedConfig: { - openingMessage?: string; - openingQuestions?: string[]; - }; -} diff --git a/packages/builtin-tool-group-management/src/client/Inspector/Broadcast/index.tsx b/packages/builtin-tool-group-management/src/client/Inspector/Broadcast/index.tsx index 3d021cba9d..288080f6f9 100644 --- a/packages/builtin-tool-group-management/src/client/Inspector/Broadcast/index.tsx +++ b/packages/builtin-tool-group-management/src/client/Inspector/Broadcast/index.tsx @@ -1,7 +1,7 @@ 'use client'; import { DEFAULT_AVATAR } from '@lobechat/const'; -import type { AgentItem, BuiltinInspectorProps } from '@lobechat/types'; +import type { AgentGroupMember, BuiltinInspectorProps } from '@lobechat/types'; import { Avatar, Flexbox } from '@lobehub/ui'; import { createStaticStyles, cx, useTheme } from 'antd-style'; import { memo, useMemo } from 'react'; @@ -45,7 +45,7 @@ export const BroadcastInspector = memo>( if (!agentIds.length || !groupAgents.length) return []; return agentIds .map((id) => groupAgents.find((agent) => agent.id === id)) - .filter((agent): agent is AgentItem => !!agent); + .filter((agent): agent is AgentGroupMember => !!agent); }, [agentIds, groupAgents]); // Transform agents to Avatar.Group format diff --git a/packages/builtin-tool-group-management/src/manifest.ts b/packages/builtin-tool-group-management/src/manifest.ts index b053848389..7a8886f085 100644 --- a/packages/builtin-tool-group-management/src/manifest.ts +++ b/packages/builtin-tool-group-management/src/manifest.ts @@ -57,7 +57,7 @@ export const GroupManagementManifest: BuiltinToolManifest = { { description: 'Create a new agent dynamically based on user requirements and add it to the group. Use this when no existing agent matches the needed expertise.', - humanIntervention: 'always', + humanIntervention: 'required', name: GroupManagementApiName.createAgent, parameters: { properties: { diff --git a/packages/builtin-tool-gtd/src/client/Inspector/ClearTodos/index.tsx b/packages/builtin-tool-gtd/src/client/Inspector/ClearTodos/index.tsx index 2105ffd0dc..25b1cfde01 100644 --- a/packages/builtin-tool-gtd/src/client/Inspector/ClearTodos/index.tsx +++ b/packages/builtin-tool-gtd/src/client/Inspector/ClearTodos/index.tsx @@ -5,7 +5,7 @@ import { createStaticStyles, cx } from 'antd-style'; import { memo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { inspectorTextStyles, shinyTextStyles } from '@/styles'; import type { ClearTodosParams, ClearTodosState } from '../../../types'; @@ -15,14 +15,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({ color: ${cssVar.colorText}; background: linear-gradient(to top, ${cssVar.colorWarningBg} 40%, transparent 40%); `, - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, })); export const ClearTodosInspector = memo>( @@ -33,7 +25,7 @@ export const ClearTodosInspector = memo +
{t('builtins.lobe-gtd.apiName.clearTodos')}
); @@ -45,7 +37,9 @@ export const ClearTodosInspector = memo +
}} i18nKey="builtins.lobe-gtd.apiName.clearTodos.result" diff --git a/packages/builtin-tool-gtd/src/client/Inspector/CompleteTodos/index.tsx b/packages/builtin-tool-gtd/src/client/Inspector/CompleteTodos/index.tsx index 88eeb97ee1..696855f068 100644 --- a/packages/builtin-tool-gtd/src/client/Inspector/CompleteTodos/index.tsx +++ b/packages/builtin-tool-gtd/src/client/Inspector/CompleteTodos/index.tsx @@ -7,17 +7,11 @@ import { CheckCircle } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { oneLineEllipsis, shinyTextStyles } from '@/styles'; import type { CompleteTodosParams, CompleteTodosState } from '../../../types'; const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - `, title: css` margin-inline-end: 8px; color: ${cssVar.colorText}; @@ -34,14 +28,14 @@ export const CompleteTodosInspector = memo< if (isArgumentsStreaming && count === 0) { return ( -
+
{t('builtins.lobe-gtd.apiName.completeTodos')}
); } return ( -
+
{t('builtins.lobe-gtd.apiName.completeTodos')} {count > 0 && ( diff --git a/packages/builtin-tool-gtd/src/client/Inspector/CreatePlan/index.tsx b/packages/builtin-tool-gtd/src/client/Inspector/CreatePlan/index.tsx index 0ed09733b7..3c95767bdf 100644 --- a/packages/builtin-tool-gtd/src/client/Inspector/CreatePlan/index.tsx +++ b/packages/builtin-tool-gtd/src/client/Inspector/CreatePlan/index.tsx @@ -1,25 +1,14 @@ 'use client'; import type { BuiltinInspectorProps } from '@lobechat/types'; -import { createStaticStyles, cx } from 'antd-style'; +import { cx } from 'antd-style'; import { memo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import type { CreatePlanParams, CreatePlanState } from '../../../types'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const CreatePlanInspector = memo>( ({ args, partialArgs, isArgumentsStreaming }) => { const { t } = useTranslation('plugin'); @@ -28,14 +17,16 @@ export const CreatePlanInspector = memo +
{t('builtins.lobe-gtd.apiName.createPlan')}
); } return ( -
+
{goal ? ( }} diff --git a/packages/builtin-tool-gtd/src/client/Inspector/CreateTodos/index.tsx b/packages/builtin-tool-gtd/src/client/Inspector/CreateTodos/index.tsx index 41741b670e..6d8b59219a 100644 --- a/packages/builtin-tool-gtd/src/client/Inspector/CreateTodos/index.tsx +++ b/packages/builtin-tool-gtd/src/client/Inspector/CreateTodos/index.tsx @@ -7,17 +7,11 @@ import { Plus } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { oneLineEllipsis, shinyTextStyles } from '@/styles'; import type { CreateTodosParams, CreateTodosState } from '../../../types'; const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - `, title: css` margin-inline-end: 8px; color: ${cssVar.colorText}; @@ -35,14 +29,14 @@ export const CreateTodosInspector = memo< if (isArgumentsStreaming && count === 0) { return ( -
+
{t('builtins.lobe-gtd.apiName.createTodos')}
); } return ( -
+
{t('builtins.lobe-gtd.apiName.createTodos')} {count > 0 && ( diff --git a/packages/builtin-tool-gtd/src/client/Inspector/ExecTask/index.tsx b/packages/builtin-tool-gtd/src/client/Inspector/ExecTask/index.tsx index 0d83206b2f..db8c954775 100644 --- a/packages/builtin-tool-gtd/src/client/Inspector/ExecTask/index.tsx +++ b/packages/builtin-tool-gtd/src/client/Inspector/ExecTask/index.tsx @@ -1,25 +1,14 @@ 'use client'; import type { BuiltinInspectorProps } from '@lobechat/types'; -import { createStaticStyles, cx } from 'antd-style'; +import { cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import type { ExecTaskParams, ExecTaskState } from '../../../types'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const ExecTaskInspector = memo>( ({ args, partialArgs, isArgumentsStreaming, isLoading }) => { const { t } = useTranslation('plugin'); @@ -30,13 +19,13 @@ export const ExecTaskInspector = memo +
{t('builtins.lobe-gtd.apiName.execTask')}
); return ( -
+
{t('builtins.lobe-gtd.apiName.execTask.loading')} {description}
@@ -46,7 +35,7 @@ export const ExecTaskInspector = memo +
{isLoading ? t('builtins.lobe-gtd.apiName.execTask.loading') @@ -59,7 +48,7 @@ export const ExecTaskInspector = memo +
{t('builtins.lobe-gtd.apiName.execTask')}
); diff --git a/packages/builtin-tool-gtd/src/client/Inspector/RemoveTodos/index.tsx b/packages/builtin-tool-gtd/src/client/Inspector/RemoveTodos/index.tsx index 27348aac1a..6a7a68a835 100644 --- a/packages/builtin-tool-gtd/src/client/Inspector/RemoveTodos/index.tsx +++ b/packages/builtin-tool-gtd/src/client/Inspector/RemoveTodos/index.tsx @@ -7,17 +7,11 @@ import { Minus } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { oneLineEllipsis, shinyTextStyles } from '@/styles'; import type { RemoveTodosParams, RemoveTodosState } from '../../../types'; const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - `, title: css` margin-inline-end: 8px; color: ${cssVar.colorText}; @@ -34,14 +28,14 @@ export const RemoveTodosInspector = memo< if (isArgumentsStreaming && count === 0) { return ( -
+
{t('builtins.lobe-gtd.apiName.removeTodos')}
); } return ( -
+
{t('builtins.lobe-gtd.apiName.removeTodos')} {count > 0 && ( diff --git a/packages/builtin-tool-gtd/src/client/Inspector/UpdatePlan/index.tsx b/packages/builtin-tool-gtd/src/client/Inspector/UpdatePlan/index.tsx index 5d1b9897ef..20b2f6b3a9 100644 --- a/packages/builtin-tool-gtd/src/client/Inspector/UpdatePlan/index.tsx +++ b/packages/builtin-tool-gtd/src/client/Inspector/UpdatePlan/index.tsx @@ -7,17 +7,11 @@ import { CheckCircle, DiffIcon } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { oneLineEllipsis, shinyTextStyles } from '@/styles'; import type { UpdatePlanParams, UpdatePlanState } from '../../../types'; const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - `, title: css` margin-inline-end: 8px; color: ${cssVar.colorText}; @@ -34,14 +28,14 @@ export const UpdatePlanInspector = memo +
{t('builtins.lobe-gtd.apiName.updatePlan')}
); } return ( -
+
{t('builtins.lobe-gtd.apiName.updatePlan')} {completed && ( diff --git a/packages/builtin-tool-gtd/src/client/Inspector/UpdateTodos/index.tsx b/packages/builtin-tool-gtd/src/client/Inspector/UpdateTodos/index.tsx index f7bceb2c64..13b73cdd24 100644 --- a/packages/builtin-tool-gtd/src/client/Inspector/UpdateTodos/index.tsx +++ b/packages/builtin-tool-gtd/src/client/Inspector/UpdateTodos/index.tsx @@ -7,17 +7,11 @@ import { CheckCircle, DiffIcon, Minus, Plus } from 'lucide-react'; import { type ReactNode, memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { oneLineEllipsis, shinyTextStyles } from '@/styles'; import type { UpdateTodosParams, UpdateTodosState } from '../../../types'; const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - `, separator: css` margin-inline: 2px; color: ${cssVar.colorTextQuaternary}; @@ -66,7 +60,7 @@ export const UpdateTodosInspector = memo< if (isArgumentsStreaming && !hasOperations) { return ( -
+
{t('builtins.lobe-gtd.apiName.updateTodos')}
); @@ -107,7 +101,7 @@ export const UpdateTodosInspector = memo< } return ( -
+
{t('builtins.lobe-gtd.apiName.updateTodos')} {statsParts.length > 0 && ( <> diff --git a/packages/builtin-tool-knowledge-base/src/client/Inspector/ReadKnowledge/index.tsx b/packages/builtin-tool-knowledge-base/src/client/Inspector/ReadKnowledge/index.tsx index 5eb35c984a..a267203a1a 100644 --- a/packages/builtin-tool-knowledge-base/src/client/Inspector/ReadKnowledge/index.tsx +++ b/packages/builtin-tool-knowledge-base/src/client/Inspector/ReadKnowledge/index.tsx @@ -5,7 +5,7 @@ import { createStaticStyles, cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type ReadKnowledgeArgs, type ReadKnowledgeState } from '../../..'; @@ -14,18 +14,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({ margin-inline-start: 4px; color: ${cssVar.colorTextTertiary}; `, - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, - statusIcon: css` - margin-block-end: -2px; - margin-inline-start: 4px; - `, })); export const ReadKnowledgeInspector = memo< @@ -43,13 +31,13 @@ export const ReadKnowledgeInspector = memo< if (isArgumentsStreaming) { if (fileCount === 0) return ( -
+
{t('builtins.lobe-knowledge-base.apiName.readKnowledge')}
); return ( -
+
{t('builtins.lobe-knowledge-base.apiName.readKnowledge')}: {fileCount} {fileCount === 1 ? 'file' : 'files'} @@ -85,7 +73,7 @@ export const ReadKnowledgeInspector = memo< }; return ( -
+
{t('builtins.lobe-knowledge-base.apiName.readKnowledge')}: {renderFileInfo()} diff --git a/packages/builtin-tool-knowledge-base/src/client/Inspector/SearchKnowledgeBase/index.tsx b/packages/builtin-tool-knowledge-base/src/client/Inspector/SearchKnowledgeBase/index.tsx index adfe50e73d..99685190ea 100644 --- a/packages/builtin-tool-knowledge-base/src/client/Inspector/SearchKnowledgeBase/index.tsx +++ b/packages/builtin-tool-knowledge-base/src/client/Inspector/SearchKnowledgeBase/index.tsx @@ -2,25 +2,14 @@ import { type BuiltinInspectorProps } from '@lobechat/types'; import { Text } from '@lobehub/ui'; -import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { cssVar, cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type SearchKnowledgeBaseArgs, type SearchKnowledgeBaseState } from '../../..'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const SearchKnowledgeBaseInspector = memo< BuiltinInspectorProps >(({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { @@ -35,13 +24,13 @@ export const SearchKnowledgeBaseInspector = memo< if (isArgumentsStreaming) { if (!query) return ( -
+
{t('builtins.lobe-knowledge-base.apiName.searchKnowledgeBase')}
); return ( -
+
{t('builtins.lobe-knowledge-base.apiName.searchKnowledgeBase')}: {query}
@@ -49,7 +38,7 @@ export const SearchKnowledgeBaseInspector = memo< } return ( -
+
{t('builtins.lobe-knowledge-base.apiName.searchKnowledgeBase')}: {query && {query}} diff --git a/packages/builtin-tool-local-system/src/client/Inspector/EditLocalFile/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/EditLocalFile/index.tsx index f8c9453563..823cb84387 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/EditLocalFile/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/EditLocalFile/index.tsx @@ -8,20 +8,12 @@ import { Minus, Plus } from 'lucide-react'; import { type ReactNode, memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type EditLocalFileState } from '../../../types'; import { FilePathDisplay } from '../../components/FilePathDisplay'; const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, separator: css` margin-inline: 2px; color: ${cssVar.colorTextQuaternary}; @@ -39,13 +31,13 @@ export const EditLocalFileInspector = memo< if (isArgumentsStreaming) { if (!filePath) return ( -
+
{t('builtins.lobe-local-system.apiName.editLocalFile')}
); return ( -
+
{t('builtins.lobe-local-system.apiName.editLocalFile')}:
@@ -75,7 +67,7 @@ export const EditLocalFileInspector = memo< } return ( -
+
{t('builtins.lobe-local-system.apiName.editLocalFile')}: {!isLoading && statsParts.length > 0 && ( diff --git a/packages/builtin-tool-local-system/src/client/Inspector/GlobLocalFiles/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/GlobLocalFiles/index.tsx index d36fd495e3..4306ab3524 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/GlobLocalFiles/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/GlobLocalFiles/index.tsx @@ -7,19 +7,11 @@ import { Check, X } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type GlobFilesState } from '../../..'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, +const styles = createStaticStyles(({ css }) => ({ statusIcon: css` margin-block-end: -2px; margin-inline-start: 4px; @@ -36,13 +28,13 @@ export const GlobLocalFilesInspector = memo +
{t('builtins.lobe-local-system.apiName.globLocalFiles')}
); return ( -
+
{t('builtins.lobe-local-system.apiName.globLocalFiles')}: {pattern}
@@ -53,7 +45,7 @@ export const GlobLocalFilesInspector = memo +
{t('builtins.lobe-local-system.apiName.globLocalFiles')}: {pattern && {pattern}} diff --git a/packages/builtin-tool-local-system/src/client/Inspector/GrepContent/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/GrepContent/index.tsx index 8115c8796c..e8879fecf8 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/GrepContent/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/GrepContent/index.tsx @@ -3,25 +3,14 @@ import { type GrepContentParams } from '@lobechat/electron-client-ipc'; import { type BuiltinInspectorProps } from '@lobechat/types'; import { Text } from '@lobehub/ui'; -import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { cssVar, cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type GrepContentState } from '../../..'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const GrepContentInspector = memo< BuiltinInspectorProps >(({ args, partialArgs, isArgumentsStreaming, pluginState, isLoading }) => { @@ -33,13 +22,13 @@ export const GrepContentInspector = memo< if (isArgumentsStreaming) { if (!pattern) return ( -
+
{t('builtins.lobe-local-system.apiName.grepContent')}
); return ( -
+
{t('builtins.lobe-local-system.apiName.grepContent')}: {pattern}
@@ -51,7 +40,7 @@ export const GrepContentInspector = memo< const hasResults = resultCount > 0; return ( -
+
{t('builtins.lobe-local-system.apiName.grepContent')}: {pattern && {pattern}} {!isLoading && diff --git a/packages/builtin-tool-local-system/src/client/Inspector/ListLocalFiles/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/ListLocalFiles/index.tsx index 9352b6fee2..ea5da6bfad 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/ListLocalFiles/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/ListLocalFiles/index.tsx @@ -3,26 +3,15 @@ import { type ListLocalFileParams } from '@lobechat/electron-client-ipc'; import { type BuiltinInspectorProps } from '@lobechat/types'; import { Text } from '@lobehub/ui'; -import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { cssVar, cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type LocalFileListState } from '../../..'; import { FilePathDisplay } from '../../components/FilePathDisplay'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const ListLocalFilesInspector = memo< BuiltinInspectorProps >(({ args, partialArgs, isArgumentsStreaming, pluginState, isLoading }) => { @@ -34,13 +23,13 @@ export const ListLocalFilesInspector = memo< if (isArgumentsStreaming) { if (!path) return ( -
+
{t('builtins.lobe-local-system.apiName.listLocalFiles')}
); return ( -
+
{t('builtins.lobe-local-system.apiName.listLocalFiles')}:
@@ -52,7 +41,7 @@ export const ListLocalFilesInspector = memo< const hasResults = resultCount > 0; return ( -
+
{t('builtins.lobe-local-system.apiName.listLocalFiles')}: {!isLoading && diff --git a/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx index 8293275f3b..139d4be3f6 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx @@ -2,26 +2,15 @@ import { type LocalReadFileParams } from '@lobechat/electron-client-ipc'; import { type BuiltinInspectorProps } from '@lobechat/types'; -import { createStaticStyles, cx } from 'antd-style'; +import { cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type LocalReadFileState } from '../../..'; import { FilePathDisplay } from '../../components/FilePathDisplay'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const ReadLocalFileInspector = memo< BuiltinInspectorProps >(({ args, partialArgs, isArgumentsStreaming, isLoading }) => { @@ -33,13 +22,13 @@ export const ReadLocalFileInspector = memo< if (isArgumentsStreaming) { if (!filePath) return ( -
+
{t('builtins.lobe-local-system.apiName.readLocalFile')}
); return ( -
+
{t('builtins.lobe-local-system.apiName.readLocalFile')}:
@@ -47,7 +36,7 @@ export const ReadLocalFileInspector = memo< } return ( -
+
{t('builtins.lobe-local-system.apiName.readLocalFile')}:
diff --git a/packages/builtin-tool-local-system/src/client/Inspector/RenameLocalFile/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/RenameLocalFile/index.tsx index c438ebb0f5..7a26bb98e7 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/RenameLocalFile/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/RenameLocalFile/index.tsx @@ -8,23 +8,15 @@ import path from 'path-browserify-esm'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type LocalRenameFileState } from '../../..'; -const styles = createStaticStyles(({ css, cssVar }) => ({ +const styles = createStaticStyles(({ css }) => ({ icon: css` flex-shrink: 0; margin-inline-end: 4px; `, - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, })); export const RenameLocalFileInspector = memo< @@ -39,7 +31,9 @@ export const RenameLocalFileInspector = memo< const oldName = filePath ? path.basename(filePath) : ''; return ( -
+
{oldName && newName ? ( <> {t('builtins.lobe-local-system.apiName.renameLocalFile')} {oldName} →{' '} diff --git a/packages/builtin-tool-local-system/src/client/Inspector/RunCommand/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/RunCommand/index.tsx index b98e3d2847..a2104059a1 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/RunCommand/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/RunCommand/index.tsx @@ -7,17 +7,9 @@ import { Check, X } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, +const styles = createStaticStyles(({ css }) => ({ statusIcon: css` margin-block-end: -2px; margin-inline-start: 4px; @@ -40,13 +32,13 @@ export const RunCommandInspector = memo +
{t('builtins.lobe-local-system.apiName.runCommand')}
); return ( -
+
{t('builtins.lobe-local-system.apiName.runCommand')}: {description}
@@ -58,7 +50,7 @@ export const RunCommandInspector = memo +
{t('builtins.lobe-local-system.apiName.runCommand')}: {description && {description}} diff --git a/packages/builtin-tool-local-system/src/client/Inspector/SearchLocalFiles/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/SearchLocalFiles/index.tsx index 790984d410..349fff4b64 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/SearchLocalFiles/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/SearchLocalFiles/index.tsx @@ -3,25 +3,14 @@ import { type LocalSearchFilesParams } from '@lobechat/electron-client-ipc'; import { type BuiltinInspectorProps } from '@lobechat/types'; import { Text } from '@lobehub/ui'; -import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { cssVar, cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import { type LocalFileSearchState } from '../../..'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const SearchLocalFilesInspector = memo< BuiltinInspectorProps >(({ args, partialArgs, isArgumentsStreaming, pluginState, isLoading }) => { @@ -33,13 +22,13 @@ export const SearchLocalFilesInspector = memo< if (isArgumentsStreaming) { if (!keywords) return ( -
+
{t('builtins.lobe-local-system.apiName.searchLocalFiles')}
); return ( -
+
{t('builtins.lobe-local-system.apiName.searchLocalFiles')}: {keywords}
@@ -51,7 +40,7 @@ export const SearchLocalFilesInspector = memo< const hasResults = resultCount > 0; return ( -
+
{t('builtins.lobe-local-system.apiName.searchLocalFiles')}: {keywords && {keywords}} diff --git a/packages/builtin-tool-local-system/src/client/Inspector/WriteLocalFile/index.tsx b/packages/builtin-tool-local-system/src/client/Inspector/WriteLocalFile/index.tsx index c41c48f139..acc6429e94 100644 --- a/packages/builtin-tool-local-system/src/client/Inspector/WriteLocalFile/index.tsx +++ b/packages/builtin-tool-local-system/src/client/Inspector/WriteLocalFile/index.tsx @@ -3,26 +3,15 @@ import { type WriteLocalFileParams } from '@lobechat/electron-client-ipc'; import { type BuiltinInspectorProps } from '@lobechat/types'; import { Icon, Text } from '@lobehub/ui'; -import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { cssVar, cx } from 'antd-style'; import { Plus } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { inspectorTextStyles, shinyTextStyles } from '@/styles'; import { FilePathDisplay } from '../../components/FilePathDisplay'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const WriteLocalFileInspector = memo>( ({ args, partialArgs, isArgumentsStreaming }) => { const { t } = useTranslation('plugin'); @@ -36,14 +25,16 @@ export const WriteLocalFileInspector = memo +
{t('builtins.lobe-local-system.apiName.writeLocalFile')}
); } return ( -
+
{t('builtins.lobe-local-system.apiName.writeLocalFile')}: {lines > 0 && ( diff --git a/packages/builtin-tool-notebook/src/client/Inspector/CreateDocument/index.tsx b/packages/builtin-tool-notebook/src/client/Inspector/CreateDocument/index.tsx index 551b8e6677..c6c6233ace 100644 --- a/packages/builtin-tool-notebook/src/client/Inspector/CreateDocument/index.tsx +++ b/packages/builtin-tool-notebook/src/client/Inspector/CreateDocument/index.tsx @@ -1,25 +1,14 @@ 'use client'; import type { BuiltinInspectorProps } from '@lobechat/types'; -import { createStaticStyles, cx } from 'antd-style'; +import { cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import type { CreateDocumentArgs, CreateDocumentState } from '../../../types'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const CreateDocumentInspector = memo< BuiltinInspectorProps >(({ args, partialArgs, isArgumentsStreaming, isLoading }) => { @@ -30,7 +19,7 @@ export const CreateDocumentInspector = memo< // During streaming without title, show init if (isArgumentsStreaming && !title) { return ( -
+
{t('builtins.lobe-notebook.apiName.createDocument')}
); @@ -38,7 +27,10 @@ export const CreateDocumentInspector = memo< return (
{t('builtins.lobe-notebook.apiName.createDocument')}: {title && {title}} diff --git a/packages/builtin-tool-page-agent/src/client/Inspector/EditTitle/index.tsx b/packages/builtin-tool-page-agent/src/client/Inspector/EditTitle/index.tsx index e3942921cb..644606406c 100644 --- a/packages/builtin-tool-page-agent/src/client/Inspector/EditTitle/index.tsx +++ b/packages/builtin-tool-page-agent/src/client/Inspector/EditTitle/index.tsx @@ -2,25 +2,14 @@ import type { EditTitleArgs } from '@lobechat/editor-runtime'; import type { BuiltinInspectorProps } from '@lobechat/types'; -import { createStaticStyles, cx } from 'antd-style'; +import { cx } from 'antd-style'; import { memo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import type { EditTitleState } from '../../../types'; -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); - export const EditTitleInspector = memo>( ({ args, partialArgs, isArgumentsStreaming }) => { const { t } = useTranslation('plugin'); @@ -28,7 +17,9 @@ export const EditTitleInspector = memo +
{title ? ( }} diff --git a/packages/builtin-tool-page-agent/src/client/Inspector/GetPageContent/index.tsx b/packages/builtin-tool-page-agent/src/client/Inspector/GetPageContent/index.tsx index 32e0b8e9a8..be1ecb1d92 100644 --- a/packages/builtin-tool-page-agent/src/client/Inspector/GetPageContent/index.tsx +++ b/packages/builtin-tool-page-agent/src/client/Inspector/GetPageContent/index.tsx @@ -5,25 +5,24 @@ import { createStaticStyles, cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { oneLineEllipsis, shinyTextStyles } from '@/styles'; const styles = createStaticStyles(({ css, cssVar }) => ({ done: css` color: ${cssVar.colorTextDescription}; `, - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - `, })); export const GetPageContentInspector = memo(({ isArgumentsStreaming }) => { const { t } = useTranslation('plugin'); return ( -
+
{t('builtins.lobe-page-agent.apiName.getPageContent')}
); diff --git a/packages/builtin-tool-page-agent/src/client/Inspector/InitPage/index.tsx b/packages/builtin-tool-page-agent/src/client/Inspector/InitPage/index.tsx index d2cd3deb1a..eccb59a0a2 100644 --- a/packages/builtin-tool-page-agent/src/client/Inspector/InitPage/index.tsx +++ b/packages/builtin-tool-page-agent/src/client/Inspector/InitPage/index.tsx @@ -8,18 +8,12 @@ import { Plus } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { oneLineEllipsis, shinyTextStyles } from '@/styles'; import type { InitDocumentState } from '../../../types'; import { AnimatedNumber } from '../../components/AnimatedNumber'; const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - `, title: css` margin-inline-end: 8px; color: ${cssVar.colorText}; @@ -43,14 +37,14 @@ export const InitPageInspector = memo +
{t('builtins.lobe-page-agent.apiName.initPage')}
); // During streaming with content, show "creating" title with shiny effect return ( -
+
{t('builtins.lobe-page-agent.apiName.initPage.creating')} @@ -74,7 +68,7 @@ export const InitPageInspector = memo +
{t('builtins.lobe-page-agent.apiName.initPage.result')} diff --git a/packages/builtin-tool-page-agent/src/client/Inspector/ModifyNodes/index.tsx b/packages/builtin-tool-page-agent/src/client/Inspector/ModifyNodes/index.tsx index e7a153fdac..9c6dfae6cf 100644 --- a/packages/builtin-tool-page-agent/src/client/Inspector/ModifyNodes/index.tsx +++ b/packages/builtin-tool-page-agent/src/client/Inspector/ModifyNodes/index.tsx @@ -8,17 +8,11 @@ import { DiffIcon, Minus, Plus } from 'lucide-react'; import { type ReactNode, memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { shinyTextStyles } from '@/styles'; +import { oneLineEllipsis, shinyTextStyles } from '@/styles'; import type { ModifyNodesState } from '../../../types'; const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - `, separator: css` margin-inline: 2px; color: ${cssVar.colorTextQuaternary}; @@ -66,7 +60,7 @@ export const ModifyNodesInspector = memo +
{t('builtins.lobe-page-agent.apiName.modifyNodes.init')}
); @@ -100,7 +94,7 @@ export const ModifyNodesInspector = memo +
{t('builtins.lobe-page-agent.apiName.modifyNodes')} {statsParts.length > 0 && ( <> diff --git a/packages/builtin-tool-page-agent/src/client/Inspector/ReplaceText/index.tsx b/packages/builtin-tool-page-agent/src/client/Inspector/ReplaceText/index.tsx index 132fa1fbf1..df73897733 100644 --- a/packages/builtin-tool-page-agent/src/client/Inspector/ReplaceText/index.tsx +++ b/packages/builtin-tool-page-agent/src/client/Inspector/ReplaceText/index.tsx @@ -8,7 +8,7 @@ import { ArrowRight } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; import type { ReplaceTextState } from '../../../types'; @@ -21,14 +21,6 @@ const styles = createStaticStyles(({ css, cssVar }) => ({ color: ${cssVar.colorTextSecondary}; text-decoration: line-through; `, - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, title: css` margin-inline-end: 8px; color: ${cssVar.colorText}; @@ -45,7 +37,7 @@ export const ReplaceTextInspector = memo +
{t('builtins.lobe-page-agent.apiName.replaceText.init')}
); @@ -55,7 +47,9 @@ export const ReplaceTextInspector = memo +
{t('builtins.lobe-page-agent.apiName.replaceText')} {hasResult && ( <> diff --git a/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlMultiPages/index.tsx b/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlMultiPages/index.tsx index b17f10ecde..3f99d109ec 100644 --- a/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlMultiPages/index.tsx +++ b/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlMultiPages/index.tsx @@ -1,22 +1,11 @@ 'use client'; import { type BuiltinInspectorProps } from '@lobechat/types'; -import { createStaticStyles, cx } from 'antd-style'; +import { cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; - -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; interface CrawlMultiPagesParams { urls: string[]; @@ -42,14 +31,16 @@ export const CrawlMultiPagesInspector = memo +
{t('builtins.lobe-web-browsing.apiName.crawlMultiPages')}
); } return ( -
+
{t('builtins.lobe-web-browsing.apiName.crawlMultiPages')}: {displayText && {displayText}}
diff --git a/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlSinglePage/index.tsx b/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlSinglePage/index.tsx index c8768e6fe8..fa050293f7 100644 --- a/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlSinglePage/index.tsx +++ b/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlSinglePage/index.tsx @@ -1,22 +1,11 @@ 'use client'; import type { BuiltinInspectorProps } from '@lobechat/types'; -import { createStaticStyles, cx } from 'antd-style'; +import { cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; - -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; interface CrawlSinglePageParams { url: string; @@ -30,14 +19,16 @@ export const CrawlSinglePageInspector = memo +
{t('builtins.lobe-web-browsing.apiName.crawlSinglePage')}
); } return ( -
+
{t('builtins.lobe-web-browsing.apiName.crawlSinglePage')}: {url && {url}}
diff --git a/packages/builtin-tool-web-browsing/src/client/Inspector/Search/index.tsx b/packages/builtin-tool-web-browsing/src/client/Inspector/Search/index.tsx index 86c911687a..3261c9b8ab 100644 --- a/packages/builtin-tool-web-browsing/src/client/Inspector/Search/index.tsx +++ b/packages/builtin-tool-web-browsing/src/client/Inspector/Search/index.tsx @@ -6,22 +6,11 @@ import { type UniformSearchResponse, } from '@lobechat/types'; import { Text } from '@lobehub/ui'; -import { createStaticStyles, cssVar, cx } from 'antd-style'; +import { cssVar, cx } from 'antd-style'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightTextStyles, shinyTextStyles } from '@/styles'; - -const styles = createStaticStyles(({ css, cssVar }) => ({ - root: css` - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - - color: ${cssVar.colorTextSecondary}; - `, -})); +import { highlightTextStyles, inspectorTextStyles, shinyTextStyles } from '@/styles'; export const SearchInspector = memo>( ({ args, partialArgs, isArgumentsStreaming, isLoading, pluginState }) => { @@ -33,7 +22,7 @@ export const SearchInspector = memo +
{t('builtins.lobe-web-browsing.apiName.search')}
); @@ -42,7 +31,7 @@ export const SearchInspector = memo diff --git a/packages/database/src/models/chatGroup.ts b/packages/database/src/models/chatGroup.ts index be7918b8d8..a4f5d2cae0 100644 --- a/packages/database/src/models/chatGroup.ts +++ b/packages/database/src/models/chatGroup.ts @@ -115,7 +115,7 @@ export class ChatGroupModel { async update(id: string, value: Partial): Promise { const [result] = await this.db .update(chatGroups) - .set({ ...value, updatedAt: new Date() }) + .set(value) .where(and(eq(chatGroups.id, id), eq(chatGroups.userId, this.userId))) .returning(); diff --git a/packages/types/src/agentGroup/index.ts b/packages/types/src/agentGroup/index.ts index 25b9c911c4..785fe28426 100644 --- a/packages/types/src/agentGroup/index.ts +++ b/packages/types/src/agentGroup/index.ts @@ -5,6 +5,8 @@ import { TaskDetail, UIChatMessage } from '../message'; import { ChatTopic } from '../topic'; export interface LobeChatGroupMetaConfig { + avatar?: string; + backgroundColor?: string; description: string; title: string; } @@ -75,6 +77,8 @@ export interface NewChatGroupAgent { // New Chat Group type for creating groups (independent from schema) export interface NewChatGroup { + avatar?: string | null; + backgroundColor?: string | null; clientId?: string | null; config?: LobeChatGroupConfig | null; description?: string | null; @@ -88,10 +92,14 @@ export interface NewChatGroup { // Chat Group Item type (independent from schema) export interface ChatGroupItem { accessedAt?: Date; + avatar?: string | null; + backgroundColor?: string | null; clientId?: string | null; config?: LobeChatGroupConfig | null; + content?: string | null; createdAt: Date; description?: string | null; + editorData?: Record | null; groupId?: string | null; id: string; pinned?: boolean | null; diff --git a/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx b/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx index cdd2f5607c..1f035e2648 100644 --- a/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx +++ b/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx @@ -55,7 +55,7 @@ const Nav = memo(() => { active={isProfileActive} icon={BotPromptIcon} onClick={() => { - switchTopic(undefined, true); + switchTopic(null, { skipRefreshMessage: true }); router.push(urlJoin('/agent', agentId!, 'profile')); }} title={t('tab.profile')} diff --git a/src/app/[variants]/(main)/agent/cron/[cronId]/index.tsx b/src/app/[variants]/(main)/agent/cron/[cronId]/index.tsx index 3433d1833e..3189cbaefc 100644 --- a/src/app/[variants]/(main)/agent/cron/[cronId]/index.tsx +++ b/src/app/[variants]/(main)/agent/cron/[cronId]/index.tsx @@ -11,7 +11,7 @@ import { ReactMathPlugin, ReactTablePlugin, } from '@lobehub/editor'; -import { Editor, useEditor } from '@lobehub/editor/react'; +import { Editor, useEditor, useEditorState } from '@lobehub/editor/react'; import { ActionIcon, Flexbox, Icon, Input, Tag, Text } from '@lobehub/ui'; import { useDebounceFn } from 'ahooks'; import { App, Card, Checkbox, Empty, InputNumber, Select, Switch, TimePicker, message } from 'antd'; @@ -33,7 +33,7 @@ import useSWR from 'swr'; import AutoSaveHint from '@/components/Editor/AutoSaveHint'; import Loading from '@/components/Loading/BrandTextLoading'; import type { ExecutionConditions, UpdateAgentCronJobData } from '@/database/schemas/agentCronJob'; -import TypoBar from '@/features/EditorModal/Typobar'; +import { InlineToolbar } from '@/features/EditorCanvas'; import NavHeader from '@/features/NavHeader'; import WideScreenContainer from '@/features/WideScreenContainer'; import { useQueryRoute } from '@/hooks/useQueryRoute'; @@ -142,6 +142,7 @@ const CronJobDetailPage = memo(() => { const router = useQueryRoute(); const { modal } = App.useApp(); const editor = useEditor(); + const editorState = useEditorState(editor); const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown); const [editorReady, setEditorReady] = useState(false); @@ -628,7 +629,7 @@ const CronJobDetailPage = memo(() => { style={{ borderRadius: 12, overflow: 'hidden' }} styles={{ body: { padding: 0 } }} > - {enableRichRender && } + {enableRichRender && } { onClick={() => { if (!agentId) return; // Clear topicId before navigating to prevent stale state - switchTopic(undefined, true); + switchTopic(null, { skipRefreshMessage: true }); router.push(urlJoin('/agent', agentId)); }} type={'primary'} diff --git a/src/app/[variants]/(main)/agent/profile/features/store/action.ts b/src/app/[variants]/(main)/agent/profile/features/store/action.ts index 9b71816672..e979a919d5 100644 --- a/src/app/[variants]/(main)/agent/profile/features/store/action.ts +++ b/src/app/[variants]/(main)/agent/profile/features/store/action.ts @@ -28,26 +28,25 @@ export interface Action { export type Store = State & Action; -// Create debounced save function outside of store for reuse -const createDebouncedSave = ( - get: () => Store, - updateConfig: (payload: SaveConfigPayload) => Promise, -) => - debounce( - async (payload: SaveConfigPayload) => { - try { - await updateConfig(payload); - } catch (error) { - console.error('[ProfileEditor] Failed to save:', error); - } - }, - EDITOR_DEBOUNCE_TIME, - { leading: false, maxWait: EDITOR_MAX_WAIT, trailing: true }, - ); +// Store the latest updateConfig reference to avoid stale closures +let updateConfigRef: ((payload: SaveConfigPayload) => Promise) | null = null; export const store: (initState?: Partial) => StateCreator = (initState) => (set, get) => { - let debouncedSave: ReturnType | null = null; + // Create debounced save that uses the latest callback reference + const debouncedSave = debounce( + async (payload: SaveConfigPayload) => { + try { + if (updateConfigRef) { + await updateConfigRef(payload); + } + } catch (error) { + console.error('[ProfileEditor] Failed to save:', error); + } + }, + EDITOR_DEBOUNCE_TIME, + { leading: false, maxWait: EDITOR_MAX_WAIT, trailing: true }, + ); return { ...initialState, @@ -115,10 +114,8 @@ export const store: (initState?: Partial) => StateCreator = const { editor } = get(); if (!editor) return; - // Create debounced save on first use - if (!debouncedSave) { - debouncedSave = createDebouncedSave(get, updateConfig); - } + // Always update ref to use the latest callback + updateConfigRef = updateConfig; try { const markdownContent = (editor.getDocument('markdown') as unknown as string) || ''; diff --git a/src/app/[variants]/(main)/group/_layout/GroupIdSync.tsx b/src/app/[variants]/(main)/group/_layout/GroupIdSync.tsx index f7cb1f0260..fc24dd05c6 100644 --- a/src/app/[variants]/(main)/group/_layout/GroupIdSync.tsx +++ b/src/app/[variants]/(main)/group/_layout/GroupIdSync.tsx @@ -2,6 +2,7 @@ import { useUnmount } from 'ahooks'; import { useParams } from 'react-router-dom'; import { createStoreUpdater } from 'zustand-utils'; +import { useQueryRoute } from '@/hooks/useQueryRoute'; import { useAgentGroupStore } from '@/store/agentGroup'; import { useChatStore } from '@/store/chat'; @@ -9,14 +10,18 @@ const GroupIdSync = () => { const useAgentGroupStoreUpdater = createStoreUpdater(useAgentGroupStore); const useChatStoreUpdater = createStoreUpdater(useChatStore); const params = useParams<{ gid?: string }>(); + const router = useQueryRoute(); // Sync groupId to agentGroupStore and chatStore useAgentGroupStoreUpdater('activeGroupId', params.gid); useChatStoreUpdater('activeGroupId', params.gid); + // Inject router to agentGroupStore for navigation + useAgentGroupStoreUpdater('router', router); + // Clear activeGroupId when unmounting (leaving group page) useUnmount(() => { - useAgentGroupStore.setState({ activeGroupId: undefined }); + useAgentGroupStore.setState({ activeGroupId: undefined, router: undefined }); useChatStore.setState({ activeGroupId: undefined, activeTopicId: undefined }); }); diff --git a/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/AgentProfilePopup.tsx b/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/AgentProfilePopup.tsx index c626cf301c..8e79110a37 100644 --- a/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/AgentProfilePopup.tsx +++ b/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/AgentProfilePopup.tsx @@ -1,10 +1,12 @@ 'use client'; import type { AgentItem } from '@lobechat/types'; -import { Avatar, Center, Flexbox, Popover, Text, Tooltip } from '@lobehub/ui'; +import { ActionIcon, Avatar, Center, Flexbox, Popover, Text, Tooltip } from '@lobehub/ui'; import { createStaticStyles, cssVar } from 'antd-style'; +import { Settings } from 'lucide-react'; import { type PropsWithChildren, memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; import { DEFAULT_AVATAR } from '@/const/meta'; import ModelSelect from '@/features/ModelSelect'; @@ -73,6 +75,7 @@ interface AgentProfilePopupProps extends PropsWithChildren { const AgentProfilePopup = memo(({ agent, groupId, children }) => { const { t } = useTranslation('chat'); + const navigate = useNavigate(); const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); @@ -90,10 +93,10 @@ const AgentProfilePopup = memo(({ agent, groupId, childr } }; - // const handleChat = () => { - // setOpen(false); - // onChat(); - // }; + const handleSettings = () => { + setOpen(false); + navigate(`/group/${groupId}/profile?tab=${agent.id}`); + }; const content = ( @@ -127,9 +130,26 @@ const AgentProfilePopup = memo(({ agent, groupId, childr }} /> - - {agent.title || t('defaultSession', { ns: 'common' })} - + + + {agent.title || t('defaultSession', { ns: 'common' })} + + + {/* Settings Button */} + + + + {agent.description && ( @@ -149,18 +169,6 @@ const AgentProfilePopup = memo(({ agent, groupId, childr value={{ model: agent.model!, provider: agent.provider! }} /> - - {/* Actions */} - {/**/} - {/* }*/} - {/* onClick={handleChat}*/} - {/* type="primary"*/} - {/* >*/} - {/* {t('groupSidebar.agentProfile.chat')}*/} - {/* */} - {/**/} ); @@ -171,7 +179,7 @@ const AgentProfilePopup = memo(({ agent, groupId, childr open={open} placement="right" styles={{ - content: { overflow: 'hidden', padding: 0 }, + content: { borderRadius: 12, overflow: 'hidden', padding: 0 }, }} trigger="click" > diff --git a/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx b/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx index 3480afa48a..44e50a1f09 100644 --- a/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx +++ b/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx @@ -109,6 +109,7 @@ const GroupMember = memo(({ addModalOpen, onAddModalOpenChange } avatar={item.avatar || DEFAULT_AVATAR} background={item.backgroundColor ?? undefined} + isExternal={!item.virtual} title={item.title || t('defaultSession', { ns: 'common' })} />
diff --git a/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMemberItem.tsx b/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMemberItem.tsx index e8ca47b324..2c979c7f23 100644 --- a/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMemberItem.tsx +++ b/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMemberItem.tsx @@ -1,7 +1,8 @@ 'use client'; -import { Avatar } from '@lobehub/ui'; +import { Avatar, Flexbox, Tag } from '@lobehub/ui'; import { type ReactNode, memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { DEFAULT_AVATAR } from '@/const/meta'; import NavItem from '@/features/NavPanel/components/NavItem'; @@ -10,26 +11,42 @@ interface GroupMemberItemProps { actions?: ReactNode; avatar?: string; background?: string; + isExternal?: boolean; onClick?: () => void; title: string; } -const GroupMemberItem = memo(({ title, avatar, background, actions }) => { - return ( - - } - title={title} - /> - ); -}); +const GroupMemberItem = memo( + ({ title, avatar, background, actions, isExternal }) => { + const { t } = useTranslation('chat'); + + return ( + + } + title={ + + + {title} + + {isExternal && ( + + {t('group.profile.external')} + + )} + + } + /> + ); + }, +); export default GroupMemberItem; diff --git a/src/app/[variants]/(main)/group/_layout/Sidebar/Header/AddTopicButon.tsx b/src/app/[variants]/(main)/group/_layout/Sidebar/Header/AddTopicButon.tsx index 3f82c8104e..26f41c502e 100644 --- a/src/app/[variants]/(main)/group/_layout/Sidebar/Header/AddTopicButon.tsx +++ b/src/app/[variants]/(main)/group/_layout/Sidebar/Header/AddTopicButon.tsx @@ -4,12 +4,9 @@ import { ActionIcon } from '@lobehub/ui'; import { MessageSquarePlusIcon } from 'lucide-react'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import urlJoin from 'url-join'; import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens'; -import { useQueryRoute } from '@/hooks/useQueryRoute'; import { useAgentGroupStore } from '@/store/agentGroup'; -import { useChatStore } from '@/store/chat'; import { useUserStore } from '@/store/user'; import { settingsSelectors } from '@/store/user/selectors'; import { HotkeyEnum } from '@/types/hotkey'; @@ -17,17 +14,12 @@ import { HotkeyEnum } from '@/types/hotkey'; const AddTopicButon = memo(() => { const { t } = useTranslation('topic'); const hotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.SaveTopic)); - const activeGroupId = useAgentGroupStore((s) => s.activeGroupId); - const router = useQueryRoute(); + const switchToNewTopic = useAgentGroupStore((s) => s.switchToNewTopic); return ( { - if (!activeGroupId) return; - useChatStore.setState({ activeTopicId: undefined }); - router.push(urlJoin('/group', activeGroupId), { query: { thread: null, topic: null } }); - }} + onClick={switchToNewTopic} size={DESKTOP_HEADER_ICON_SIZE} title={t('actions.addNewTopic')} tooltipProps={{ diff --git a/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx b/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx index 987cb7197f..c888cf0d4d 100644 --- a/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx +++ b/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx @@ -2,7 +2,7 @@ import { Flexbox } from '@lobehub/ui'; import { BotPromptIcon } from '@lobehub/ui/icons'; -import { SearchIcon } from 'lucide-react'; +import { MessageSquarePlusIcon, SearchIcon } from 'lucide-react'; import { usePathname } from 'next/navigation'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -11,12 +11,14 @@ import urlJoin from 'url-join'; import NavItem from '@/features/NavPanel/components/NavItem'; import { useQueryRoute } from '@/hooks/useQueryRoute'; +import { useAgentGroupStore } from '@/store/agentGroup'; import { useChatStore } from '@/store/chat'; import { useGlobalStore } from '@/store/global'; import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; const Nav = memo(() => { const { t } = useTranslation('chat'); + const { t: tTopic } = useTranslation('topic'); const params = useParams(); const groupId = params.gid; const pathname = usePathname(); @@ -25,15 +27,21 @@ const Nav = memo(() => { const { isAgentEditable } = useServerConfigStore(featureFlagsSelectors); const toggleCommandMenu = useGlobalStore((s) => s.toggleCommandMenu); const switchTopic = useChatStore((s) => s.switchTopic); + const switchToNewTopic = useAgentGroupStore((s) => s.switchToNewTopic); return ( + {isAgentEditable && ( { - switchTopic(undefined, true); + switchTopic(null, { skipRefreshMessage: true }); router.push(urlJoin('/group', groupId!, 'profile')); }} title={t('tab.groupProfile')} diff --git a/src/app/[variants]/(main)/group/_layout/Sidebar/Header/index.tsx b/src/app/[variants]/(main)/group/_layout/Sidebar/Header/index.tsx index 6a0f066772..7cda3110d7 100644 --- a/src/app/[variants]/(main)/group/_layout/Sidebar/Header/index.tsx +++ b/src/app/[variants]/(main)/group/_layout/Sidebar/Header/index.tsx @@ -4,14 +4,13 @@ import { type PropsWithChildren, memo } from 'react'; import SideBarHeaderLayout from '@/features/NavPanel/SideBarHeaderLayout'; -import AddTopicButon from './AddTopicButon'; import Agent from './Agent'; import Nav from './Nav'; const HeaderInfo = memo(() => { return ( <> - } right={} /> + } />