mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-25 20:02:47 +00:00
perf: agent config
This commit is contained in:
parent
7bddef488e
commit
7088543a9f
|
|
@ -10,7 +10,7 @@ export enum AppTypeEnum {
|
|||
folder = 'folder',
|
||||
toolFolder = 'toolFolder',
|
||||
simple = 'simple',
|
||||
agent = 'agent',
|
||||
chatAgent = 'chatAgent',
|
||||
workflow = 'advanced',
|
||||
workflowTool = 'plugin',
|
||||
mcpToolSet = 'toolSet', // 'mcp'
|
||||
|
|
@ -33,7 +33,7 @@ export const ToolTypeList = [
|
|||
AppTypeEnum.httpToolSet,
|
||||
AppTypeEnum.workflowTool
|
||||
];
|
||||
export const AppTypeList = [AppTypeEnum.simple, AppTypeEnum.agent, AppTypeEnum.workflow];
|
||||
export const AppTypeList = [AppTypeEnum.simple, AppTypeEnum.chatAgent, AppTypeEnum.workflow];
|
||||
|
||||
export const defaultTTSConfig: AppTTSConfigType = { type: 'web' };
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import { Output_Template_Error_Message } from '@fastgpt/global/core/workflow/tem
|
|||
import { splitCombineToolId } from '@fastgpt/global/core/app/tool/utils';
|
||||
import { getMCPToolRuntimeNode } from '@fastgpt/global/core/app/tool/mcpTool/utils';
|
||||
import { getHTTPToolRuntimeNode } from '@fastgpt/global/core/app/tool/httpTool/utils';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { getMCPChildren } from '../mcp';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { UserError } from '@fastgpt/global/common/error/utils';
|
||||
|
|
@ -227,6 +227,7 @@ export async function getChildAppPreviewNode({
|
|||
if (source === AppToolSourceEnum.personal) {
|
||||
const item = await MongoApp.findById(pluginId).lean();
|
||||
if (!item) return Promise.reject(PluginErrEnum.unExist);
|
||||
if (AppFolderTypeList.includes(item.type)) return Promise.reject(PluginErrEnum.unExist);
|
||||
|
||||
const version = await getAppVersionById({ appId: pluginId, versionId, app: item });
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export const checkTeamMemberLimit = async (teamId: string, newCount: number) =>
|
|||
|
||||
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
||||
const type = [
|
||||
AppTypeEnum.agent,
|
||||
AppTypeEnum.chatAgent,
|
||||
AppTypeEnum.simple,
|
||||
AppTypeEnum.workflow,
|
||||
AppTypeEnum.workflowTool,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { retryFn } from '@fastgpt/global/common/system/utils';
|
|||
export function getI18nAppType(type: AppTypeEnum): string {
|
||||
if (type === AppTypeEnum.folder) return i18nT('account_team:type.Folder');
|
||||
if (type === AppTypeEnum.simple) return i18nT('app:type.Chat_Agent');
|
||||
if (type === AppTypeEnum.agent) return 'Agent';
|
||||
if (type === AppTypeEnum.chatAgent) return 'Agent';
|
||||
if (type === AppTypeEnum.workflow) return i18nT('account_team:type.Workflow bot');
|
||||
if (type === AppTypeEnum.workflowTool) return i18nT('app:toolType_workflow');
|
||||
if (type === AppTypeEnum.httpPlugin) return i18nT('account_team:type.Http plugin');
|
||||
|
|
|
|||
|
|
@ -30,10 +30,9 @@ import { useTranslation } from 'next-i18next';
|
|||
export type SkillOptionItemType = {
|
||||
description?: string;
|
||||
list: SkillItemType[];
|
||||
|
||||
onSelect?: (id: string) => Promise<SkillOptionItemType | undefined>;
|
||||
onClick?: (id: string) => Promise<string | undefined>;
|
||||
onFolderLoad?: (id: string) => Promise<SkillItemType[] | undefined>;
|
||||
onFolderLoad?: (id: string) => Promise<SkillItemType[]>;
|
||||
};
|
||||
|
||||
export type SkillItemType = {
|
||||
|
|
@ -41,12 +40,16 @@ export type SkillItemType = {
|
|||
id: string;
|
||||
label: string;
|
||||
icon?: string;
|
||||
showArrow?: boolean;
|
||||
canOpen?: boolean;
|
||||
canUse?: boolean;
|
||||
canClick: boolean;
|
||||
|
||||
// Folder
|
||||
open?: boolean;
|
||||
children?: SkillOptionItemType;
|
||||
isFolder?: boolean;
|
||||
folderChildren?: SkillItemType[];
|
||||
|
||||
// System tool/ model
|
||||
showArrow?: boolean;
|
||||
children?: SkillOptionItemType;
|
||||
};
|
||||
|
||||
export default function SkillPickerPlugin({
|
||||
|
|
@ -75,7 +78,6 @@ export default function SkillPickerPlugin({
|
|||
const [currentColumnIndex, setCurrentColumnIndex] = useState<number>(0);
|
||||
const [currentRowIndex, setCurrentRowIndex] = useState<number>(0);
|
||||
const [interactionMode, setInteractionMode] = useState<'mouse' | 'keyboard'>('mouse');
|
||||
const [loadingFolderIds, setLoadingFolderIds] = useState(new Set());
|
||||
|
||||
// Refs for scroll management
|
||||
const itemRefs = useRef<Map<string, HTMLDivElement>>(new Map());
|
||||
|
|
@ -105,10 +107,6 @@ export default function SkillPickerPlugin({
|
|||
}
|
||||
}, []);
|
||||
|
||||
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('@', {
|
||||
minLength: 0
|
||||
});
|
||||
|
||||
// Recursively collects all visible items including expanded folder children for keyboard navigation
|
||||
const getFlattenedVisibleItems = useCallback(
|
||||
(columnIndex: number): SkillItemType[] => {
|
||||
|
|
@ -119,7 +117,7 @@ export default function SkillPickerPlugin({
|
|||
items.forEach((item) => {
|
||||
result.push(item);
|
||||
// Include folder children only if folder is expanded
|
||||
if (item.canOpen && item.open && item.folderChildren) {
|
||||
if (item.isFolder && item.open && item.folderChildren) {
|
||||
result.push(...flatten(item.folderChildren));
|
||||
}
|
||||
});
|
||||
|
|
@ -142,7 +140,8 @@ export default function SkillPickerPlugin({
|
|||
item?: SkillItemType;
|
||||
option?: SkillOptionItemType;
|
||||
}) => {
|
||||
if (!item) return;
|
||||
if (!item || !option?.onSelect) return;
|
||||
|
||||
const buffer = item.children;
|
||||
if (buffer) {
|
||||
setSkillOptions((prev) => {
|
||||
|
|
@ -153,7 +152,7 @@ export default function SkillPickerPlugin({
|
|||
return;
|
||||
}
|
||||
|
||||
const result = await option?.onSelect?.(item.id);
|
||||
const result = await option.onSelect(item.id);
|
||||
|
||||
setSkillOptions((prev) => {
|
||||
const newOptions = [...prev];
|
||||
|
|
@ -173,11 +172,14 @@ export default function SkillPickerPlugin({
|
|||
// Handle item click (confirm selection)
|
||||
const { runAsync: handleItemClick, loading: isItemClickLoading } = useRequest2(
|
||||
async ({ item, option }: { item: SkillItemType; option?: SkillOptionItemType }) => {
|
||||
if (!item.canClick || !option?.onClick) return;
|
||||
|
||||
// Step 1: Execute async onClick to get skillId (outside editor.update)
|
||||
const skillId = await option?.onClick?.(item.id);
|
||||
const skillId = await option.onClick(item.id);
|
||||
|
||||
// Step 2: Update editor with the skillId (inside a fresh editor.update)
|
||||
if (skillId) {
|
||||
console.log(skillId, 2222);
|
||||
editor.update(() => {
|
||||
// Re-acquire selection in this update cycle to avoid stale node references
|
||||
const selection = $getSelection();
|
||||
|
|
@ -213,7 +215,8 @@ export default function SkillPickerPlugin({
|
|||
);
|
||||
|
||||
// Handle folder toggle
|
||||
const { runAsync: handleFolderToggle, loading: isFolderLoading } = useRequest2(
|
||||
const [loadingFolderIds, setLoadingFolderIds] = useState(new Set());
|
||||
const { runAsync: handleFolderToggle } = useRequest2(
|
||||
async ({
|
||||
currentColumnIndex,
|
||||
item,
|
||||
|
|
@ -223,7 +226,7 @@ export default function SkillPickerPlugin({
|
|||
item?: SkillItemType;
|
||||
option?: SkillOptionItemType;
|
||||
}) => {
|
||||
if (!item || !item.canOpen) return;
|
||||
if (!item || !item.isFolder || !option?.onFolderLoad) return;
|
||||
const currentFolder = item;
|
||||
|
||||
// Step 1: Toggle folder open/closed state
|
||||
|
|
@ -260,7 +263,7 @@ export default function SkillPickerPlugin({
|
|||
});
|
||||
|
||||
try {
|
||||
const result = await option?.onFolderLoad?.(currentFolder.id);
|
||||
const result = await option.onFolderLoad(currentFolder.id);
|
||||
|
||||
setSkillOptions((prev) => {
|
||||
const newOptions = [...prev];
|
||||
|
|
@ -271,7 +274,7 @@ export default function SkillPickerPlugin({
|
|||
if (item.id === currentFolder.id) {
|
||||
return {
|
||||
...item,
|
||||
folderChildren: result || []
|
||||
folderChildren: result
|
||||
};
|
||||
}
|
||||
if (item.folderChildren) {
|
||||
|
|
@ -482,6 +485,9 @@ export default function SkillPickerPlugin({
|
|||
const removeSpaceCommand = editor.registerCommand(
|
||||
KEY_SPACE_COMMAND,
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (!isMenuOpen) return true;
|
||||
|
||||
setInteractionMode('keyboard');
|
||||
|
|
@ -490,9 +496,7 @@ export default function SkillPickerPlugin({
|
|||
const latestItem = flattenedItems[currentRowIndex];
|
||||
const latestOption = skillOptions[currentColumnIndex];
|
||||
|
||||
if (latestItem?.canOpen && !(latestItem.open && latestItem.folderChildren?.length === 0)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!(latestItem.open && latestItem.folderChildren?.length === 0)) {
|
||||
handleFolderToggle({
|
||||
currentColumnIndex,
|
||||
item: latestItem,
|
||||
|
|
@ -509,6 +513,9 @@ export default function SkillPickerPlugin({
|
|||
const removeEnterCommand = editor.registerCommand(
|
||||
KEY_ENTER_COMMAND,
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (!isMenuOpen) return true;
|
||||
|
||||
setInteractionMode('keyboard');
|
||||
|
|
@ -517,9 +524,7 @@ export default function SkillPickerPlugin({
|
|||
const latestItem = flattenedItems[currentRowIndex];
|
||||
const latestOption = skillOptions[currentColumnIndex];
|
||||
|
||||
if (latestItem?.canUse && latestOption) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (latestOption?.onClick) {
|
||||
handleItemClick({ item: latestItem, option: latestOption });
|
||||
|
||||
return true;
|
||||
|
|
@ -565,7 +570,6 @@ export default function SkillPickerPlugin({
|
|||
const result: JSX.Element[] = [];
|
||||
const activeRowIndex = selectedRowIndex[columnIndex];
|
||||
let currentFlatIndex = startFlatIndex;
|
||||
console.log('items', { selectedRowIndex, columnIndex, activeRowIndex });
|
||||
|
||||
items.forEach((item) => {
|
||||
const flatIndex = currentFlatIndex;
|
||||
|
|
@ -596,7 +600,6 @@ export default function SkillPickerPlugin({
|
|||
color={isSelected ? 'primary.700' : 'myGray.600'}
|
||||
display={'flex'}
|
||||
alignItems={'center'}
|
||||
isLoading={loadingFolderIds.has(item.id)}
|
||||
size={'sm'}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -609,13 +612,13 @@ export default function SkillPickerPlugin({
|
|||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (item.canOpen) {
|
||||
if (item.isFolder) {
|
||||
handleFolderToggle({
|
||||
currentColumnIndex: columnIndex,
|
||||
item,
|
||||
option: columnData
|
||||
});
|
||||
} else if (item.canUse) {
|
||||
} else {
|
||||
handleItemClick({
|
||||
item,
|
||||
option: columnData
|
||||
|
|
@ -639,18 +642,16 @@ export default function SkillPickerPlugin({
|
|||
|
||||
setCurrentRowIndex(flatIndex);
|
||||
setCurrentColumnIndex(columnIndex);
|
||||
if (item.canUse) {
|
||||
handleItemSelect({
|
||||
currentColumnIndex: columnIndex,
|
||||
item,
|
||||
option: columnData
|
||||
});
|
||||
}
|
||||
handleItemSelect({
|
||||
currentColumnIndex: columnIndex,
|
||||
item,
|
||||
option: columnData
|
||||
});
|
||||
}}
|
||||
>
|
||||
{item.canOpen && !(item.open && item.folderChildren?.length === 0) ? (
|
||||
{item.isFolder && !(item.open && item.folderChildren?.length === 0) ? (
|
||||
<MyIcon
|
||||
name={'core/chat/chevronRight'}
|
||||
name={loadingFolderIds.has(item.id) ? 'common/loading' : 'core/chat/chevronRight'}
|
||||
w={4}
|
||||
color={'myGray.500'}
|
||||
transform={item.open ? 'rotate(90deg)' : 'none'}
|
||||
|
|
@ -661,9 +662,11 @@ export default function SkillPickerPlugin({
|
|||
<Box w={3} flexShrink={0} />
|
||||
) : null}
|
||||
{item.icon && <Avatar src={item.icon} w={'1.2rem'} borderRadius={'xs'} />}
|
||||
<Box fontSize={'sm'} fontWeight={'medium'} flex={1}>
|
||||
|
||||
{/* Folder content */}
|
||||
<Box fontSize={'sm'} fontWeight={'medium'} flex={'1 0 0'} className="textEllipsis">
|
||||
{item.label}
|
||||
{item.canOpen && item.open && item.folderChildren?.length === 0 && (
|
||||
{item.isFolder && item.open && item.folderChildren?.length === 0 && (
|
||||
<Box as="span" color={'myGray.400'} fontSize={'xs'} ml={2}>
|
||||
{t('app:empty_folder')}
|
||||
</Box>
|
||||
|
|
@ -676,7 +679,7 @@ export default function SkillPickerPlugin({
|
|||
);
|
||||
|
||||
// render folderChildren
|
||||
if (item.canOpen && item.open && !!item.folderChildren && item.folderChildren.length > 0) {
|
||||
if (item.isFolder && item.open && !!item.folderChildren && item.folderChildren.length > 0) {
|
||||
const { elements, nextFlatIndex } = renderItemList(
|
||||
item.folderChildren,
|
||||
columnData,
|
||||
|
|
@ -695,14 +698,14 @@ export default function SkillPickerPlugin({
|
|||
selectedRowIndex,
|
||||
currentColumnIndex,
|
||||
currentRowIndex,
|
||||
loadingFolderIds,
|
||||
t,
|
||||
interactionMode,
|
||||
handleFolderToggle,
|
||||
handleItemClick,
|
||||
handleItemSelect,
|
||||
interactionMode,
|
||||
loadingFolderIds
|
||||
handleItemSelect
|
||||
]
|
||||
);
|
||||
|
||||
// Render single column
|
||||
const renderColumn = useCallback(
|
||||
(columnData: SkillOptionItemType, columnIndex: number) => {
|
||||
|
|
@ -719,7 +722,7 @@ export default function SkillPickerPlugin({
|
|||
boxShadow={'0 4px 10px 0 rgba(19, 51, 107, 0.10), 0 0 1px 0 rgba(19, 51, 107, 0.10)'}
|
||||
bg={'white'}
|
||||
flexShrink={0}
|
||||
maxH={'300px'}
|
||||
maxH={'350px'}
|
||||
overflow={'auto'}
|
||||
>
|
||||
{columnData.description && (
|
||||
|
|
@ -785,6 +788,9 @@ export default function SkillPickerPlugin({
|
|||
},
|
||||
[editor]
|
||||
);
|
||||
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('@', {
|
||||
minLength: 0
|
||||
});
|
||||
|
||||
return (
|
||||
<LexicalTypeaheadMenuPlugin
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
"auto_save": "Auto save",
|
||||
"can_select_toolset": "Entire toolset available for selection",
|
||||
"change_app_type": "Change App Type",
|
||||
"chat_agent_intro": "AI autonomously plans executable processes",
|
||||
"chat_debug": "Chat Preview",
|
||||
"chat_logs": "Logs",
|
||||
"chat_logs_tips": "Logs will record the online, shared, and API (requires chatId) conversation records of this app.",
|
||||
|
|
@ -254,6 +255,7 @@
|
|||
"month.unit": "Day",
|
||||
"move.hint": "After moving, the selected app/folder will inherit the permission settings for the new folder.",
|
||||
"move_app": "Move Application",
|
||||
"my_agents": "my agents",
|
||||
"no_mcp_tools_list": "No data yet, the MCP address needs to be parsed first",
|
||||
"node_not_intro": "This node is not introduced",
|
||||
"not_json_file": "Please select a JSON file",
|
||||
|
|
@ -440,6 +442,7 @@
|
|||
"tts_close": "Close",
|
||||
"type.All": "All",
|
||||
"type.Chat_Agent": "Dialogue Agent",
|
||||
"type.Chat_Agent_v2": "Dialogue Agent V2",
|
||||
"type.Create http plugin tip": "Batch create plugins through OpenAPI Schema, compatible with GPTs format.",
|
||||
"type.Create mcp tools tip": "Automatically parse and batch create callable MCP tools by entering the MCP address",
|
||||
"type.Create one plugin tip": "Customizable input and output workflows, usually used to encapsulate reusable workflows.",
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@
|
|||
"auto_save": "自动保存",
|
||||
"can_select_toolset": "可选择整个工具集",
|
||||
"change_app_type": "更改应用类型",
|
||||
"chat_agent_intro": "由 AI 自主规划可执行流程",
|
||||
"chat_debug": "调试预览",
|
||||
"chat_logs": "对话日志",
|
||||
"chat_logs_tips": "日志会记录该应用的在线、分享和 API(需填写 chatId)对话记录",
|
||||
|
|
@ -267,6 +268,7 @@
|
|||
"month.unit": "号",
|
||||
"move.hint": "移动后,所选应用/文件夹将继承新文件夹的权限设置。",
|
||||
"move_app": "移动应用",
|
||||
"my_agents": "我的 agents",
|
||||
"no_mcp_tools_list": "暂无数据,需先解析 MCP 地址",
|
||||
"node_not_intro": "这个节点没有介绍",
|
||||
"not_json_file": "请选择JSON文件",
|
||||
|
|
@ -457,6 +459,7 @@
|
|||
"tts_close": "关闭",
|
||||
"type.All": "全部",
|
||||
"type.Chat_Agent": "对话 Agent",
|
||||
"type.Chat_Agent_v2": "对话 Agent V2",
|
||||
"type.Create http plugin tip": "通过 OpenAPI Schema 批量创建工具(兼容GPTs)、通过curl或手动创建工具",
|
||||
"type.Create http toolset tip": "通过 OpenAPI Schema 批量创建工具(兼容GPTs)",
|
||||
"type.Create mcp tools tip": "通过输入 MCP 地址,自动解析并批量创建可调用的 MCP 工具",
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
"auto_save": "自動儲存",
|
||||
"can_select_toolset": "可選擇整個工具集",
|
||||
"change_app_type": "更改應用程式類型",
|
||||
"chat_agent_intro": "由 AI 自主規劃可執行流程",
|
||||
"chat_debug": "聊天預覽",
|
||||
"chat_logs": "對話紀錄",
|
||||
"chat_logs_tips": "紀錄會記錄此應用程式的線上、分享和 API(需填寫 chatId)對話紀錄",
|
||||
|
|
@ -253,6 +254,7 @@
|
|||
"month.unit": "號",
|
||||
"move.hint": "移動後,所選應用/文件夾將繼承新文件夾的權限設置。",
|
||||
"move_app": "移動應用程式",
|
||||
"my_agents": "我的 agents",
|
||||
"no_mcp_tools_list": "暫無數據,需先解析 MCP 地址",
|
||||
"node_not_intro": "這個節點沒有介紹",
|
||||
"not_json_file": "請選擇 JSON 檔案",
|
||||
|
|
@ -437,6 +439,7 @@
|
|||
"tts_browser": "瀏覽器自帶 (免費)",
|
||||
"tts_close": "關閉",
|
||||
"type.All": "全部",
|
||||
"type.Chat_Agent_v2": "對話 Agent V2",
|
||||
"type.Create http plugin tip": "透過 OpenAPI Schema 批次建立外掛,相容 GPTs 格式",
|
||||
"type.Create mcp tools tip": "通過輸入 MCP 地址,自動解析並批量創建可調用的 MCP 工具",
|
||||
"type.Create one plugin tip": "可以自訂輸入和輸出的工作流程,通常用於封裝重複使用的工作流程",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
|||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
|
||||
export const createAppTypeMap = {
|
||||
[AppTypeEnum.agent]: {
|
||||
type: AppTypeEnum.agent,
|
||||
icon: 'core/app/type/workflowFill',
|
||||
title: 'Agent',
|
||||
intro: 'Agent',
|
||||
description: 'Agent',
|
||||
imgUrl: '/imgs/app/workflowPreview.svg'
|
||||
[AppTypeEnum.chatAgent]: {
|
||||
type: AppTypeEnum.chatAgent,
|
||||
icon: 'core/app/simpleBot',
|
||||
title: i18nT('app:type.Chat_Agent_v2'),
|
||||
intro: i18nT('app:chat_agent_intro'),
|
||||
description: i18nT('app:chat_agent_intro'),
|
||||
imgUrl: '/imgs/app/simpleAgentPreview.svg'
|
||||
},
|
||||
[AppTypeEnum.workflow]: {
|
||||
type: AppTypeEnum.workflow,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import { getWebLLMModel } from '@/web/common/system/utils';
|
|||
import ToolSelect from '../FormComponent/ToolSelector/ToolSelect';
|
||||
import OptimizerPopover from '@/components/common/PromptEditor/OptimizerPopover';
|
||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { useSkillManager } from './hooks/useSkillManager';
|
||||
import { type SelectedToolItemType, useSkillManager } from './hooks/useSkillManager';
|
||||
import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
|
||||
|
||||
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
|
||||
|
|
@ -76,18 +76,36 @@ const EditForm = ({
|
|||
|
||||
// Skill picker
|
||||
const selectedTools = useMemoEnhance(() => appForm.selectedTools, [appForm.selectedTools]);
|
||||
const setSelectedTools = useCallback(
|
||||
(tools: FlowNodeTemplateType[]) => {
|
||||
const onUpdateOrAddTool = useCallback(
|
||||
(tool: SelectedToolItemType) => {
|
||||
setAppForm((state) => {
|
||||
if (state.selectedTools.some((t) => t.id === tool.id)) {
|
||||
return {
|
||||
...state,
|
||||
selectedTools: state.selectedTools.map((t) => (t.id === tool.id ? tool : t))
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
selectedTools: [tool, ...state.selectedTools]
|
||||
};
|
||||
});
|
||||
},
|
||||
[setAppForm]
|
||||
);
|
||||
const onDeleteTool = useCallback(
|
||||
(id: string) => {
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
selectedTools: tools
|
||||
selectedTools: state.selectedTools.filter((t) => t.id !== id)
|
||||
}));
|
||||
},
|
||||
[setAppForm]
|
||||
);
|
||||
const { SkillModal, skillOption, selectedSkills, onClickSkill, onRemoveSkill } = useSkillManager({
|
||||
selectedTools,
|
||||
setSelectedTools,
|
||||
onUpdateOrAddTool,
|
||||
onDeleteTool,
|
||||
canSelectFile: appForm.chatConfig?.fileSelectConfig?.canSelectFile,
|
||||
canSelectImg: appForm.chatConfig?.fileSelectConfig?.canSelectImg
|
||||
});
|
||||
|
|
@ -24,7 +24,12 @@ import {
|
|||
getToolPreviewNode,
|
||||
getTeamAppTemplates
|
||||
} from '@/web/core/app/api/tool';
|
||||
import { AppTypeEnum, AppTypeList, ToolTypeList } from '@fastgpt/global/core/app/constants';
|
||||
import {
|
||||
AppFolderTypeList,
|
||||
AppTypeEnum,
|
||||
AppTypeList,
|
||||
ToolTypeList
|
||||
} from '@fastgpt/global/core/app/constants';
|
||||
|
||||
const ConfigToolModal = dynamic(() => import('../../component/ConfigToolModal'));
|
||||
|
||||
|
|
@ -38,16 +43,18 @@ const isSubApp = (flowNodeType: FlowNodeTypeEnum) => {
|
|||
return !!subAppTypeMap[flowNodeType];
|
||||
};
|
||||
|
||||
type SelectedToolItemType = AppFormEditFormType['selectedTools'][number];
|
||||
export type SelectedToolItemType = AppFormEditFormType['selectedTools'][number];
|
||||
|
||||
export const useSkillManager = ({
|
||||
selectedTools,
|
||||
setSelectedTools,
|
||||
onUpdateOrAddTool,
|
||||
onDeleteTool,
|
||||
canSelectFile,
|
||||
canSelectImg
|
||||
}: {
|
||||
selectedTools: SelectedToolItemType[];
|
||||
setSelectedTools: (tools: SelectedToolItemType[]) => void;
|
||||
onDeleteTool: (id: string) => void;
|
||||
onUpdateOrAddTool: (tool: SelectedToolItemType) => void;
|
||||
canSelectFile?: boolean;
|
||||
canSelectImg?: boolean;
|
||||
}) => {
|
||||
|
|
@ -66,7 +73,8 @@ export const useSkillManager = ({
|
|||
parentId: item.parentId,
|
||||
label: item.name,
|
||||
icon: item.avatar,
|
||||
showArrow: item.isFolder
|
||||
showArrow: item.isFolder,
|
||||
canClick: true
|
||||
};
|
||||
});
|
||||
},
|
||||
|
|
@ -83,15 +91,10 @@ export const useSkillManager = ({
|
|||
[systemTools]
|
||||
);
|
||||
|
||||
/* ===== Team Apps ===== */
|
||||
const { data: allTeamApps = [] } = useRequest2(
|
||||
async () => {
|
||||
return await getTeamAppTemplates({ parentId: null });
|
||||
},
|
||||
{
|
||||
manual: false
|
||||
}
|
||||
);
|
||||
/* ===== Team agents/tools ===== */
|
||||
const { data: allTeamApps = [] } = useRequest2(() => getTeamAppTemplates({ parentId: null }), {
|
||||
manual: false
|
||||
});
|
||||
const myTools = useMemo(
|
||||
() =>
|
||||
allTeamApps
|
||||
|
|
@ -100,12 +103,12 @@ export const useSkillManager = ({
|
|||
id: item.id,
|
||||
label: item.name,
|
||||
icon: item.avatar,
|
||||
canOpen: item.isFolder ?? false,
|
||||
canUse: item.appType !== AppTypeEnum.folder && item.appType !== AppTypeEnum.toolFolder
|
||||
isFolder: item.isFolder ?? false,
|
||||
canClick: !AppFolderTypeList.includes(item.appType)
|
||||
})),
|
||||
[allTeamApps]
|
||||
);
|
||||
const agentApps = useMemo(
|
||||
const myAgents = useMemo(
|
||||
() =>
|
||||
allTeamApps
|
||||
.filter((item) => [AppTypeEnum.folder, ...AppTypeList].includes(item.appType))
|
||||
|
|
@ -113,8 +116,8 @@ export const useSkillManager = ({
|
|||
id: item.id,
|
||||
label: item.name,
|
||||
icon: item.avatar,
|
||||
canOpen: item.isFolder ?? false,
|
||||
canUse: item.appType !== AppTypeEnum.folder && item.appType !== AppTypeEnum.toolFolder
|
||||
isFolder: item.isFolder ?? false,
|
||||
canClick: !AppFolderTypeList.includes(item.appType)
|
||||
})),
|
||||
[allTeamApps]
|
||||
);
|
||||
|
|
@ -132,16 +135,22 @@ export const useSkillManager = ({
|
|||
id: item.id,
|
||||
label: item.name,
|
||||
icon: item.avatar,
|
||||
canOpen: item.isFolder ?? false,
|
||||
canUse: item.appType !== AppTypeEnum.folder && item.appType !== AppTypeEnum.toolFolder
|
||||
isFolder: item.isFolder ?? false,
|
||||
canClick: !AppFolderTypeList.includes(item.appType)
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
/* ===== Workflow tool ===== */
|
||||
const { runAsync: onAddAppOrTool } = useRequest2(
|
||||
const onAddAppOrTool = useCallback(
|
||||
async (appId: string) => {
|
||||
console.log(appId);
|
||||
const toolTemplate = await getToolPreviewNode({ appId });
|
||||
console.log(toolTemplate);
|
||||
|
||||
if (!toolTemplate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const checkRes = validateToolConfiguration({
|
||||
toolTemplate,
|
||||
canSelectFile,
|
||||
|
|
@ -171,17 +180,14 @@ export const useSkillManager = ({
|
|||
};
|
||||
const hasFormInput = checkNeedsUserConfiguration(tool);
|
||||
|
||||
setSelectedTools([
|
||||
...selectedTools,
|
||||
{
|
||||
...tool,
|
||||
configStatus: hasFormInput ? 'waitingForConfig' : 'active'
|
||||
}
|
||||
]);
|
||||
onUpdateOrAddTool({
|
||||
...tool,
|
||||
configStatus: hasFormInput ? 'waitingForConfig' : 'active'
|
||||
});
|
||||
|
||||
return tool.id;
|
||||
},
|
||||
{ manual: true }
|
||||
[canSelectFile, canSelectImg, onUpdateOrAddTool, t, toast]
|
||||
);
|
||||
|
||||
/* ===== Skill option ===== */
|
||||
|
|
@ -199,7 +205,8 @@ export const useSkillManager = ({
|
|||
onClick: onAddAppOrTool,
|
||||
list: data.map((item) => ({
|
||||
id: item.id,
|
||||
label: item.label
|
||||
label: item.label,
|
||||
canClick: true
|
||||
}))
|
||||
};
|
||||
},
|
||||
|
|
@ -215,7 +222,7 @@ export const useSkillManager = ({
|
|||
} else if (id === 'agent') {
|
||||
return {
|
||||
description: t('app:space_to_expand_folder'),
|
||||
list: agentApps,
|
||||
list: myAgents,
|
||||
onFolderLoad: (folderId: string) => onFolderLoadTeamApps(folderId, AppTypeList),
|
||||
onClick: onAddAppOrTool
|
||||
};
|
||||
|
|
@ -226,21 +233,24 @@ export const useSkillManager = ({
|
|||
{
|
||||
id: 'systemTool',
|
||||
label: t('app:core.module.template.System Tools'),
|
||||
icon: 'core/workflow/template/toolCall'
|
||||
icon: 'core/workflow/template/toolCall',
|
||||
canClick: false
|
||||
},
|
||||
{
|
||||
id: 'myTools',
|
||||
label: t('common:navbar.Tools'),
|
||||
icon: 'core/app/type/pluginFill'
|
||||
icon: 'core/app/type/pluginFill',
|
||||
canClick: false
|
||||
},
|
||||
{
|
||||
id: 'agent',
|
||||
label: 'Agent',
|
||||
icon: 'core/workflow/template/runApp'
|
||||
label: t('app:my_agents'),
|
||||
icon: 'core/workflow/template/runApp',
|
||||
canClick: false
|
||||
}
|
||||
]
|
||||
};
|
||||
}, [onAddAppOrTool, onLoadSystemTool, myTools, agentApps, onFolderLoadTeamApps, t]);
|
||||
}, [onAddAppOrTool, onLoadSystemTool, myTools, myAgents, onFolderLoadTeamApps, t]);
|
||||
|
||||
/* ===== Selected skills ===== */
|
||||
const selectedSkills = useMemoEnhance<SkillLabelItemType[]>(() => {
|
||||
|
|
@ -279,9 +289,9 @@ export const useSkillManager = ({
|
|||
const onRemoveSkill = useCallback(
|
||||
(id: string) => {
|
||||
console.log('onRemoveSkill', id);
|
||||
setSelectedTools(selectedTools.filter((tool) => tool.id !== id));
|
||||
onDeleteTool(id);
|
||||
},
|
||||
[selectedTools, setSelectedTools]
|
||||
[onDeleteTool]
|
||||
);
|
||||
|
||||
const SkillModal = useCallback(() => {
|
||||
|
|
@ -292,22 +302,16 @@ export const useSkillManager = ({
|
|||
configTool={configTool}
|
||||
onCloseConfigTool={() => setConfigTool(undefined)}
|
||||
onAddTool={(tool) =>
|
||||
setSelectedTools(
|
||||
selectedTools.map((t) =>
|
||||
t.id === tool.id
|
||||
? {
|
||||
...tool,
|
||||
configStatus: 'active'
|
||||
}
|
||||
: t
|
||||
)
|
||||
)
|
||||
onUpdateOrAddTool({
|
||||
...tool,
|
||||
configStatus: 'active'
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}, [configTool, selectedTools, setSelectedTools]);
|
||||
}, [configTool, onUpdateOrAddTool]);
|
||||
|
||||
return {
|
||||
skillOption,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import type { AppChatConfigType, AppFormEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import type { WorkflowType } from '../Agent/utils';
|
||||
import type { WorkflowType } from '../ChatAgent/utils';
|
||||
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
|
||||
export type AppForm2WorkflowFnType = ({
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const AppTypeTag = ({ type }: { type: AppTypeEnum }) => {
|
|||
const { t } = useTranslation();
|
||||
|
||||
const map = useRef({
|
||||
[AppTypeEnum.agent]: {
|
||||
[AppTypeEnum.chatAgent]: {
|
||||
label: 'Agent',
|
||||
icon: 'core/app/type/simple',
|
||||
bg: '#DBF3FF',
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const MyApps = () => {
|
|||
const map = useMemo(
|
||||
() => ({
|
||||
all: t('common:core.module.template.all_team_app'),
|
||||
[AppTypeEnum.agent]: 'Agent',
|
||||
[AppTypeEnum.chatAgent]: 'Agent',
|
||||
[AppTypeEnum.simple]: t('app:type.Chat_Agent'),
|
||||
[AppTypeEnum.workflow]: t('app:type.Workflow bot'),
|
||||
[AppTypeEnum.workflowTool]: t('app:toolType_workflow'),
|
||||
|
|
|
|||
|
|
@ -296,7 +296,11 @@ const List = () => {
|
|||
/>
|
||||
}
|
||||
menuList={[
|
||||
...([AppTypeEnum.simple, AppTypeEnum.workflow].includes(app.type)
|
||||
...([
|
||||
AppTypeEnum.simple,
|
||||
AppTypeEnum.workflow,
|
||||
AppTypeEnum.chatAgent
|
||||
].includes(app.type)
|
||||
? [
|
||||
{
|
||||
children: [
|
||||
|
|
@ -500,7 +504,7 @@ const CreateButton = ({ appType }: { appType: AppTypeEnum | 'all' }) => {
|
|||
const parentId = router.query.parentId;
|
||||
const createAppType =
|
||||
createAppTypeMap[appType as CreateAppType]?.type ||
|
||||
(router.pathname.includes('/agent') ? AppTypeEnum.workflow : AppTypeEnum.workflowTool);
|
||||
(router.pathname.includes('/agent') ? AppTypeEnum.chatAgent : AppTypeEnum.workflowTool);
|
||||
const isToolType = ToolTypeList.includes(createAppType);
|
||||
|
||||
return (
|
||||
|
|
@ -572,7 +576,7 @@ const ListCreateButton = ({ appType }: { appType: AppTypeEnum | 'all' }) => {
|
|||
const parentId = router.query.parentId;
|
||||
const createAppType =
|
||||
createAppTypeMap[appType as CreateAppType]?.type ||
|
||||
(router.pathname.includes('/agent') ? AppTypeEnum.workflow : AppTypeEnum.workflowTool);
|
||||
(router.pathname.includes('/agent') ? AppTypeEnum.chatAgent : AppTypeEnum.workflowTool);
|
||||
|
||||
return (
|
||||
<MyBox
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
|
|||
// agent page
|
||||
if (router.pathname.includes('/agent')) {
|
||||
return !type || type === 'all'
|
||||
? [AppTypeEnum.folder, AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.agent]
|
||||
? [AppTypeEnum.folder, AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.chatAgent]
|
||||
: [AppTypeEnum.folder, type];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
|||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
|
||||
export const appTypeTagMap = {
|
||||
[AppTypeEnum.agent]: {
|
||||
label: 'Agent',
|
||||
icon: 'core/app/type/mcpTools'
|
||||
[AppTypeEnum.chatAgent]: {
|
||||
label: i18nT('app:type.Chat_Agent_v2'),
|
||||
icon: 'core/app/type/simple'
|
||||
},
|
||||
[AppTypeEnum.simple]: {
|
||||
label: i18nT('app:type.Chat_Agent'),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const SimpleEdit = dynamic(() => import('@/pageComponents/app/detail/Edit/Simple
|
|||
ssr: false,
|
||||
loading: () => <Loading fixed={false} />
|
||||
});
|
||||
const AgentEdit = dynamic(() => import('@/pageComponents/app/detail/Edit/Agent'), {
|
||||
const AgentEdit = dynamic(() => import('@/pageComponents/app/detail/Edit/ChatAgent'), {
|
||||
ssr: false,
|
||||
loading: () => <Loading fixed={false} />
|
||||
});
|
||||
|
|
@ -61,7 +61,7 @@ const AppDetail = () => {
|
|||
) : (
|
||||
<>
|
||||
{appDetail.type === AppTypeEnum.simple && <SimpleEdit />}
|
||||
{appDetail.type === AppTypeEnum.agent && <AgentEdit />}
|
||||
{appDetail.type === AppTypeEnum.chatAgent && <AgentEdit />}
|
||||
{appDetail.type === AppTypeEnum.workflow && <Workflow />}
|
||||
{appDetail.type === AppTypeEnum.workflowTool && <Plugin />}
|
||||
{appDetail.type === AppTypeEnum.mcpToolSet && <MCPTools />}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ type FormType = {
|
|||
};
|
||||
|
||||
export type CreateAppType =
|
||||
| AppTypeEnum.agent
|
||||
| AppTypeEnum.chatAgent
|
||||
| AppTypeEnum.simple
|
||||
| AppTypeEnum.workflow
|
||||
| AppTypeEnum.workflowTool
|
||||
|
|
@ -71,7 +71,7 @@ const CreateAppsPage = () => {
|
|||
const { parentId, appType } = query;
|
||||
|
||||
const [selectedAppType, setSelectedAppType] = useState<CreateAppType>(
|
||||
(appType as CreateAppType) || AppTypeEnum.workflow
|
||||
(appType as CreateAppType) || AppTypeEnum.chatAgent
|
||||
);
|
||||
const [creatingTemplateId, setCreatingTemplateId] = useState<string | null>(null);
|
||||
const isToolType = ToolTypeList.includes(selectedAppType);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { i18nT } from '@fastgpt/web/i18n/utils';
|
|||
|
||||
// TODO: 需要做一个类型强制约束
|
||||
export const emptyTemplates = {
|
||||
[AppTypeEnum.agent]: {
|
||||
[AppTypeEnum.chatAgent]: {
|
||||
avatar: 'core/app/type/simpleFill',
|
||||
name: 'Agent',
|
||||
nodes: [],
|
||||
|
|
|
|||
Loading…
Reference in New Issue