mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-25 20:02:47 +00:00
tool select ux
This commit is contained in:
parent
d8c598035c
commit
2986a540a3
|
|
@ -100,6 +100,9 @@ export type AppDatasetSearchParamsType = {
|
|||
datasetSearchExtensionBg?: string;
|
||||
};
|
||||
|
||||
export type SelectedToolItemType = FlowNodeTemplateType & {
|
||||
configStatus?: 'active' | 'waitingForConfig' | 'invalid';
|
||||
};
|
||||
export type AppFormEditFormType = {
|
||||
// templateId: string;
|
||||
aiSettings: {
|
||||
|
|
@ -118,9 +121,7 @@ export type AppFormEditFormType = {
|
|||
dataset: {
|
||||
datasets: SelectedDatasetType[];
|
||||
} & AppDatasetSearchParamsType;
|
||||
selectedTools: (FlowNodeTemplateType & {
|
||||
configStatus?: 'active' | 'waitingForConfig' | 'invalid';
|
||||
})[];
|
||||
selectedTools: SelectedToolItemType[];
|
||||
chatConfig: AppChatConfigType;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
"Team_Tags": "Team tags",
|
||||
"Tool_name": "Tool name",
|
||||
"ai_point_price": "Billing",
|
||||
"ai_role": "AI role description",
|
||||
"ai_role_placeholder": "Refer to the following template:\n## Role\n## Response style\n## Response rule",
|
||||
"ai_settings": "AI Configuration",
|
||||
"all_apps": "All Applications",
|
||||
"app.Version name": "Version Name",
|
||||
|
|
@ -295,7 +297,6 @@
|
|||
"plugin_cost_per_times": "{{cost}} points/time",
|
||||
"plugin_db": "Plugins",
|
||||
"plugin_dispatch": "Plugin Invocation",
|
||||
"plugin_dispatch_tip": "Adds extra capabilities to the model. The specific plugins to be invoked will be autonomously decided by the model.\nIf a plugin is selected, the Dataset invocation will automatically be treated as a special plugin.",
|
||||
"plugin_offline_tips": "Your system does not have direct access to plugin market data,\n\nPlease manually copy the URL and go to get more plugins",
|
||||
"plugin_offline_url": "URL",
|
||||
"pro_modal_feature_1": "External organization structure integration and multi-tenancy",
|
||||
|
|
@ -377,11 +378,13 @@
|
|||
"tool_active_system_config_price_desc": "Additional payment for key price ({{price}} points/time)",
|
||||
"tool_active_system_config_price_desc_folder": "The additional key price is required, and the fee will be deducted based on the actual use of the tool.",
|
||||
"tool_detail": "Tool details",
|
||||
"tool_input_param_tip": "This plugin requires configuration of related information to run properly.",
|
||||
"tool_input_param_tip": "This tool requires configuration of relevant information for normal operation.",
|
||||
"tool_not_active": "This tool has not been activated yet",
|
||||
"tool_offset_tips": "This tool is no longer available and will interrupt application operation. Please replace it immediately.",
|
||||
"tool_param_config": "Parameter configuration",
|
||||
"tool_params_description_tips": "The description of parameter functions, if used as tool invocation parameters, affects the model tool invocation effect.",
|
||||
"tool_run_free": "This tool runs without points consumption",
|
||||
"tool_select": "Select tool",
|
||||
"tool_soon_offset_tips": "This tool will be offline in the future. For the sake of your business stability, please replace it as soon as possible.",
|
||||
"tool_tip": "When executed as a tool, is this field used as a tool response result?",
|
||||
"tool_type_tools": "tool",
|
||||
|
|
@ -448,7 +451,9 @@
|
|||
"toolkit_uninstalled": "Uninstalled",
|
||||
"toolkit_update_failed": "Update failed",
|
||||
"toolkit_user_guide": "User Guide",
|
||||
"tools": "tool",
|
||||
"tools_no_description": "This tool has not been introduced ~",
|
||||
"tools_tip": "Tools available for declaring models to enable extended capabilities such as interaction with external systems",
|
||||
"transition_to_workflow": "Convert to Workflow",
|
||||
"transition_to_workflow_create_new_placeholder": "Create a new app instead of modifying the current app",
|
||||
"transition_to_workflow_create_new_tip": "Once converted to a workflow, it cannot be reverted to simple mode. Please confirm!",
|
||||
|
|
@ -522,6 +527,7 @@
|
|||
"version_copy": "Duplicate",
|
||||
"version_initial_copy": "Duplicate - Original State",
|
||||
"vision_model_title": "Image recognition ability",
|
||||
"wait_for_config": "Unconfig",
|
||||
"week.Friday": "Friday",
|
||||
"week.Monday": "Monday",
|
||||
"week.Saturday": "Saturday",
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@
|
|||
"core.ai.Model": "Model",
|
||||
"core.ai.Not deploy rerank model": "Re-rank Model Not Deployed",
|
||||
"core.ai.Prompt": "Prompt",
|
||||
"core.ai.Support tool": "Function Call",
|
||||
"core.ai.Support tool": "Tool call",
|
||||
"core.ai.model.Dataset Agent Model": "File read model",
|
||||
"core.ai.model.Vector Model": "Index model",
|
||||
"core.ai.model.doc_index_and_dialog": "Document Index & Dialog Index",
|
||||
|
|
@ -272,9 +272,7 @@
|
|||
"core.app.TTS Tip": "After enabling, you can use the voice playback function after each conversation. Using this feature may incur additional costs.",
|
||||
"core.app.TTS start": "Read Content",
|
||||
"core.app.Team tags": "Team Tags",
|
||||
"core.app.Tool call": "Tool Call",
|
||||
"core.app.ToolCall.No plugin": "No Available Plugins",
|
||||
"core.app.ToolCall.Parameter setting": "Input Parameters",
|
||||
"core.app.ToolCall.System": "System",
|
||||
"core.app.ToolCall.Team": "Team",
|
||||
"core.app.Welcome Text": "Conversation Opening",
|
||||
|
|
@ -440,7 +438,6 @@
|
|||
"core.chat.response.user_select_result": "User Selection Result",
|
||||
"core.chat.retry": "Regenerate",
|
||||
"core.chat.tts.Stop Speech": "Stop",
|
||||
"core.dataset.Choose Dataset": "Associate Dataset",
|
||||
"core.dataset.Collection": "Dataset",
|
||||
"core.dataset.Create dataset": "Create a {{name}}",
|
||||
"core.dataset.Dataset": "Dataset",
|
||||
|
|
@ -677,7 +674,7 @@
|
|||
"core.module.output.label.query extension result": "Optimization Result",
|
||||
"core.module.template.AI function": "AI Capability",
|
||||
"core.module.template.AI response switch tip": "If you want the current node not to output content, you can turn off this switch. The content output by AI will not be displayed to the user, and you can manually use 'AI Response Content' for special processing.",
|
||||
"core.module.template.AI support tool tip": "Models that support function calls can better use tool calls.",
|
||||
"core.module.template.AI support tool tip": "Models that support tool calling enable better use of tools.",
|
||||
"core.module.template.Basic Node": "Basic",
|
||||
"core.module.template.Query extension": "Question Optimization",
|
||||
"core.module.template.System input module": "System Input",
|
||||
|
|
@ -694,7 +691,6 @@
|
|||
"core.module.template.system_config": "System configuration",
|
||||
"core.module.template.system_config_info": "Can configure application system parameters",
|
||||
"core.module.template.work_start": "Process starts",
|
||||
"core.module.templates.Load plugin error": "Failed to Load Plugin",
|
||||
"core.module.variable add option": "Add Option",
|
||||
"core.module.variable.Custom type": "Custom Variable",
|
||||
"core.module.variable.add option": "Add Option",
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@
|
|||
"Tool_description": "工具描述",
|
||||
"Tool_name": "工具名称",
|
||||
"ai_point_price": "AI积分计费",
|
||||
"ai_role": "AI 角色描述",
|
||||
"ai_role_placeholder": "参考以下模板:\n## 角色\n## 回答风格\n## 回答要求",
|
||||
"ai_settings": "AI 配置",
|
||||
"all_apps": "全部应用",
|
||||
"app.Version name": "版本名称",
|
||||
|
|
@ -307,7 +309,6 @@
|
|||
"plugin_cost_folder_tip": "该工具集包含下属工具,调用积分依据实际调用工具决定",
|
||||
"plugin_cost_per_times": "{{cost}} 积分/次",
|
||||
"plugin_dispatch": "插件调用",
|
||||
"plugin_dispatch_tip": "给模型附加获取外部数据的能力,具体调用哪些插件,将由模型自主决定,所有插件都将以非流模式运行。\n若选择了插件,知识库调用将自动作为一个特殊的插件。",
|
||||
"plugin_offline_tips": "您的系统无法直接访问插件市场数据,\n请手动复制网址并前往,以获得更多插件",
|
||||
"plugin_offline_url": "网址",
|
||||
"pro_modal_feature_1": "外部组织架构接入与多租户",
|
||||
|
|
@ -394,11 +395,13 @@
|
|||
"tool_active_system_config_price_desc": "需额外支付密钥价格( {{price}} 积分/次)",
|
||||
"tool_active_system_config_price_desc_folder": "需额外支付密钥价格,依据实际使用工具扣费。",
|
||||
"tool_detail": "工具详情",
|
||||
"tool_input_param_tip": "该插件正常运行需要配置相关信息",
|
||||
"tool_input_param_tip": "该工具正常运行需要配置相关信息",
|
||||
"tool_not_active": "该工具尚未激活",
|
||||
"tool_offset_tips": "该工具已无法使用,将中断应用运行,请立即替换",
|
||||
"tool_param_config": "参数配置",
|
||||
"tool_params_description_tips": "参数功能的描述,若作为工具调用参数,影响模型工具调用效果",
|
||||
"tool_run_free": "该工具运行无积分消耗",
|
||||
"tool_select": "选择工具",
|
||||
"tool_soon_offset_tips": "该工具将在后续下线,为了您的业务稳定,请尽快替换",
|
||||
"tool_tip": "作为工具执行时,该字段是否作为工具响应结果",
|
||||
"tool_type_tools": "工具",
|
||||
|
|
@ -465,7 +468,9 @@
|
|||
"toolkit_uninstalled": "未安装",
|
||||
"toolkit_update_failed": "更新失败",
|
||||
"toolkit_user_guide": "使用说明",
|
||||
"tools": "工具",
|
||||
"tools_no_description": "这个工具没有介绍~",
|
||||
"tools_tip": "声明模型可用的工具,可以实现与外部系统交互等扩展能力",
|
||||
"transition_to_workflow": "转成工作流",
|
||||
"transition_to_workflow_create_new_placeholder": "创建一个新的应用,而不是修改当前应用",
|
||||
"transition_to_workflow_create_new_tip": "转化成工作流后,将无法转化回简易模式,请确认!",
|
||||
|
|
@ -541,6 +546,7 @@
|
|||
"version_copy": "副本",
|
||||
"version_initial_copy": "副本-初始状态",
|
||||
"vision_model_title": "图片识别能力",
|
||||
"wait_for_config": "等待配置",
|
||||
"week.Friday": "星期五",
|
||||
"week.Monday": "星期一",
|
||||
"week.Saturday": "星期六",
|
||||
|
|
|
|||
|
|
@ -103,8 +103,11 @@
|
|||
"add_new": "新增",
|
||||
"add_new_param": "新增参数",
|
||||
"add_success": "添加成功",
|
||||
<<<<<<< HEAD
|
||||
"aipoint_desc": "每次调用 AI 模型时,都会消耗一定的 AI 积分(类似于 token)。点击可查看详细计算规则。",
|
||||
"agent_prompt_tips": "建议按照以下模板填写,以获得最佳效果。\n「角色身份」\n「任务目标」\n「任务流程与技能」\n输入“/”插入全局变量;输入“@”插入特定技能,包括应用、工具、知识库、模型。",
|
||||
=======
|
||||
>>>>>>> e4b2fca6d (tool select ux)
|
||||
"all_quotes": "全部引用",
|
||||
"all_result": "完整结果",
|
||||
"app_evaluation": "Agent 评测(Beta)",
|
||||
|
|
@ -234,7 +237,7 @@
|
|||
"core.ai.Model": "AI 模型",
|
||||
"core.ai.Not deploy rerank model": "未部署重排模型",
|
||||
"core.ai.Prompt": "提示词",
|
||||
"core.ai.Support tool": "函数调用",
|
||||
"core.ai.Support tool": "工具调用",
|
||||
"core.ai.model.Dataset Agent Model": "文本理解模型",
|
||||
"core.ai.model.Vector Model": "索引模型",
|
||||
"core.ai.model.doc_index_and_dialog": "文档索引 & 对话索引",
|
||||
|
|
@ -275,9 +278,7 @@
|
|||
"core.app.TTS Tip": "开启后,每次对话后可使用语音播放功能。使用该功能可能产生额外费用。",
|
||||
"core.app.TTS start": "朗读内容",
|
||||
"core.app.Team tags": "团队标签",
|
||||
"core.app.Tool call": "工具调用",
|
||||
"core.app.ToolCall.No plugin": "没有可用的插件",
|
||||
"core.app.ToolCall.Parameter setting": "输入参数",
|
||||
"core.app.ToolCall.System": "系统",
|
||||
"core.app.ToolCall.Team": "团队",
|
||||
"core.app.Welcome Text": "对话开场白",
|
||||
|
|
@ -443,7 +444,6 @@
|
|||
"core.chat.response.user_select_result": "用户选择结果",
|
||||
"core.chat.retry": "重新生成",
|
||||
"core.chat.tts.Stop Speech": "停止",
|
||||
"core.dataset.Choose Dataset": "关联知识库",
|
||||
"core.dataset.Collection": "数据集",
|
||||
"core.dataset.Create dataset": "创建一个{{name}}",
|
||||
"core.dataset.Dataset": "知识库",
|
||||
|
|
@ -680,7 +680,7 @@
|
|||
"core.module.output.label.query extension result": "优化结果",
|
||||
"core.module.template.AI function": "AI能力",
|
||||
"core.module.template.AI response switch tip": "如果你希望当前节点不输出内容,可以关闭该开关。AI 输出的内容不会展示给用户,你可以手动的使用“AI 回复内容”进行特殊处理。",
|
||||
"core.module.template.AI support tool tip": "支持函数调用的模型,可以更好的使用工具调用。",
|
||||
"core.module.template.AI support tool tip": "支持工具调用的模型,可以更好的使用工具。",
|
||||
"core.module.template.Basic Node": "基础功能",
|
||||
"core.module.template.Query extension": "问题优化",
|
||||
"core.module.template.System input module": "系统输入",
|
||||
|
|
@ -698,7 +698,6 @@
|
|||
"core.module.template.system_config": "系统配置",
|
||||
"core.module.template.system_config_info": "可以配置应用的系统参数",
|
||||
"core.module.template.work_start": "流程开始",
|
||||
"core.module.templates.Load plugin error": "加载插件失败",
|
||||
"core.module.variable add option": "添加选项",
|
||||
"core.module.variable.Custom type": "自定义变量",
|
||||
"core.module.variable.add option": "添加选项",
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
"Tool_description": "工具描述",
|
||||
"Tool_name": "工具名稱",
|
||||
"ai_point_price": "AI 積分計費",
|
||||
"ai_role": "AI 角色描述",
|
||||
"ai_role_placeholder": "參考以下模板:\n## 角色\n## 回答风格\n## 回答要求",
|
||||
"ai_settings": "AI 設定",
|
||||
"all_apps": "所有應用程式",
|
||||
"app.Version name": "版本名稱",
|
||||
|
|
@ -293,7 +295,6 @@
|
|||
"plugin_cost_per_times": "{{cost}} 積分/次",
|
||||
"plugin_db": "資源庫",
|
||||
"plugin_dispatch": "外掛呼叫",
|
||||
"plugin_dispatch_tip": "賦予模型取得外部資料的能力,具體呼叫哪些外掛,將由模型自主決定,所有外掛都將以非串流模式執行。\n若選擇了外掛,知識庫呼叫將自動作為一個特殊的外掛。",
|
||||
"plugin_offline_tips": "您的系統無法直接訪問插件市場數據,\n請手動複製網址並前往,以獲得更多插件",
|
||||
"plugin_offline_url": "網址",
|
||||
"pro_modal_feature_1": "外部組織架構接入與多租戶",
|
||||
|
|
@ -375,11 +376,13 @@
|
|||
"tool_active_system_config_price_desc": "需額外支付密鑰價格( {{price}} 積分/次)",
|
||||
"tool_active_system_config_price_desc_folder": "需額外支付密鑰價格,依據實際使用工具扣費。",
|
||||
"tool_detail": "工具詳情",
|
||||
"tool_input_param_tip": "這個外掛正常執行需要設定相關資訊",
|
||||
"tool_input_param_tip": "該工具正常運行需要配置相關信息",
|
||||
"tool_not_active": "該工具尚未激活",
|
||||
"tool_offset_tips": "該工具已無法使用,將中斷應用運行,請立即替換",
|
||||
"tool_param_config": "參數配置",
|
||||
"tool_params_description_tips": "參數功能的描述,若作為工具調用參數,影響模型工具調用效果",
|
||||
"tool_run_free": "該工具運行無積分消耗",
|
||||
"tool_select": "選擇工具",
|
||||
"tool_soon_offset_tips": "該工具將在後續下線,為了您的業務穩定,請盡快替換",
|
||||
"tool_tip": "作為工具執行時,該字段是否作為工具響應結果",
|
||||
"tool_type_tools": "工具",
|
||||
|
|
@ -445,7 +448,9 @@
|
|||
"toolkit_uninstalled": "未安裝",
|
||||
"toolkit_update_failed": "更新失敗",
|
||||
"toolkit_user_guide": "使用說明",
|
||||
"tools": "工具",
|
||||
"tools_no_description": "這個工具沒有介紹~",
|
||||
"tools_tip": "聲明模型可用的工具,可以實現與外部系統交互等擴展能力",
|
||||
"transition_to_workflow": "轉換成工作流程",
|
||||
"transition_to_workflow_create_new_placeholder": "建立新的應用程式,而不是修改目前應用程式",
|
||||
"transition_to_workflow_create_new_tip": "轉換成工作流程後,將無法轉換回簡易模式,請確認!",
|
||||
|
|
@ -519,6 +524,7 @@
|
|||
"version_copy": "副本",
|
||||
"version_initial_copy": "副本 - 初始狀態",
|
||||
"vision_model_title": "圖片辨識功能",
|
||||
"wait_for_config": "等待配置",
|
||||
"week.Friday": "星期五",
|
||||
"week.Monday": "星期一",
|
||||
"week.Saturday": "星期六",
|
||||
|
|
|
|||
|
|
@ -102,8 +102,11 @@
|
|||
"add_new": "新增",
|
||||
"add_new_param": "新增參數",
|
||||
"add_success": "新增成功",
|
||||
<<<<<<< HEAD
|
||||
"aipoint_desc": "每次呼叫 AI 模型時,都會消耗一定的 AI 點數(類似於 Token)。點選可檢視詳細計算規則。",
|
||||
"agent_prompt_tips": "建議按照以下模板填寫,以獲得最佳效果。\n\n「角色身份」\n「任務目標」\n「任務流程與技能」\n輸入“/”插入全局變量;輸入“@”插入特定技能,包括應用、工具、知識庫、模型。",
|
||||
=======
|
||||
>>>>>>> e4b2fca6d (tool select ux)
|
||||
"all_quotes": "全部引用",
|
||||
"all_result": "完整結果",
|
||||
"app_evaluation": "應用評測(Beta)",
|
||||
|
|
@ -231,7 +234,7 @@
|
|||
"core.ai.Model": "AI 模型",
|
||||
"core.ai.Not deploy rerank model": "未部署重新排名模型",
|
||||
"core.ai.Prompt": "提示詞",
|
||||
"core.ai.Support tool": "函式呼叫",
|
||||
"core.ai.Support tool": "工具調用",
|
||||
"core.ai.model.Dataset Agent Model": "檔案處理模型",
|
||||
"core.ai.model.Vector Model": "索引模型",
|
||||
"core.ai.model.doc_index_and_dialog": "文件索引與對話索引",
|
||||
|
|
@ -272,9 +275,7 @@
|
|||
"core.app.TTS Tip": "開啟後,每次對話後可使用語音播放功能。使用此功能可能會產生額外費用。",
|
||||
"core.app.TTS start": "朗讀內容",
|
||||
"core.app.Team tags": "團隊標籤",
|
||||
"core.app.Tool call": "工具呼叫",
|
||||
"core.app.ToolCall.No plugin": "沒有可用的外掛程式",
|
||||
"core.app.ToolCall.Parameter setting": "輸入參數",
|
||||
"core.app.ToolCall.System": "系統",
|
||||
"core.app.ToolCall.Team": "團隊",
|
||||
"core.app.Welcome Text": "對話開場白",
|
||||
|
|
@ -439,7 +440,6 @@
|
|||
"core.chat.response.update_var_result": "變數更新結果(依序顯示多個變數更新結果)",
|
||||
"core.chat.response.user_select_result": "使用者選擇結果",
|
||||
"core.chat.tts.Stop Speech": "停止",
|
||||
"core.dataset.Choose Dataset": "關聯知識庫",
|
||||
"core.dataset.Collection": "資料集",
|
||||
"core.dataset.Create dataset": "建立一個{{name}}",
|
||||
"core.dataset.Dataset": "知識庫",
|
||||
|
|
@ -676,7 +676,7 @@
|
|||
"core.module.output.label.query extension result": "最佳化結果",
|
||||
"core.module.template.AI function": "AI 功能",
|
||||
"core.module.template.AI response switch tip": "如果您希望目前節點不輸出內容,可以關閉此開關。AI 輸出的內容不會顯示給使用者,您可以手動使用「AI 回覆內容」進行特殊處理。",
|
||||
"core.module.template.AI support tool tip": "支援函式呼叫的模型可以更好地使用工具呼叫。",
|
||||
"core.module.template.AI support tool tip": "支持工具調用的模型,可以更好的使用工具。",
|
||||
"core.module.template.Basic Node": "基本功能",
|
||||
"core.module.template.Query extension": "問題最佳化",
|
||||
"core.module.template.System input module": "系統輸入模組",
|
||||
|
|
@ -693,7 +693,6 @@
|
|||
"core.module.template.system_config": "系統設定",
|
||||
"core.module.template.system_config_info": "可以設定應用程式的系統參數",
|
||||
"core.module.template.work_start": "流程開始",
|
||||
"core.module.templates.Load plugin error": "載入外掛程式失敗",
|
||||
"core.module.variable add option": "新增選項",
|
||||
"core.module.variable.Custom type": "自訂變數",
|
||||
"core.module.variable.add option": "新增選項",
|
||||
|
|
|
|||
|
|
@ -54,13 +54,18 @@ const LabelStyles: BoxProps = {
|
|||
mr: 5
|
||||
};
|
||||
|
||||
export type AIChatSettingsModalProps = {};
|
||||
export type AIChatSettingsModalProps = {
|
||||
showStopSign?: boolean;
|
||||
showResponseFormat?: boolean;
|
||||
};
|
||||
|
||||
const AIChatSettingsModal = ({
|
||||
onClose,
|
||||
onSuccess,
|
||||
defaultData,
|
||||
llmModels = []
|
||||
llmModels = [],
|
||||
showStopSign = true,
|
||||
showResponseFormat = true
|
||||
}: AIChatSettingsModalProps & {
|
||||
onClose: () => void;
|
||||
onSuccess: (e: SettingAIDataType) => void;
|
||||
|
|
@ -335,7 +340,7 @@ const AIChatSettingsModal = ({
|
|||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{llmSupportStopSign && (
|
||||
{showStopSign && llmSupportStopSign && (
|
||||
<Flex {...FlexItemStyles}>
|
||||
<Box {...LabelStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
|
|
@ -360,7 +365,7 @@ const AIChatSettingsModal = ({
|
|||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{llmSupportResponseFormat && selectedModel?.responseFormatList && (
|
||||
{showResponseFormat && llmSupportResponseFormat && selectedModel?.responseFormatList && (
|
||||
<Flex {...FlexItemStyles}>
|
||||
<Box {...LabelStyles}>
|
||||
<Flex alignItems={'center'}>{t('app:response_format')}</Flex>
|
||||
|
|
@ -393,7 +398,7 @@ const AIChatSettingsModal = ({
|
|||
</Flex>
|
||||
)}
|
||||
{/* Json schema */}
|
||||
{responseFormat === 'json_schema' && (
|
||||
{showResponseFormat && responseFormat === 'json_schema' && (
|
||||
<Flex {...FlexItemStyles} h="auto">
|
||||
<Box {...LabelStyles}>
|
||||
<Flex alignItems={'center'}>JSON Schema</Flex>
|
||||
|
|
|
|||
|
|
@ -66,9 +66,7 @@ const FileSelect = ({
|
|||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/simpleMode/file'} mr={2} w={'20px'} />
|
||||
<FormLabel color={'myGray.600'} {...labelStyle}>
|
||||
{t('app:file_upload')}
|
||||
</FormLabel>
|
||||
<FormLabel {...labelStyle}>{t('app:file_upload')}</FormLabel>
|
||||
<ChatFunctionTip type={'file'} />
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={t('app:config_file_upload')}>
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ const InputGuideConfig = ({
|
|||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/inputGuides'} mr={2} w={'20px'} />
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel color={'myGray.600'}>{t('chat:input_guide')}</FormLabel>
|
||||
<FormLabel>{t('chat:input_guide')}</FormLabel>
|
||||
<ChatFunctionTip type={'inputGuide'} />
|
||||
</Flex>
|
||||
<Box flex={1} />
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const QGConfig = ({
|
|||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/chat/QGFill'} mr={2} w={'20px'} />
|
||||
<FormLabel color={'myGray.600'}>{t('common:core.app.Question Guide')}</FormLabel>
|
||||
<FormLabel>{t('common:core.app.Question Guide')}</FormLabel>
|
||||
<ChatFunctionTip type={'nextQuestion'} />
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={t('app:config_question_guide')}>
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ const TTSSelect = ({
|
|||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/simpleMode/tts'} mr={2} w={'20px'} />
|
||||
<FormLabel color={'myGray.600'}>{t('common:core.app.TTS')}</FormLabel>
|
||||
<FormLabel>{t('common:core.app.TTS')}</FormLabel>
|
||||
<ChatFunctionTip type={'tts'} />
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={t('common:core.app.Select TTS')}>
|
||||
|
|
|
|||
|
|
@ -92,9 +92,7 @@ const VariableEdit = ({
|
|||
{/* Row box */}
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/simpleMode/variable'} w={'20px'} />
|
||||
<FormLabel ml={2} color={'myGray.600'}>
|
||||
{t('common:core.module.Variable')}
|
||||
</FormLabel>
|
||||
<FormLabel ml={2}>{t('common:core.module.Variable')}</FormLabel>
|
||||
<ChatFunctionTip type={'variable'} />
|
||||
<Box flex={1} />
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ const WelcomeTextConfig = (props: TextareaProps) => {
|
|||
<>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/simpleMode/chat'} w={'20px'} />
|
||||
<FormLabel ml={2} color={'myGray.600'}>
|
||||
{t('common:core.app.Welcome Text')}
|
||||
</FormLabel>
|
||||
<FormLabel ml={2}>{t('common:core.app.Welcome Text')}</FormLabel>
|
||||
<ChatFunctionTip type={'welcome'} />
|
||||
</Flex>
|
||||
<MyTextarea
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ const WhisperConfig = ({
|
|||
return (
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/simpleMode/whisper'} mr={2} w={'20px'} />
|
||||
<FormLabel color={'myGray.600'}>{t('common:core.app.Whisper')}</FormLabel>
|
||||
<FormLabel>{t('common:core.app.Whisper')}</FormLabel>
|
||||
<Box flex={1} />
|
||||
<MyTooltip label={t('common:core.app.Config whisper')}>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -51,13 +51,6 @@ const BoxStyles: BoxProps = {
|
|||
borderBottomWidth: '1px',
|
||||
borderBottomColor: 'borderColor.low'
|
||||
};
|
||||
const LabelStyles: BoxProps = {
|
||||
w: ['60px', '100px'],
|
||||
whiteSpace: 'nowrap',
|
||||
flexShrink: 0,
|
||||
fontSize: 'sm',
|
||||
color: 'myGray.900'
|
||||
};
|
||||
|
||||
const EditForm = ({
|
||||
appForm,
|
||||
|
|
@ -75,40 +68,40 @@ const EditForm = ({
|
|||
const [, startTst] = useTransition();
|
||||
|
||||
// Skill picker
|
||||
const selectedTools = useMemoEnhance(() => appForm.selectedTools, [appForm.selectedTools]);
|
||||
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: state.selectedTools.filter((t) => t.id !== id)
|
||||
}));
|
||||
},
|
||||
[setAppForm]
|
||||
);
|
||||
const { SkillModal, skillOption, selectedSkills, onClickSkill, onRemoveSkill } = useSkillManager({
|
||||
selectedTools,
|
||||
onUpdateOrAddTool,
|
||||
onDeleteTool,
|
||||
canSelectFile: appForm.chatConfig?.fileSelectConfig?.canSelectFile,
|
||||
canSelectImg: appForm.chatConfig?.fileSelectConfig?.canSelectImg
|
||||
});
|
||||
// const selectedTools = useMemoEnhance(() => appForm.selectedTools, [appForm.selectedTools]);
|
||||
// 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: state.selectedTools.filter((t) => t.id !== id)
|
||||
// }));
|
||||
// },
|
||||
// [setAppForm]
|
||||
// );
|
||||
// const { SkillModal, skillOption, selectedSkills, onClickSkill, onRemoveSkill } = useSkillManager({
|
||||
// selectedTools,
|
||||
// onUpdateOrAddTool,
|
||||
// onDeleteTool,
|
||||
// canSelectFile: appForm.chatConfig?.fileSelectConfig?.canSelectFile,
|
||||
// canSelectImg: appForm.chatConfig?.fileSelectConfig?.canSelectImg
|
||||
// });
|
||||
|
||||
const {
|
||||
isOpen: isOpenDatasetSelect,
|
||||
|
|
@ -199,7 +192,7 @@ const EditForm = ({
|
|||
</FormLabel>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={5}>
|
||||
<Box {...LabelStyles}>{t('common:core.ai.Model')}</Box>
|
||||
<FormLabel w={['60px', '100px']}>{t('common:core.ai.Model')}</FormLabel>
|
||||
<Box flex={'1 0 0'}>
|
||||
<SettingLLMModel
|
||||
bg="myGray.50"
|
||||
|
|
@ -215,6 +208,8 @@ const EditForm = ({
|
|||
aiChatResponseFormat: appForm.aiSettings.aiChatResponseFormat,
|
||||
aiChatJsonSchema: appForm.aiSettings.aiChatJsonSchema
|
||||
}}
|
||||
showStopSign={false}
|
||||
showResponseFormat={false}
|
||||
onChange={({ maxHistories = 6, ...data }) => {
|
||||
setAppForm((state) => ({
|
||||
...state,
|
||||
|
|
@ -229,17 +224,18 @@ const EditForm = ({
|
|||
</Box>
|
||||
</Flex>
|
||||
|
||||
{/* Prompt */}
|
||||
<Box mt={4}>
|
||||
<HStack {...LabelStyles} w={'100%'}>
|
||||
<Box>{t('common:core.ai.Prompt')}</Box>
|
||||
<QuestionTip label={t('common:agent_prompt_tips')} />
|
||||
<HStack w={'100%'}>
|
||||
<FormLabel>{t('app:ai_role')}</FormLabel>
|
||||
<QuestionTip label={t('app:ai_role_placeholder')} />
|
||||
|
||||
<Box flex={1} />
|
||||
<VariableTip color={'myGray.500'} />
|
||||
{/* <VariableTip color={'myGray.500'} /> */}
|
||||
</HStack>
|
||||
<Box mt={1}>
|
||||
<PromptEditor
|
||||
minH={150}
|
||||
minH={120}
|
||||
value={appForm.aiSettings.systemPrompt}
|
||||
bg={'myGray.50'}
|
||||
onChange={(text) => {
|
||||
|
|
@ -253,12 +249,8 @@ const EditForm = ({
|
|||
}));
|
||||
});
|
||||
}}
|
||||
variableLabels={formatVariables}
|
||||
skillOption={skillOption}
|
||||
selectedSkills={selectedSkills}
|
||||
onClickSkill={onClickSkill}
|
||||
onRemoveSkill={onRemoveSkill}
|
||||
placeholder={t('common:agent_prompt_tips')}
|
||||
// variableLabels={formatVariables}
|
||||
placeholder={t('app:ai_role_placeholder')}
|
||||
title={t('common:core.ai.Prompt')}
|
||||
ExtensionPopover={[OptimizerPromptPopverComponent]}
|
||||
isRichText={true}
|
||||
|
|
@ -267,12 +259,17 @@ const EditForm = ({
|
|||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* tool choice */}
|
||||
<Box {...BoxStyles}>
|
||||
<ToolSelect appForm={appForm} setAppForm={setAppForm} />
|
||||
</Box>
|
||||
|
||||
{/* dataset */}
|
||||
<Box {...BoxStyles}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<MyIcon name={'core/app/simpleMode/dataset'} w={'20px'} />
|
||||
<FormLabel ml={2}>{t('common:core.dataset.Choose Dataset')}</FormLabel>
|
||||
<FormLabel ml={2}>{t('app:dataset')}</FormLabel>
|
||||
</Flex>
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
|
|
@ -345,11 +342,6 @@ const EditForm = ({
|
|||
</Grid>
|
||||
</Box>
|
||||
|
||||
{/* tool choice */}
|
||||
<Box {...BoxStyles}>
|
||||
<ToolSelect appForm={appForm} setAppForm={setAppForm} />
|
||||
</Box>
|
||||
|
||||
{/* File select */}
|
||||
<Box {...BoxStyles}>
|
||||
<FileSelectConfig
|
||||
|
|
@ -368,7 +360,7 @@ const EditForm = ({
|
|||
</Box>
|
||||
|
||||
{/* variable */}
|
||||
<Box {...BoxStyles}>
|
||||
{/* <Box {...BoxStyles}>
|
||||
<VariableEdit
|
||||
variables={appForm.chatConfig.variables}
|
||||
onChange={(e) => {
|
||||
|
|
@ -381,7 +373,7 @@ const EditForm = ({
|
|||
}));
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box> */}
|
||||
|
||||
{/* welcome */}
|
||||
<Box {...BoxStyles}>
|
||||
|
|
@ -468,7 +460,6 @@ const EditForm = ({
|
|||
|
||||
{isOpenDatasetSelect && (
|
||||
<DatasetSelectModal
|
||||
isOpen={isOpenDatasetSelect}
|
||||
defaultSelectedDatasets={selectDatasets.map((item) => ({
|
||||
datasetId: item.datasetId,
|
||||
vectorModel: item.vectorModel,
|
||||
|
|
@ -503,7 +494,6 @@ const EditForm = ({
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
<SkillModal />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
|
|||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { checkNeedsUserConfiguration, validateToolConfiguration } from './utils';
|
||||
import { checkNeedsUserConfiguration, validateToolConfiguration } from '../utils';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
|
|
@ -143,9 +143,7 @@ export const useSkillManager = ({
|
|||
|
||||
const onAddAppOrTool = useCallback(
|
||||
async (appId: string) => {
|
||||
console.log(appId);
|
||||
const toolTemplate = await getToolPreviewNode({ appId });
|
||||
console.log(toolTemplate);
|
||||
|
||||
if (!toolTemplate) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import type { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node';
|
||||
|
||||
export const validateToolConfiguration = ({
|
||||
toolTemplate,
|
||||
canSelectFile,
|
||||
canSelectImg
|
||||
}: {
|
||||
toolTemplate: FlowNodeTemplateType;
|
||||
canSelectFile?: boolean;
|
||||
canSelectImg?: boolean;
|
||||
}): boolean => {
|
||||
// 检查文件上传配置
|
||||
const oneFileInput =
|
||||
toolTemplate.inputs.filter((input) =>
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
|
||||
).length === 1;
|
||||
|
||||
const canUploadFile = canSelectFile || canSelectImg;
|
||||
|
||||
const hasValidFileInput = oneFileInput && !!canUploadFile;
|
||||
|
||||
// 检查是否有无效的输入配置
|
||||
const hasInvalidInput = toolTemplate.inputs.some(
|
||||
(input) =>
|
||||
// 引用类型但没有工具描述
|
||||
(input.renderTypeList.length === 1 &&
|
||||
input.renderTypeList[0] === FlowNodeInputTypeEnum.reference &&
|
||||
!input.toolDescription) ||
|
||||
// 包含数据集选择
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectDataset) ||
|
||||
// 包含动态输入参数
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.addInputParam) ||
|
||||
// 文件选择但配置无效
|
||||
(input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) && !hasValidFileInput)
|
||||
);
|
||||
|
||||
if (hasInvalidInput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
export const checkNeedsUserConfiguration = (toolTemplate: FlowNodeTemplateType): boolean => {
|
||||
const formRenderTypesMap: Record<string, boolean> = {
|
||||
[FlowNodeInputTypeEnum.input]: true,
|
||||
[FlowNodeInputTypeEnum.textarea]: true,
|
||||
[FlowNodeInputTypeEnum.numberInput]: true,
|
||||
[FlowNodeInputTypeEnum.password]: true,
|
||||
[FlowNodeInputTypeEnum.switch]: true,
|
||||
[FlowNodeInputTypeEnum.select]: true,
|
||||
[FlowNodeInputTypeEnum.JSONEditor]: true,
|
||||
[FlowNodeInputTypeEnum.timePointSelect]: true,
|
||||
[FlowNodeInputTypeEnum.timeRangeSelect]: true
|
||||
};
|
||||
return (
|
||||
toolTemplate.inputs.length > 0 &&
|
||||
toolTemplate.inputs.some((input) => {
|
||||
// 有工具描述的不需要配置
|
||||
if (input.toolDescription) return false;
|
||||
// 禁用流的不需要配置
|
||||
if (input.key === NodeInputKeyEnum.forbidStream) return false;
|
||||
// 系统输入配置需要配置
|
||||
if (input.key === NodeInputKeyEnum.systemInputConfig) return true;
|
||||
|
||||
// 检查是否包含表单类型的输入
|
||||
return input.renderTypeList.some((type) => formRenderTypesMap[type]);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
|
@ -200,6 +200,9 @@ export function agentForm2AppWorkflow(
|
|||
value: data.aiSettings.maxHistories
|
||||
};
|
||||
}
|
||||
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)) {
|
||||
input.value = [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]];
|
||||
}
|
||||
return input;
|
||||
})
|
||||
}))
|
||||
|
|
@ -226,3 +229,76 @@ export function agentForm2AppWorkflow(
|
|||
chatConfig: data.chatConfig
|
||||
};
|
||||
}
|
||||
|
||||
/* Invalid tool check
|
||||
1. Reference type. but not tool description;
|
||||
2. Has dataset select
|
||||
3. Has dynamic external data
|
||||
*/
|
||||
export const validateToolConfiguration = ({
|
||||
toolTemplate,
|
||||
canSelectFile,
|
||||
canSelectImg
|
||||
}: {
|
||||
toolTemplate: FlowNodeTemplateType;
|
||||
canSelectFile?: boolean;
|
||||
canSelectImg?: boolean;
|
||||
}): boolean => {
|
||||
// 检查文件上传配置
|
||||
const oneFileInput =
|
||||
toolTemplate.inputs.filter((input) =>
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
|
||||
).length === 1;
|
||||
|
||||
const canUploadFile = canSelectFile || canSelectImg;
|
||||
|
||||
const hasValidFileInput = oneFileInput && !!canUploadFile;
|
||||
|
||||
// 检查是否有无效的输入配置
|
||||
const hasInvalidInput = toolTemplate.inputs.some(
|
||||
(input) =>
|
||||
// 引用类型但没有工具描述
|
||||
(input.renderTypeList.length === 1 &&
|
||||
input.renderTypeList[0] === FlowNodeInputTypeEnum.reference &&
|
||||
!input.toolDescription) ||
|
||||
// 包含数据集选择
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectDataset) ||
|
||||
// 包含动态输入参数
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.addInputParam) ||
|
||||
// 文件选择但配置无效
|
||||
(input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) && !hasValidFileInput)
|
||||
);
|
||||
|
||||
if (hasInvalidInput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
export const checkNeedsUserConfiguration = (toolTemplate: FlowNodeTemplateType): boolean => {
|
||||
const formRenderTypesMap: Record<string, boolean> = {
|
||||
[FlowNodeInputTypeEnum.input]: true,
|
||||
[FlowNodeInputTypeEnum.textarea]: true,
|
||||
[FlowNodeInputTypeEnum.numberInput]: true,
|
||||
[FlowNodeInputTypeEnum.password]: true,
|
||||
[FlowNodeInputTypeEnum.switch]: true,
|
||||
[FlowNodeInputTypeEnum.select]: true,
|
||||
[FlowNodeInputTypeEnum.JSONEditor]: true,
|
||||
[FlowNodeInputTypeEnum.timePointSelect]: true,
|
||||
[FlowNodeInputTypeEnum.timeRangeSelect]: true
|
||||
};
|
||||
return (
|
||||
toolTemplate.inputs.length > 0 &&
|
||||
toolTemplate.inputs.some((input) => {
|
||||
// 有工具描述的不需要配置
|
||||
if (input.toolDescription) return false;
|
||||
// 禁用流的不需要配置
|
||||
if (input.key === NodeInputKeyEnum.forbidStream) return false;
|
||||
// 系统输入配置需要配置
|
||||
if (input.key === NodeInputKeyEnum.systemInputConfig) return true;
|
||||
|
||||
// 检查是否包含表单类型的输入
|
||||
return input.renderTypeList.some((type) => formRenderTypesMap[type]);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
|||
import { formatToolError } from '@fastgpt/global/core/app/utils';
|
||||
import { PluginStatusEnum, PluginStatusMap } from '@fastgpt/global/core/plugin/type';
|
||||
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
||||
import { checkNeedsUserConfiguration } from '../../ChatAgent/utils';
|
||||
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||
|
||||
const ToolSelect = ({
|
||||
appForm,
|
||||
|
|
@ -46,8 +48,8 @@ const ToolSelect = ({
|
|||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<MyIcon name={'core/app/toolCall'} w={'20px'} />
|
||||
<FormLabel ml={2}>{t('common:core.app.Tool call')}</FormLabel>
|
||||
<QuestionTip ml={1} label={t('app:plugin_dispatch_tip')} />
|
||||
<FormLabel ml={2}>{t('app:tools')}</FormLabel>
|
||||
<QuestionTip ml={1} label={t('app:tools_tip')} />
|
||||
</Flex>
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
|
|
@ -68,8 +70,12 @@ const ToolSelect = ({
|
|||
>
|
||||
{appForm.selectedTools.map((item) => {
|
||||
const toolError = formatToolError(item.pluginData?.error);
|
||||
// 即将下架/已下架
|
||||
const status = item.status || item.pluginData?.status;
|
||||
|
||||
const hasFormInput = checkNeedsUserConfiguration(item);
|
||||
const isUnconfigured = item.configStatus === 'waitingForConfig';
|
||||
|
||||
return (
|
||||
<MyTooltip key={item.id} label={item.intro}>
|
||||
<Flex
|
||||
|
|
@ -79,30 +85,21 @@ const ToolSelect = ({
|
|||
bg={'white'}
|
||||
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
|
||||
borderRadius={'md'}
|
||||
border={theme.borders.base}
|
||||
border={'base'}
|
||||
borderColor={toolError ? 'red.600' : ''}
|
||||
userSelect={'none'}
|
||||
_hover={{
|
||||
...hoverDeleteStyles,
|
||||
borderColor: toolError ? 'red.600' : 'primary.300'
|
||||
}}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
if (
|
||||
item.inputs
|
||||
.filter((input) => !childAppSystemKey.includes(input.key))
|
||||
.every(
|
||||
(input) =>
|
||||
input.toolDescription ||
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) ||
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
|
||||
) ||
|
||||
toolError ||
|
||||
item.flowNodeType === FlowNodeTypeEnum.tool ||
|
||||
item.flowNodeType === FlowNodeTypeEnum.toolSet
|
||||
) {
|
||||
return;
|
||||
borderColor: toolError ? 'red.600' : 'primary.300',
|
||||
'.delete': {
|
||||
display: 'block'
|
||||
},
|
||||
'.hoverStyle': {
|
||||
display: 'block'
|
||||
},
|
||||
'.unHoverStyle': {
|
||||
display: 'none'
|
||||
}
|
||||
setConfigTool(item);
|
||||
}}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'1.5rem'} h={'1.5rem'} borderRadius={'sm'} />
|
||||
|
|
@ -116,29 +113,52 @@ const ToolSelect = ({
|
|||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
|
||||
{status !== undefined && status !== PluginStatusEnum.Normal && (
|
||||
<MyTooltip label={t(PluginStatusMap[status].tooltip)}>
|
||||
<MyTag mr={2} colorSchema={PluginStatusMap[status].tagColor} type="borderFill">
|
||||
<MyTag
|
||||
display={'block'}
|
||||
className="unHoverStyle"
|
||||
mr={2}
|
||||
colorSchema={PluginStatusMap[status].tagColor}
|
||||
type="borderFill"
|
||||
>
|
||||
{t(PluginStatusMap[status].label)}
|
||||
</MyTag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{toolError && (
|
||||
<Flex
|
||||
bg={'red.50'}
|
||||
alignItems={'center'}
|
||||
h={6}
|
||||
px={2}
|
||||
rounded={'6px'}
|
||||
fontSize={'xs'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
<MyIcon name={'common/errorFill'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'}>{t(toolError as any)}</Box>
|
||||
</Flex>
|
||||
<MyTag colorSchema="red" type="fill" className="unHoverStyle">
|
||||
<MyIcon name={'common/error'} w={'14px'} mr={1} />
|
||||
<Box color={'red.600'} maxW={'150px'} className="textEllipsis">
|
||||
{t(toolError as any)}
|
||||
</Box>
|
||||
</MyTag>
|
||||
)}
|
||||
<DeleteIcon
|
||||
ml={2}
|
||||
{isUnconfigured && (
|
||||
<MyTag colorSchema="blue" type="fill" className="unHoverStyle">
|
||||
{t('app:wait_for_config')}
|
||||
</MyTag>
|
||||
)}
|
||||
|
||||
{/* Edit icon */}
|
||||
{hasFormInput && !toolError && (
|
||||
<MyIconButton
|
||||
className="hoverStyle"
|
||||
display={['block', 'none']}
|
||||
icon="common/setting"
|
||||
onClick={() => setConfigTool(item)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Delete icon */}
|
||||
<MyIconButton
|
||||
className="hoverStyle"
|
||||
display={['block', 'none']}
|
||||
ml={0.5}
|
||||
icon="delete"
|
||||
hoverBg="red.50"
|
||||
hoverColor="red.600"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setAppForm((state: AppFormEditFormType) => ({
|
||||
|
|
@ -181,7 +201,12 @@ const ToolSelect = ({
|
|||
setAppForm((state) => ({
|
||||
...state,
|
||||
selectedTools: state.selectedTools.map((item) =>
|
||||
item.pluginId === configTool.pluginId ? e : item
|
||||
item.pluginId === configTool.pluginId
|
||||
? {
|
||||
...e,
|
||||
configStatus: 'active'
|
||||
}
|
||||
: item
|
||||
)
|
||||
}));
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useCallback, useState, useMemo } from 'react';
|
||||
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { parseI18nString } from '@fastgpt/global/common/i18n/utils';
|
||||
|
|
@ -26,25 +25,24 @@ import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
|
|||
import { useMemoizedFn } from 'ahooks';
|
||||
import MyAvatar from '@fastgpt/web/components/common/Avatar';
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { type AppFormEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import type { SelectedToolItemType, AppFormEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { workflowStartNodeId } from '@/web/core/app/constants';
|
||||
import ConfigToolModal from '../../component/ConfigToolModal';
|
||||
import CostTooltip from '@/components/core/app/tool/CostTooltip';
|
||||
import { useSafeTranslation } from '@fastgpt/web/hooks/useSafeTranslation';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import ToolTagFilterBox from '@fastgpt/web/components/core/plugin/tool/TagFilterBox';
|
||||
import { getPluginToolTags } from '@/web/core/plugin/toolTag/api';
|
||||
import { types } from 'util';
|
||||
import { useRouter } from 'next/router';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { checkNeedsUserConfiguration, validateToolConfiguration } from '../../ChatAgent/utils';
|
||||
|
||||
type Props = {
|
||||
selectedTools: FlowNodeTemplateType[];
|
||||
chatConfig: AppFormEditFormType['chatConfig'];
|
||||
selectedModel: LLMModelItemType;
|
||||
onAddTool: (tool: FlowNodeTemplateType) => void;
|
||||
onAddTool: (tool: SelectedToolItemType) => void;
|
||||
onRemoveTool: (tool: NodeTemplateListItemType) => void;
|
||||
};
|
||||
|
||||
|
|
@ -110,8 +108,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
|
|||
setTemplateType(type);
|
||||
setParentId(parentId);
|
||||
},
|
||||
refreshDeps: [templateType, searchKey, parentId],
|
||||
errorToast: t('common:core.module.templates.Load plugin error')
|
||||
refreshDeps: [templateType, searchKey, parentId]
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -159,7 +156,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
|
|||
return (
|
||||
<MyModal
|
||||
isOpen
|
||||
title={t('common:core.app.Tool call')}
|
||||
title={t('app:tool_select')}
|
||||
iconSrc="core/app/toolCall"
|
||||
onClose={onClose}
|
||||
maxW={['90vw', '700px']}
|
||||
|
|
@ -257,97 +254,28 @@ const RenderList = React.memo(function RenderList({
|
|||
const { t } = useSafeTranslation();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const router = useRouter();
|
||||
|
||||
const [configTool, setConfigTool] = useState<FlowNodeTemplateType>();
|
||||
const onCloseConfigTool = useCallback(() => setConfigTool(undefined), []);
|
||||
const { toast } = useToast();
|
||||
|
||||
const { runAsync: onClickAdd, loading: isLoading } = useRequest2(
|
||||
async (template: NodeTemplateListItemType) => {
|
||||
const res = await getToolPreviewNode({ appId: template.id });
|
||||
|
||||
/* Invalid plugin check
|
||||
1. Reference type. but not tool description;
|
||||
2. Has dataset select
|
||||
3. Has dynamic external data
|
||||
*/
|
||||
const oneFileInput =
|
||||
res.inputs.filter((input) =>
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
|
||||
).length === 1;
|
||||
const canUploadFile =
|
||||
chatConfig?.fileSelectConfig?.canSelectFile || chatConfig?.fileSelectConfig?.canSelectImg;
|
||||
const invalidFileInput = oneFileInput && !!canUploadFile;
|
||||
if (
|
||||
res.inputs.some(
|
||||
(input) =>
|
||||
(input.renderTypeList.length === 1 &&
|
||||
input.renderTypeList[0] === FlowNodeInputTypeEnum.reference &&
|
||||
!input.toolDescription) ||
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectDataset) ||
|
||||
input.renderTypeList.includes(FlowNodeInputTypeEnum.addInputParam) ||
|
||||
(input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) && !invalidFileInput)
|
||||
)
|
||||
) {
|
||||
const toolValid = validateToolConfiguration({
|
||||
toolTemplate: res,
|
||||
canSelectFile: chatConfig?.fileSelectConfig?.canSelectFile,
|
||||
canSelectImg: chatConfig?.fileSelectConfig?.canSelectImg
|
||||
});
|
||||
if (!toolValid) {
|
||||
return toast({
|
||||
title: t('app:simple_tool_tips'),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
|
||||
// 判断是否可以直接添加工具,满足以下任一条件:
|
||||
// 1. 有工具描述
|
||||
// 2. 是模型选择类型
|
||||
// 3. 是文件上传类型且:已开启文件上传、非必填、只有一个文件上传输入
|
||||
const hasInputForm =
|
||||
res.inputs.length > 0 &&
|
||||
res.inputs.some((input) => {
|
||||
if (input.toolDescription) {
|
||||
return false;
|
||||
}
|
||||
if (input.key === NodeInputKeyEnum.forbidStream) {
|
||||
return false;
|
||||
}
|
||||
if (input.key === NodeInputKeyEnum.systemInputConfig) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if input has any of the form render types
|
||||
const formRenderTypes = [
|
||||
FlowNodeInputTypeEnum.input,
|
||||
FlowNodeInputTypeEnum.textarea,
|
||||
FlowNodeInputTypeEnum.numberInput,
|
||||
FlowNodeInputTypeEnum.switch,
|
||||
FlowNodeInputTypeEnum.select,
|
||||
FlowNodeInputTypeEnum.JSONEditor
|
||||
];
|
||||
|
||||
return formRenderTypes.some((type) => input.renderTypeList.includes(type));
|
||||
});
|
||||
|
||||
// 构建默认表单数据
|
||||
const defaultForm = {
|
||||
onAddTool({
|
||||
...res,
|
||||
inputs: res.inputs.map((input) => {
|
||||
// 如果是文件上传类型,设置为从工作流开始节点获取用户文件
|
||||
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)) {
|
||||
return {
|
||||
...input,
|
||||
value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]]
|
||||
};
|
||||
}
|
||||
return input;
|
||||
})
|
||||
};
|
||||
|
||||
if (hasInputForm) {
|
||||
setConfigTool(defaultForm);
|
||||
} else {
|
||||
onAddTool(defaultForm);
|
||||
}
|
||||
},
|
||||
{
|
||||
errorToast: t('common:core.module.templates.Load plugin error')
|
||||
configStatus: checkNeedsUserConfiguration(res) ? 'waitingForConfig' : 'active'
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -504,7 +432,7 @@ const RenderList = React.memo(function RenderList({
|
|||
_hover={{
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={() => router.push('/plugin/tool')}
|
||||
onClick={() => router.push('/dashboard/tool')}
|
||||
gap={1}
|
||||
bottom={0}
|
||||
right={[3, 6]}
|
||||
|
|
@ -515,13 +443,6 @@ const RenderList = React.memo(function RenderList({
|
|||
<MyIcon name="common/rightArrowLight" w="0.9rem" />
|
||||
</Flex>
|
||||
)}
|
||||
{!!configTool && (
|
||||
<ConfigToolModal
|
||||
configTool={configTool}
|
||||
onCloseConfigTool={onCloseConfigTool}
|
||||
onAddTool={onAddTool}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ const EditForm = ({
|
|||
<Flex alignItems={'center'}>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
<MyIcon name={'core/app/simpleMode/dataset'} w={'20px'} />
|
||||
<FormLabel ml={2}>{t('common:core.dataset.Choose Dataset')}</FormLabel>
|
||||
<FormLabel ml={2}>{t('app:dataset')}</FormLabel>
|
||||
</Flex>
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
|
|
|
|||
|
|
@ -551,6 +551,9 @@ export function form2AppWorkflow(
|
|||
value: formData.aiSettings.maxHistories
|
||||
};
|
||||
}
|
||||
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)) {
|
||||
input.value = [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]];
|
||||
}
|
||||
return input;
|
||||
}),
|
||||
outputs: tool.outputs
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ const ConfigToolModal = ({
|
|||
<MyModal
|
||||
isOpen
|
||||
isCentered
|
||||
title={t('common:core.app.ToolCall.Parameter setting')}
|
||||
title={t('app:tool_param_config')}
|
||||
iconSrc="core/app/toolCall"
|
||||
overflow={'auto'}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -69,8 +69,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
|
|||
onSuccess(_, [{ parentId = '' }]) {
|
||||
setParentId(parentId);
|
||||
},
|
||||
refreshDeps: [searchKey, parentId],
|
||||
errorToast: t('common:core.module.templates.Load plugin error')
|
||||
refreshDeps: [searchKey, parentId]
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -268,9 +267,6 @@ const RenderList = React.memo(function RenderList({
|
|||
} else {
|
||||
onAddTool(defaultForm);
|
||||
}
|
||||
},
|
||||
{
|
||||
errorToast: t('common:core.module.templates.Load plugin error')
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue