From ec30d7928690ae9d5a2c3adeaaedf9e1f4817046 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Tue, 18 Mar 2025 23:15:20 +0800 Subject: [PATCH 01/35] feat: custom dataset split sign (#4221) * feat: custom dataset split sign * feat: custom dataset split sign --- .../zh-cn/docs/development/upgrading/492.md | 20 ++++++ packages/global/common/string/textSplitter.ts | 31 ++++++--- packages/web/i18n/en/common.json | 1 - packages/web/i18n/en/dataset.json | 9 +++ packages/web/i18n/zh-CN/common.json | 1 - packages/web/i18n/zh-CN/dataset.json | 9 +++ packages/web/i18n/zh-Hant/common.json | 1 - packages/web/i18n/zh-Hant/dataset.json | 9 +++ .../Import/commonProgress/DataProcess.tsx | 66 ++++++++++++++----- 9 files changed, 121 insertions(+), 26 deletions(-) create mode 100644 docSite/content/zh-cn/docs/development/upgrading/492.md diff --git a/docSite/content/zh-cn/docs/development/upgrading/492.md b/docSite/content/zh-cn/docs/development/upgrading/492.md new file mode 100644 index 000000000..fb98bd53a --- /dev/null +++ b/docSite/content/zh-cn/docs/development/upgrading/492.md @@ -0,0 +1,20 @@ +--- +title: 'V4.9.2(进行中)' +description: 'FastGPT V4.9.2 更新说明' +icon: 'upgrade' +draft: false +toc: true +weight: 799 +--- + + +## 🚀 新增内容 + +1. 知识库分块增加自定义分隔符预设值,同时支持自定义换行符分割。 + +## ⚙️ 优化 + +1. 导出对话日志时,支持导出成员名。 + +## 🐛 修复 + diff --git a/packages/global/common/string/textSplitter.ts b/packages/global/common/string/textSplitter.ts index fe1212ba7..8c56029dd 100644 --- a/packages/global/common/string/textSplitter.ts +++ b/packages/global/common/string/textSplitter.ts @@ -1,5 +1,4 @@ import { getErrText } from '../error/utils'; -import { replaceRegChars } from './tools'; export const CUSTOM_SPLIT_SIGN = '-----CUSTOM_SPLIT_SIGN-----'; @@ -115,9 +114,10 @@ const commonSplit = (props: SplitProps): SplitResponse => { // The larger maxLen is, the next sentence is less likely to trigger splitting const markdownIndex = 4; const forbidOverlapIndex = 8; - const stepReges: { reg: RegExp; maxLen: number }[] = [ + + const stepReges: { reg: RegExp | string; maxLen: number }[] = [ ...customReg.map((text) => ({ - reg: new RegExp(`(${replaceRegChars(text)})`, 'g'), + reg: text.replaceAll('\\n', '\n'), maxLen: chunkLen * 1.4 })), { reg: /^(#\s[^\n]+\n)/gm, maxLen: chunkLen * 1.2 }, @@ -161,17 +161,32 @@ const commonSplit = (props: SplitProps): SplitResponse => { const { reg } = stepReges[step]; - const splitTexts = text - .replace( + const replaceText = (() => { + if (typeof reg === 'string') { + let tmpText = text; + reg.split('|').forEach((itemReg) => { + tmpText = tmpText.replaceAll( + itemReg, + (() => { + if (isCustomStep) return splitMarker; + if (isMarkdownSplit) return `${splitMarker}$1`; + return `$1${splitMarker}`; + })() + ); + }); + return tmpText; + } + + return text.replace( reg, (() => { if (isCustomStep) return splitMarker; if (isMarkdownSplit) return `${splitMarker}$1`; return `$1${splitMarker}`; })() - ) - .split(`${splitMarker}`) - .filter((part) => part.trim()); + ); + })(); + const splitTexts = replaceText.split(splitMarker).filter((part) => part.trim()); return splitTexts .map((text) => { diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index d62ea1bd9..cd36d6a6a 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -570,7 +570,6 @@ "core.dataset.import.Custom process desc": "Customize segmentation and preprocessing rules", "core.dataset.import.Custom prompt": "Custom Prompt", "core.dataset.import.Custom split char": "Custom Separator", - "core.dataset.import.Custom split char Tips": "Allows you to segment based on custom separators. Usually used for pre-processed data, using specific separators for precise segmentation.", "core.dataset.import.Custom text": "Custom Text", "core.dataset.import.Custom text desc": "Manually enter a piece of text as a dataset", "core.dataset.import.Data process params": "Data Processing Parameters", diff --git a/packages/web/i18n/en/dataset.json b/packages/web/i18n/en/dataset.json index 7a57c3e97..e4760e04d 100644 --- a/packages/web/i18n/en/dataset.json +++ b/packages/web/i18n/en/dataset.json @@ -25,6 +25,7 @@ "core.dataset.import.Adjust parameters": "Adjust parameters", "custom_data_process_params": "Custom", "custom_data_process_params_desc": "Customize data processing rules", + "custom_split_sign_tip": "Allows you to chunk according to custom delimiters. \nUsually used for processed data, using specific separators for precise chunking. \nYou can use the | symbol to represent multiple splitters, such as: \".|.\" to represent a period in Chinese and English.\n\nTry to avoid using special symbols related to regular, such as: * () [] {}, etc.", "data.ideal_chunk_length": "ideal block length", "data_amount": "{{dataAmount}} Datas, {{indexAmount}} Indexes", "data_index_num": "Index {{index}}", @@ -86,6 +87,14 @@ "retain_collection": "Adjust Training Parameters", "retrain_task_submitted": "The retraining task has been submitted", "same_api_collection": "The same API set exists", + "split_sign_break": "1 newline character", + "split_sign_break2": "2 newline characters", + "split_sign_custom": "Customize", + "split_sign_exclamatiob": "exclamation mark", + "split_sign_null": "Not set", + "split_sign_period": "period", + "split_sign_question": "question mark", + "split_sign_semicolon": "semicolon", "start_sync_website_tip": "Confirm to start synchronizing data? \nThe old data will be deleted and retrieved again, please confirm!", "sync_collection_failed": "Synchronization collection error, please check whether the source file can be accessed normally", "sync_schedule": "Timing synchronization", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index 3c2d6ae85..52445499c 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -574,7 +574,6 @@ "core.dataset.import.Custom process desc": "自定义设置数据处理规则", "core.dataset.import.Custom prompt": "自定义提示词", "core.dataset.import.Custom split char": "自定义分隔符", - "core.dataset.import.Custom split char Tips": "允许你根据自定义的分隔符进行分块。通常用于已处理好的数据,使用特定的分隔符来精确分块。", "core.dataset.import.Custom text": "自定义文本", "core.dataset.import.Custom text desc": "手动输入一段文本作为数据集", "core.dataset.import.Data process params": "数据处理参数", diff --git a/packages/web/i18n/zh-CN/dataset.json b/packages/web/i18n/zh-CN/dataset.json index 7dd79ee32..11b48d81c 100644 --- a/packages/web/i18n/zh-CN/dataset.json +++ b/packages/web/i18n/zh-CN/dataset.json @@ -25,6 +25,7 @@ "core.dataset.import.Adjust parameters": "调整参数", "custom_data_process_params": "自定义", "custom_data_process_params_desc": "自定义设置数据处理规则", + "custom_split_sign_tip": "允许你根据自定义的分隔符进行分块。通常用于已处理好的数据,使用特定的分隔符来精确分块。可以使用 | 符号表示多个分割符,例如:“。|.” 表示中英文句号。\n尽量避免使用正则相关特殊符号,例如: * () [] {} 等。", "data.ideal_chunk_length": "理想分块长度", "data_amount": "{{dataAmount}} 组数据, {{indexAmount}} 组索引", "data_index_num": "索引 {{index}}", @@ -86,6 +87,14 @@ "retain_collection": "调整训练参数", "retrain_task_submitted": "重新训练任务已提交", "same_api_collection": "存在相同的 API 集合", + "split_sign_break": "1 个换行符", + "split_sign_break2": "2 个换行符", + "split_sign_custom": "自定义", + "split_sign_exclamatiob": "感叹号", + "split_sign_null": "不设置", + "split_sign_period": "句号", + "split_sign_question": "问号", + "split_sign_semicolon": "分号", "start_sync_website_tip": "确认开始同步数据?将会删除旧数据后重新获取,请确认!", "sync_collection_failed": "同步集合错误,请检查是否能正常访问源文件", "sync_schedule": "定时同步", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index fe11b5474..fff2a0c78 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -569,7 +569,6 @@ "core.dataset.import.Custom process desc": "自訂設定資料處理規則", "core.dataset.import.Custom prompt": "自訂提示詞", "core.dataset.import.Custom split char": "自訂分隔符", - "core.dataset.import.Custom split char Tips": "允許您根據自訂的分隔符進行分割。通常用於已處理好的資料,使用特定的分隔符來精確分割。", "core.dataset.import.Custom text": "自訂文字", "core.dataset.import.Custom text desc": "手動輸入一段文字作為資料集", "core.dataset.import.Data process params": "資料處理參數", diff --git a/packages/web/i18n/zh-Hant/dataset.json b/packages/web/i18n/zh-Hant/dataset.json index 712956760..4a7a9fa97 100644 --- a/packages/web/i18n/zh-Hant/dataset.json +++ b/packages/web/i18n/zh-Hant/dataset.json @@ -25,6 +25,7 @@ "core.dataset.import.Adjust parameters": "調整參數", "custom_data_process_params": "自訂", "custom_data_process_params_desc": "自訂資料處理規則", + "custom_split_sign_tip": "允許你根據自定義的分隔符進行分塊。\n通常用於已處理好的數據,使用特定的分隔符來精確分塊。\n可以使用 | 符號表示多個分割符,例如:“。|.” 表示中英文句號。\n\n盡量避免使用正則相關特殊符號,例如: * () [] {} 等。", "data.ideal_chunk_length": "理想分塊長度", "data_amount": "{{dataAmount}} 組數據, {{indexAmount}} 組索引", "data_index_num": "索引 {{index}}", @@ -86,6 +87,14 @@ "retain_collection": "調整訓練參數", "retrain_task_submitted": "重新訓練任務已提交", "same_api_collection": "存在相同的 API 集合", + "split_sign_break": "1 個換行符", + "split_sign_break2": "2 個換行符", + "split_sign_custom": "自定義", + "split_sign_exclamatiob": "驚嘆號", + "split_sign_null": "不設置", + "split_sign_period": "句號", + "split_sign_question": "問號", + "split_sign_semicolon": "分號", "start_sync_website_tip": "確認開始同步資料?\n將會刪除舊資料後重新獲取,請確認!", "sync_collection_failed": "同步集合錯誤,請檢查是否能正常存取來源文件", "sync_schedule": "定時同步", diff --git a/projects/app/src/pageComponents/dataset/detail/Import/commonProgress/DataProcess.tsx b/projects/app/src/pageComponents/dataset/detail/Import/commonProgress/DataProcess.tsx index 898a2297b..5a1dd5065 100644 --- a/projects/app/src/pageComponents/dataset/detail/Import/commonProgress/DataProcess.tsx +++ b/projects/app/src/pageComponents/dataset/detail/Import/commonProgress/DataProcess.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Box, Flex, @@ -36,6 +36,7 @@ import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import { shadowLight } from '@fastgpt/web/styles/theme'; import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext'; +import MySelect from '@fastgpt/web/components/common/MySelect'; function DataProcess() { const { t } = useTranslation(); @@ -44,18 +45,39 @@ function DataProcess() { const { goToNext, processParamsForm, chunkSizeField, minChunkSize, maxChunkSize } = useContextSelector(DatasetImportContext, (v) => v); const datasetDetail = useContextSelector(DatasetPageContext, (v) => v.datasetDetail); - const { setValue, register, watch } = processParamsForm; + const { setValue, register, watch, getValues } = processParamsForm; const trainingType = watch('trainingType'); const chunkSettingMode = watch('chunkSettingMode'); - const qaPrompt = watch('qaPrompt'); + const qaPrompt = watch('qaPrompt'); const { isOpen: isOpenCustomPrompt, onOpen: onOpenCustomPrompt, onClose: onCloseCustomPrompt } = useDisclosure(); + const customSplitList = [ + { label: t('dataset:split_sign_null'), value: '' }, + { label: t('dataset:split_sign_break'), value: '\\n' }, + { label: t('dataset:split_sign_break2'), value: '\\n\\n' }, + { label: t('dataset:split_sign_period'), value: '.|。' }, + { label: t('dataset:split_sign_exclamatiob'), value: '!|!' }, + { label: t('dataset:split_sign_question'), value: '?|?' }, + { label: t('dataset:split_sign_semicolon'), value: ';|;' }, + { label: '=====', value: '=====' }, + { label: t('dataset:split_sign_custom'), value: 'Other' } + ]; + + const [customListSelectValue, setCustomListSelectValue] = useState(getValues('customSplitChar')); + useEffect(() => { + if (customListSelectValue === 'Other') { + setValue('customSplitChar', ''); + } else { + setValue('customSplitChar', customListSelectValue); + } + }, [customListSelectValue, setValue]); + const trainingModeList = useMemo(() => { const list = Object.entries(DatasetCollectionDataProcessModeMap); return list @@ -248,19 +270,33 @@ function DataProcess() { {t('common:core.dataset.import.Custom split char')} - - - - + + + + + + list={customSplitList} + size={'sm'} + bg={'myGray.50'} + value={customListSelectValue} + h={'32px'} + onChange={(val) => { + setCustomListSelectValue(val); + }} + /> + + {customListSelectValue === 'Other' && ( + + )} + {showQAPromptInput && ( From d52700c645c80590c7073366f4bd20643e400c19 Mon Sep 17 00:00:00 2001 From: heheer Date: Wed, 19 Mar 2025 11:57:30 +0800 Subject: [PATCH 02/35] add external variable debug (#4204) * add external variable debug * fix ui * plugin variables --- packages/web/i18n/en/common.json | 2 + packages/web/i18n/zh-CN/common.json | 2 + packages/web/i18n/zh-Hant/common.json | 2 + .../ChatBox/components/VariableInput.tsx | 217 +++++++++++++++--- .../ChatBox/components/VariablePopover.tsx | 98 ++++++++ .../core/chat/ChatContainer/ChatBox/index.tsx | 55 ++++- .../PluginRunBox/components/RenderInput.tsx | 66 +++--- .../components/renderPluginInput.tsx | 18 +- .../app/detail/SimpleApp/ChatTest.tsx | 12 +- .../WorkflowComponents/Flow/ChatTest.tsx | 6 +- .../Flow/hooks/useDebug.tsx | 41 +++- .../nodes/NodePluginIO/InputTypeConfig.tsx | 21 +- .../pageComponents/app/detail/useChatTest.tsx | 2 +- .../src/pageComponents/chat/ChatHeader.tsx | 12 +- .../web/core/chat/context/chatItemContext.tsx | 16 +- 15 files changed, 473 insertions(+), 97 deletions(-) create mode 100644 projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariablePopover.tsx diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index cd36d6a6a..9ec8e914b 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -430,6 +430,8 @@ "core.chat.Start Chat": "Start Chat", "core.chat.Type a message": "Enter a Question, Press [Enter] to Send / Press [Ctrl(Alt/Shift) + Enter] for New Line", "core.chat.Unpin": "Unpin", + "core.chat.Variable_Visiable_in_test": "This variable is not visible in the login-free link", + "core.chat.Visiable_in_test": "Custom variables are not visible in login-free links", "core.chat.You need to a chat app": "You Do Not Have an Available App", "core.chat.error.Chat error": "Chat Error", "core.chat.error.Messages empty": "API Content is Empty, Possibly Due to Text Being Too Long", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index 52445499c..451ccf4cd 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -433,6 +433,8 @@ "core.chat.Start Chat": "开始对话", "core.chat.Type a message": "输入问题,发送 [Enter]/换行 [Ctrl(Alt/Shift) + Enter]", "core.chat.Unpin": "取消置顶", + "core.chat.Variable_Visiable_in_test": "该变量在免登录链接中不可见", + "core.chat.Visiable_in_test": "自定义变量在免登录链接中不可见", "core.chat.You need to a chat app": "你没有可用的应用", "core.chat.error.Chat error": "对话出现异常", "core.chat.error.Messages empty": "接口内容为空,可能文本超长了~", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index fff2a0c78..53c122b7e 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -429,6 +429,8 @@ "core.chat.Start Chat": "開始對話", "core.chat.Type a message": "輸入問題,按 [Enter] 傳送 / 按 [Ctrl(Alt/Shift) + Enter] 換行", "core.chat.Unpin": "取消釘選", + "core.chat.Variable_Visiable_in_test": "該變量在免登錄鏈接中不可見", + "core.chat.Visiable_in_test": "自定義變量在免登錄鏈接中不可見", "core.chat.You need to a chat app": "您沒有可用的應用程式", "core.chat.error.Chat error": "對話發生錯誤", "core.chat.error.Messages empty": "API 內容為空,可能是文字過長", diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariableInput.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariableInput.tsx index 69593c36d..5d06bdd5f 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariableInput.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariableInput.tsx @@ -1,20 +1,26 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { Controller, UseFormReturn } from 'react-hook-form'; import { useTranslation } from 'next-i18next'; -import { Box, Button, Card, Textarea } from '@chakra-ui/react'; +import { Box, Button, Card, Flex, Switch, Textarea } from '@chakra-ui/react'; import ChatAvatar from './ChatAvatar'; import { MessageCardStyle } from '../constants'; -import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants'; +import { + VariableInputEnum, + WorkflowIOValueTypeEnum +} from '@fastgpt/global/core/workflow/constants'; import MySelect from '@fastgpt/web/components/common/MySelect'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { ChatBoxInputFormType } from '../type.d'; import { useContextSelector } from 'use-context-selector'; -import { ChatBoxContext } from '../Provider'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import { VariableItemType } from '@fastgpt/global/core/app/type'; import MyTextarea from '@/components/common/Textarea/MyTextarea'; import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; +import { ChatBoxContext } from '../Provider'; +import dynamic from 'next/dynamic'; + +const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor')); export const VariableInputItem = ({ item, @@ -108,23 +114,118 @@ export const VariableInputItem = ({ ); }; +export const ExternalVariableInputItem = ({ + item, + variablesForm, + showTag = false +}: { + item: VariableItemType; + variablesForm: UseFormReturn; + showTag?: boolean; +}) => { + const { t } = useTranslation(); + const { register, control } = variablesForm; + + return ( + + + {item.label} + {item.required && ( + + * + + )} + {item.description && } + {showTag && ( + + + {t('common:core.chat.Variable_Visiable_in_test')} + + )} + + { + if (item.valueType === WorkflowIOValueTypeEnum.boolean) { + return value !== undefined; + } + return !!value; + } + }} + render={({ field: { onChange, value } }) => { + if (item.valueType === WorkflowIOValueTypeEnum.string) { + return ( + + ); + } + if (item.valueType === WorkflowIOValueTypeEnum.number) { + return ; + } + if (item.valueType === WorkflowIOValueTypeEnum.boolean) { + return ; + } + return ; + }} + /> + + ); +}; + const VariableInput = ({ chatForm, - chatStarted + chatStarted, + showExternalVariables = false }: { - chatStarted: boolean; chatForm: UseFormReturn; + chatStarted: boolean; + showExternalVariables?: boolean; }) => { const { t } = useTranslation(); const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app?.avatar); const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm); const variableList = useContextSelector(ChatBoxContext, (v) => v.variableList); + const allVariableList = useContextSelector(ChatBoxContext, (v) => v.allVariableList); + + const externalVariableList = useMemo( + () => + allVariableList.filter((item) => + showExternalVariables ? item.type === VariableInputEnum.custom : false + ), + [allVariableList, showExternalVariables] + ); const { getValues, setValue, handleSubmit: handleSubmitChat } = variablesForm; useEffect(() => { - variableList.forEach((item) => { + allVariableList.forEach((item) => { const val = getValues(`variables.${item.key}`); if (item.defaultValue !== undefined && (val === undefined || val === null || val === '')) { setValue(`variables.${item.key}`, item.defaultValue); @@ -135,34 +236,80 @@ const VariableInput = ({ return ( - - - {variableList.map((item) => ( - - ))} - {!chatStarted && ( - - - - )} - - + {externalVariableList.length > 0 && ( + + + + + {t('common:core.chat.Visiable_in_test')} + + {externalVariableList.map((item) => ( + + ))} + {variableList.length === 0 && !chatStarted && ( + + + + )} + + + )} + + {variableList.length > 0 && ( + + + {variableList.map((item) => ( + + ))} + {!chatStarted && ( + + + + )} + + + )} ); }; diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariablePopover.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariablePopover.tsx new file mode 100644 index 000000000..d75ada0ab --- /dev/null +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariablePopover.tsx @@ -0,0 +1,98 @@ +import { Box, Button, Flex } from '@chakra-ui/react'; +import MyPopover from '@fastgpt/web/components/common/MyPopover'; +import { useTranslation } from 'next-i18next'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import { useContextSelector } from 'use-context-selector'; +import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; +import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants'; +import { useEffect } from 'react'; +import { ExternalVariableInputItem, VariableInputItem } from './VariableInput'; +import MyDivider from '@fastgpt/web/components/common/MyDivider'; + +const VariablePopover = ({ + showExternalVariables = false +}: { + showExternalVariables?: boolean; +}) => { + const { t } = useTranslation(); + const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm); + const variables = useContextSelector( + ChatItemContext, + (v) => v.chatBoxData?.app?.chatConfig?.variables ?? [] + ); + const variableList = variables.filter((item) => item.type !== VariableInputEnum.custom); + const externalVariableList = variables.filter((item) => + showExternalVariables ? item.type === VariableInputEnum.custom : false + ); + + const hasExternalVariable = externalVariableList.length > 0; + const hasVariable = variableList.length > 0; + + const { getValues, setValue } = variablesForm; + + useEffect(() => { + variables.forEach((item) => { + const val = getValues(`variables.${item.key}`); + if (item.defaultValue !== undefined && (val === undefined || val === null || val === '')) { + setValue(`variables.${item.key}`, item.defaultValue); + } + }); + }, [variables]); + + return ( + }> + {t('common:core.module.Variable')} + + } + > + {({ onClose }) => ( + + {hasExternalVariable && ( + + + + {t('common:core.chat.Visiable_in_test')} + + {externalVariableList.map((item) => ( + + ))} + + )} + {hasExternalVariable && hasVariable && } + {hasVariable && ( + + {variableList.map((item) => ( + + ))} + + )} + + + + + )} + + ); +}; + +export default VariablePopover; diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx index 993f88f7e..0dc4c5bd2 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx @@ -65,6 +65,7 @@ import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import TimeBox from './components/TimeBox'; import MyBox from '@fastgpt/web/components/common/MyBox'; +import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants'; const ResponseTags = dynamic(() => import('./components/ResponseTags')); const FeedbackModal = dynamic(() => import('./components/FeedbackModal')); @@ -103,7 +104,8 @@ const ChatBox = ({ showVoiceIcon = true, showEmptyIntro = false, active = true, - onStartChat + onStartChat, + chatType }: Props) => { const ScrollContainerRef = useRef(null); const { t } = useTranslation(); @@ -129,6 +131,8 @@ const ChatBox = ({ const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); const ChatBoxRef = useContextSelector(ChatItemContext, (v) => v.ChatBoxRef); const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm); + const setIsVariableVisible = useContextSelector(ChatItemContext, (v) => v.setIsVariableVisible); + const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const setChatRecords = useContextSelector(ChatRecordContext, (v) => v.setChatRecords); const isChatRecordsLoaded = useContextSelector(ChatRecordContext, (v) => v.isChatRecordsLoaded); @@ -150,6 +154,12 @@ const ChatBox = ({ // Workflow running, there are user input or selection const isInteractive = useMemo(() => checkIsInteractiveByHistories(chatRecords), [chatRecords]); + const externalVariableList = useMemo(() => { + if (chatType === 'chat') { + return allVariableList.filter((item) => item.type === VariableInputEnum.custom); + } + return []; + }, [allVariableList, chatType]); // compute variable input is finish. const chatForm = useForm({ defaultValues: { @@ -162,7 +172,9 @@ const ChatBox = ({ const chatStartedWatch = watch('chatStarted'); const chatStarted = chatBoxData?.appId === appId && - (chatStartedWatch || chatRecords.length > 0 || variableList.length === 0); + (chatStartedWatch || + chatRecords.length > 0 || + [...variableList, ...externalVariableList].length === 0); // 滚动到底部 const scrollToBottom = useMemoizedFn((behavior: 'smooth' | 'auto' = 'smooth', delay = 0) => { @@ -891,6 +903,33 @@ const ChatBox = ({ } })); + // Visibility check + useEffect(() => { + const checkVariableVisibility = () => { + if (!ScrollContainerRef.current) return; + const container = ScrollContainerRef.current; + const variableInput = container.querySelector('#variable-input'); + if (!variableInput) return; + + const containerRect = container.getBoundingClientRect(); + const elementRect = variableInput.getBoundingClientRect(); + + setIsVariableVisible( + elementRect.bottom > containerRect.top && elementRect.top < containerRect.bottom + ); + }; + + const container = ScrollContainerRef.current; + if (container) { + container.addEventListener('scroll', checkVariableVisibility); + checkVariableVisibility(); + + return () => { + container.removeEventListener('scroll', checkVariableVisibility); + }; + } + }, [setIsVariableVisible]); + const RenderRecords = useMemo(() => { return ( } {!!welcomeText && } {/* variable input */} - {!!variableList?.length && ( - + {(!!variableList?.length || !!externalVariableList?.length) && ( + + + )} {/* chat history */} @@ -1006,7 +1051,9 @@ const ChatBox = ({ chatForm, chatRecords, chatStarted, + chatType, delOneMessage, + externalVariableList?.length, isChatting, onAddUserDislike, onAddUserLike, diff --git a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/RenderInput.tsx b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/RenderInput.tsx index 240a5a381..b20212532 100644 --- a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/RenderInput.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/RenderInput.tsx @@ -18,6 +18,7 @@ import { ChatBoxInputFormType } from '../../ChatBox/type'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext'; +import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; const RenderInput = () => { const { t } = useTranslation(); @@ -213,36 +214,43 @@ const RenderInput = () => { )} {/* Filed */} - {formatPluginInputs.map((input) => { - return ( - { - if (!input.required) return true; - if (input.valueType === WorkflowIOValueTypeEnum.boolean) { - return value !== undefined; + {formatPluginInputs + .filter((input) => { + if (outLinkAuthData && Object.keys(outLinkAuthData).length > 0) { + return input.renderTypeList[0] !== FlowNodeInputTypeEnum.customVariable; + } + return true; + }) + .map((input) => { + return ( + { + if (!input.required) return true; + if (input.valueType === WorkflowIOValueTypeEnum.boolean) { + return value !== undefined; + } + return !!value; } - return !!value; - } - }} - render={({ field: { onChange, value } }) => { - return ( - - ); - }} - /> - ); - })} + }} + render={({ field: { onChange, value } }) => { + return ( + + ); + }} + /> + ); + })} {/* Run Button */} {onStartChat && onNewChat && ( diff --git a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx index 665a1a6f0..ba28be3ef 100644 --- a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx @@ -157,9 +157,6 @@ const RenderPluginInput = ({ const { llmModelList } = useSystemStore(); const render = (() => { - if (inputType === FlowNodeInputTypeEnum.customVariable) { - return null; - } if (inputType === FlowNodeInputTypeEnum.select && input.list) { return ( @@ -246,6 +243,21 @@ const RenderPluginInput = ({ {t(input.label as any)} {input.description && } + {inputType === FlowNodeInputTypeEnum.customVariable && ( + + + {t('common:core.chat.Variable_Visiable_in_test')} + + )} )} diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx b/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx index 7a4b2dabb..a1611b7a8 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx +++ b/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx @@ -1,4 +1,4 @@ -import { Box, Flex, IconButton } from '@chakra-ui/react'; +import { Box, Button, Flex, IconButton } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; import React, { useEffect, useMemo } from 'react'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; @@ -10,12 +10,14 @@ import { form2AppWorkflow } from '@/web/core/app/utils'; import { useContextSelector } from 'use-context-selector'; import { AppContext } from '../context'; import { useChatTest } from '../useChatTest'; +import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import ChatRecordContextProvider from '@/web/core/chat/context/chatRecordContext'; import { useChatStore } from '@/web/core/chat/context/useChatStore'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { cardStyles } from '../constants'; import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList'; +import VariablePopover from '@/components/core/chat/ChatContainer/ChatBox/components/VariablePopover'; type Props = { appForm: AppSimpleEditFormType; @@ -27,6 +29,8 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => { const { appDetail } = useContextSelector(AppContext, (v) => v); const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData); const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData); + // form2AppWorkflow dependent allDatasets + const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible); const [workflowData, setWorkflowData] = useSafeState({ nodes: appDetail.modules || [], @@ -62,10 +66,12 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => { {...cardStyles} boxShadow={'3'} > - - + + {t('app:chat_debug')} + {!isVariableVisible && } + { const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData); const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData); + const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); return ( @@ -115,10 +117,12 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => { bg={'myGray.25'} borderBottom={'1px solid #F4F4F7'} > - + {t('common:core.chat.Run test')} + {!isVariableVisible && } + { const appDetail = useContextSelector(AppContext, (v) => v.appDetail); - const filteredVar = useMemo(() => { - const variables = appDetail.chatConfig?.variables; - return variables?.filter((item) => item.type !== VariableInputEnum.custom) || []; + const { filteredVar, customVar, variables } = useMemo(() => { + const variables = appDetail.chatConfig?.variables || []; + return { + filteredVar: variables.filter((item) => item.type !== VariableInputEnum.custom) || [], + customVar: variables.filter((item) => item.type === VariableInputEnum.custom) || [], + variables + }; }, [appDetail.chatConfig?.variables]); const [defaultGlobalVariables, setDefaultGlobalVariables] = useState>( - filteredVar.reduce( + variables.reduce( (acc, item) => { acc[item.key] = item.defaultValue; return acc; @@ -241,7 +248,7 @@ export const useDebug = () => { px={0} > - {filteredVar.length > 0 && ( + {variables.length > 0 && ( gap={3} ml={-2} @@ -256,6 +263,14 @@ export const useDebug = () => { /> )} + {customVar.map((item) => ( + + ))} {filteredVar.map((item) => ( { ); }, [ - defaultGlobalVariables, - filteredVar, - onStartNodeDebug, - runtimeEdges, - runtimeNodeId, runtimeNodes, - t + runtimeEdges, + defaultGlobalVariables, + t, + variables.length, + customVar, + filteredVar, + runtimeNodeId, + onStartNodeDebug ]); return { diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig.tsx index bb8367dfa..ffa5071e5 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig.tsx @@ -297,8 +297,10 @@ const InputTypeConfig = ({ {t('common:core.module.Default Value')} - - {inputType === FlowNodeInputTypeEnum.numberInput && ( + + {(inputType === FlowNodeInputTypeEnum.numberInput || + (inputType === VariableInputEnum.custom && + valueType === WorkflowIOValueTypeEnum.number)) && ( )} {(inputType === FlowNodeInputTypeEnum.input || - inputType === VariableInputEnum.custom) && ( + (inputType === VariableInputEnum.custom && + valueType === WorkflowIOValueTypeEnum.string)) && ( )} - {inputType === FlowNodeInputTypeEnum.JSONEditor && ( + {(inputType === FlowNodeInputTypeEnum.JSONEditor || + (inputType === VariableInputEnum.custom && + ![ + WorkflowIOValueTypeEnum.number, + WorkflowIOValueTypeEnum.string, + WorkflowIOValueTypeEnum.boolean + ].includes(valueType))) && ( )} - {inputType === FlowNodeInputTypeEnum.switch && ( + {(inputType === FlowNodeInputTypeEnum.switch || + (inputType === VariableInputEnum.custom && + valueType === WorkflowIOValueTypeEnum.boolean)) && ( )} {inputType === FlowNodeInputTypeEnum.select && ( diff --git a/projects/app/src/pageComponents/app/detail/useChatTest.tsx b/projects/app/src/pageComponents/app/detail/useChatTest.tsx index a6602702e..222ae0c48 100644 --- a/projects/app/src/pageComponents/app/detail/useChatTest.tsx +++ b/projects/app/src/pageComponents/app/detail/useChatTest.tsx @@ -140,7 +140,7 @@ export const useChatTest = ({ appId={appId} chatId={chatId} showMarkIcon - chatType="chat" + chatType={'chat'} onStartChat={startChat} /> ) diff --git a/projects/app/src/pageComponents/chat/ChatHeader.tsx b/projects/app/src/pageComponents/chat/ChatHeader.tsx index 008c09bb5..1b5eb0b31 100644 --- a/projects/app/src/pageComponents/chat/ChatHeader.tsx +++ b/projects/app/src/pageComponents/chat/ChatHeader.tsx @@ -22,6 +22,7 @@ import { import { getMyApps } from '@/web/core/app/api'; import SelectOneResource from '@/components/common/folder/SelectOneResource'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; +import VariablePopover from '@/components/core/chat/ChatContainer/ChatBox/components/VariablePopover'; const ChatHeader = ({ history, @@ -38,7 +39,10 @@ const ChatHeader = ({ const { isPc } = useSystem(); const chatData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); + const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible); const isPlugin = chatData.app.type === AppTypeEnum.plugin; + const router = useRouter(); + const isChat = router.pathname === '/chat'; return isPc && isPlugin ? null : ( )} - {/* control */} - {!isPlugin && } + + {!isVariableVisible && } + + {/* control */} + {!isPlugin && } + ); }; diff --git a/projects/app/src/web/core/chat/context/chatItemContext.tsx b/projects/app/src/web/core/chat/context/chatItemContext.tsx index 3193d7eeb..b924627c0 100644 --- a/projects/app/src/web/core/chat/context/chatItemContext.tsx +++ b/projects/app/src/web/core/chat/context/chatItemContext.tsx @@ -74,6 +74,8 @@ type ChatItemContextType = { quoteData?: QuoteDataType; setQuoteData: React.Dispatch>; + isVariableVisible: boolean; + setIsVariableVisible: React.Dispatch>; } & ContextProps; export const ChatItemContext = createContext({ @@ -97,6 +99,10 @@ export const ChatItemContext = createContext({ quoteData: undefined, setQuoteData: function (value: React.SetStateAction): void { throw new Error('Function not implemented.'); + }, + isVariableVisible: true, + setIsVariableVisible: function (value: React.SetStateAction): void { + throw new Error('Function not implemented.'); } }); @@ -116,6 +122,8 @@ const ChatItemContextProvider = ({ const ChatBoxRef = useRef(null); const variablesForm = useForm(); const [quoteData, setQuoteData] = useState(); + const [isVariableVisible, setIsVariableVisible] = useState(true); + const [chatBoxData, setChatBoxData] = useState({ ...defaultChatData }); @@ -172,7 +180,9 @@ const ChatItemContextProvider = ({ showNodeStatus, quoteData, - setQuoteData + setQuoteData, + isVariableVisible, + setIsVariableVisible }; }, [ chatBoxData, @@ -187,7 +197,9 @@ const ChatItemContextProvider = ({ // isShowFullText, showNodeStatus, quoteData, - setQuoteData + setQuoteData, + isVariableVisible, + setIsVariableVisible ]); return {children}; From 077350e651450f647528f3bad0e96f098288c0ae Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Wed, 19 Mar 2025 12:35:09 +0800 Subject: [PATCH 03/35] perf: custom varialbe (#4225) --- .../zh-cn/docs/development/upgrading/492.md | 1 + packages/web/i18n/en/chat.json | 1 + packages/web/i18n/en/common.json | 2 - packages/web/i18n/zh-CN/chat.json | 1 + packages/web/i18n/zh-CN/common.json | 2 - packages/web/i18n/zh-Hant/chat.json | 1 + packages/web/i18n/zh-Hant/common.json | 2 - .../ChatBox/components/VariableInput.tsx | 43 ++++++------------- .../ChatBox/components/VariablePopover.tsx | 2 +- .../core/chat/ChatContainer/ChatBox/index.tsx | 4 +- .../components/renderPluginInput.tsx | 2 +- .../app/detail/SimpleApp/ChatTest.tsx | 2 +- .../WorkflowComponents/Flow/ChatTest.tsx | 2 +- 13 files changed, 23 insertions(+), 42 deletions(-) diff --git a/docSite/content/zh-cn/docs/development/upgrading/492.md b/docSite/content/zh-cn/docs/development/upgrading/492.md index fb98bd53a..3a7f9bd79 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/492.md +++ b/docSite/content/zh-cn/docs/development/upgrading/492.md @@ -11,6 +11,7 @@ weight: 799 ## 🚀 新增内容 1. 知识库分块增加自定义分隔符预设值,同时支持自定义换行符分割。 +2. 外部变量改名:自定义变量。 并且支持在测试时调试,在分享链接中,该变量直接隐藏。 ## ⚙️ 优化 diff --git a/packages/web/i18n/en/chat.json b/packages/web/i18n/en/chat.json index c46326eaf..280b57410 100644 --- a/packages/web/i18n/en/chat.json +++ b/packages/web/i18n/en/chat.json @@ -59,6 +59,7 @@ "to_dataset": "Go to the Knowledge Base", "unsupported_file_type": "Unsupported file types", "upload": "Upload", + "variable_invisable_in_share": "Custom variables are not visible in login-free links", "view_citations": "View References", "web_site_sync": "Web Site Sync" } diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 9ec8e914b..cd36d6a6a 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -430,8 +430,6 @@ "core.chat.Start Chat": "Start Chat", "core.chat.Type a message": "Enter a Question, Press [Enter] to Send / Press [Ctrl(Alt/Shift) + Enter] for New Line", "core.chat.Unpin": "Unpin", - "core.chat.Variable_Visiable_in_test": "This variable is not visible in the login-free link", - "core.chat.Visiable_in_test": "Custom variables are not visible in login-free links", "core.chat.You need to a chat app": "You Do Not Have an Available App", "core.chat.error.Chat error": "Chat Error", "core.chat.error.Messages empty": "API Content is Empty, Possibly Due to Text Being Too Long", diff --git a/packages/web/i18n/zh-CN/chat.json b/packages/web/i18n/zh-CN/chat.json index 6d5ab76ed..5250c44d6 100644 --- a/packages/web/i18n/zh-CN/chat.json +++ b/packages/web/i18n/zh-CN/chat.json @@ -59,6 +59,7 @@ "to_dataset": "前往知识库", "unsupported_file_type": "不支持的文件类型", "upload": "上传", + "variable_invisable_in_share": "自定义变量在免登录链接中不可见", "view_citations": "查看引用", "web_site_sync": "Web站点同步" } diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index 451ccf4cd..52445499c 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -433,8 +433,6 @@ "core.chat.Start Chat": "开始对话", "core.chat.Type a message": "输入问题,发送 [Enter]/换行 [Ctrl(Alt/Shift) + Enter]", "core.chat.Unpin": "取消置顶", - "core.chat.Variable_Visiable_in_test": "该变量在免登录链接中不可见", - "core.chat.Visiable_in_test": "自定义变量在免登录链接中不可见", "core.chat.You need to a chat app": "你没有可用的应用", "core.chat.error.Chat error": "对话出现异常", "core.chat.error.Messages empty": "接口内容为空,可能文本超长了~", diff --git a/packages/web/i18n/zh-Hant/chat.json b/packages/web/i18n/zh-Hant/chat.json index a688e4674..8026afe8d 100644 --- a/packages/web/i18n/zh-Hant/chat.json +++ b/packages/web/i18n/zh-Hant/chat.json @@ -58,6 +58,7 @@ "to_dataset": "前往知識庫", "unsupported_file_type": "不支援的檔案類型", "upload": "上傳", + "variable_invisable_in_share": "自定義變量在免登錄鏈接中不可見", "view_citations": "檢視引用", "web_site_sync": "網站同步" } diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index 53c122b7e..fff2a0c78 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -429,8 +429,6 @@ "core.chat.Start Chat": "開始對話", "core.chat.Type a message": "輸入問題,按 [Enter] 傳送 / 按 [Ctrl(Alt/Shift) + Enter] 換行", "core.chat.Unpin": "取消釘選", - "core.chat.Variable_Visiable_in_test": "該變量在免登錄鏈接中不可見", - "core.chat.Visiable_in_test": "自定義變量在免登錄鏈接中不可見", "core.chat.You need to a chat app": "您沒有可用的應用程式", "core.chat.error.Chat error": "對話發生錯誤", "core.chat.error.Messages empty": "API 內容為空,可能是文字過長", diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariableInput.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariableInput.tsx index 5d06bdd5f..7d52fc992 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariableInput.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariableInput.tsx @@ -126,22 +126,10 @@ export const ExternalVariableInputItem = ({ const { t } = useTranslation(); const { register, control } = variablesForm; - return ( - - + const Label = useMemo(() => { + return ( + {item.label} - {item.required && ( - - * - - )} {item.description && } {showTag && ( - {t('common:core.chat.Variable_Visiable_in_test')} + {t('chat:variable_invisable_in_share')} )} + ); + }, [item.description, item.label, showTag, t]); + + return ( + + {Label} { - if (item.valueType === WorkflowIOValueTypeEnum.boolean) { - return value !== undefined; - } - return !!value; - } - }} render={({ field: { onChange, value } }) => { if (item.valueType === WorkflowIOValueTypeEnum.string) { return ( @@ -179,9 +164,7 @@ export const ExternalVariableInputItem = ({ minH={40} maxH={160} bg={'myGray.50'} - {...register(`variables.${item.key}`, { - required: item.required - })} + {...register(`variables.${item.key}`)} /> ); } @@ -231,7 +214,7 @@ const VariableInput = ({ setValue(`variables.${item.key}`, item.defaultValue); } }); - }, [variableList]); + }, [allVariableList, getValues, setValue, variableList]); return ( @@ -257,7 +240,7 @@ const VariableInput = ({ rounded={'sm'} > - {t('common:core.chat.Visiable_in_test')} + {t('chat:variable_invisable_in_share')} {externalVariableList.map((item) => ( diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariablePopover.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariablePopover.tsx index d75ada0ab..2e54c5815 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariablePopover.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/VariablePopover.tsx @@ -65,7 +65,7 @@ const VariablePopover = ({ rounded={'sm'} > - {t('common:core.chat.Visiable_in_test')} + {t('chat:variable_invisable_in_share')} {externalVariableList.map((item) => ( { container.removeEventListener('scroll', checkVariableVisibility); }; } - }, [setIsVariableVisible]); + }, [chatType, setIsVariableVisible]); const RenderRecords = useMemo(() => { return ( diff --git a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx index ba28be3ef..879de95dd 100644 --- a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx @@ -255,7 +255,7 @@ const RenderPluginInput = ({ rounded={'sm'} > - {t('common:core.chat.Variable_Visiable_in_test')} + {t('chat:variable_invisable_in_share')} )} diff --git a/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx b/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx index a1611b7a8..e03edb08b 100644 --- a/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx +++ b/projects/app/src/pageComponents/app/detail/SimpleApp/ChatTest.tsx @@ -70,7 +70,7 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => { {t('app:chat_debug')} - {!isVariableVisible && } + {!isVariableVisible && } { {t('common:core.chat.Run test')} - {!isVariableVisible && } + {!isVariableVisible && } Date: Wed, 19 Mar 2025 19:59:55 +0800 Subject: [PATCH 04/35] fix: invite link (#4229) * fix: invite link * feat: create invite link and copy it directly --- .../user/team/invitationLink/schema.ts | 13 ++++--- .../support/user/team/invitationLink/type.ts | 10 ++--- .../team/Invite/CreateInvitationModal.tsx | 9 +++-- .../account/team/Invite/InviteModal.tsx | 37 ++++++++++--------- projects/app/src/web/support/user/team/api.ts | 6 +-- test/datas/users.ts | 29 +++++++++++++++ 6 files changed, 70 insertions(+), 34 deletions(-) diff --git a/packages/service/support/user/team/invitationLink/schema.ts b/packages/service/support/user/team/invitationLink/schema.ts index 7ad5259d2..efdb660c3 100644 --- a/packages/service/support/user/team/invitationLink/schema.ts +++ b/packages/service/support/user/team/invitationLink/schema.ts @@ -1,15 +1,18 @@ -import { - TeamCollectionName, - TeamMemberCollectionName -} from '@fastgpt/global/support/user/team/constant'; +import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant'; import { connectionMongo, getMongoModel } from '../../../../common/mongo'; import { InvitationSchemaType } from './type'; -import addDays from 'date-fns/esm/fp/addDays/index.js'; +import { randomUUID } from 'crypto'; const { Schema } = connectionMongo; export const InvitationCollectionName = 'team_invitation_links'; const InvitationSchema = new Schema({ + linkId: { + type: String, + required: true, + unique: true, + default: () => randomUUID() + }, teamId: { type: Schema.Types.ObjectId, ref: TeamCollectionName, diff --git a/packages/service/support/user/team/invitationLink/type.ts b/packages/service/support/user/team/invitationLink/type.ts index 59873f958..a36245fa0 100644 --- a/packages/service/support/user/team/invitationLink/type.ts +++ b/packages/service/support/user/team/invitationLink/type.ts @@ -2,6 +2,7 @@ import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type'; export type InvitationSchemaType = { _id: string; + linkId: string; teamId: string; usedTimesLimit?: number; forbidden?: boolean; @@ -25,11 +26,10 @@ export type InvitationLinkCreateType = { expires: InvitationLinkExpiresType; usedTimesLimit: 1 | -1; }; -export type InvitationLinkUpdateType = Partial< - Omit -> & { - linkId: string; -}; + +// export type InvitationLinkUpdateType = Partial< +// Omit +// >; export type InvitationInfoType = InvitationSchemaType & { teamAvatar: string; diff --git a/projects/app/src/pageComponents/account/team/Invite/CreateInvitationModal.tsx b/projects/app/src/pageComponents/account/team/Invite/CreateInvitationModal.tsx index 8f1d43a3c..302df2877 100644 --- a/projects/app/src/pageComponents/account/team/Invite/CreateInvitationModal.tsx +++ b/projects/app/src/pageComponents/account/team/Invite/CreateInvitationModal.tsx @@ -22,7 +22,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; import { useForm } from 'react-hook-form'; -function CreateInvitationModal({ onClose }: { onClose: () => void }) { +function CreateInvitationModal({ onClose }: { onClose: (linkId?: string) => void }) { const { t } = useTranslation(); const expiresOptions: Array<{ label: string; value: InvitationLinkExpiresType }> = [ { label: t('account_team:30mins'), value: '30m' }, // 30 mins @@ -45,6 +45,9 @@ function CreateInvitationModal({ onClose }: { onClose: () => void }) { manual: true, successToast: t('common:common.Create Success'), errorToast: t('common:common.Create Failed'), + onSuccess: (data) => { + onClose(data); + }, onFinally: () => onClose() }); @@ -55,7 +58,7 @@ function CreateInvitationModal({ onClose }: { onClose: () => void }) { iconColor="primary.500" title={{t('account_team:create_invitation_link')}} > - + onClose()} /> <> @@ -91,7 +94,7 @@ function CreateInvitationModal({ onClose }: { onClose: () => void }) { -