add external variable debug (#4204)

* add external variable debug

* fix ui

* plugin variables
This commit is contained in:
heheer
2025-03-19 11:57:30 +08:00
committed by archer
parent ec30d79286
commit d52700c645
15 changed files with 473 additions and 97 deletions

View File

@@ -430,6 +430,8 @@
"core.chat.Start Chat": "Start Chat", "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.Type a message": "Enter a Question, Press [Enter] to Send / Press [Ctrl(Alt/Shift) + Enter] for New Line",
"core.chat.Unpin": "Unpin", "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.You need to a chat app": "You Do Not Have an Available App",
"core.chat.error.Chat error": "Chat Error", "core.chat.error.Chat error": "Chat Error",
"core.chat.error.Messages empty": "API Content is Empty, Possibly Due to Text Being Too Long", "core.chat.error.Messages empty": "API Content is Empty, Possibly Due to Text Being Too Long",

View File

@@ -433,6 +433,8 @@
"core.chat.Start Chat": "开始对话", "core.chat.Start Chat": "开始对话",
"core.chat.Type a message": "输入问题,发送 [Enter]/换行 [Ctrl(Alt/Shift) + Enter]", "core.chat.Type a message": "输入问题,发送 [Enter]/换行 [Ctrl(Alt/Shift) + Enter]",
"core.chat.Unpin": "取消置顶", "core.chat.Unpin": "取消置顶",
"core.chat.Variable_Visiable_in_test": "该变量在免登录链接中不可见",
"core.chat.Visiable_in_test": "自定义变量在免登录链接中不可见",
"core.chat.You need to a chat app": "你没有可用的应用", "core.chat.You need to a chat app": "你没有可用的应用",
"core.chat.error.Chat error": "对话出现异常", "core.chat.error.Chat error": "对话出现异常",
"core.chat.error.Messages empty": "接口内容为空,可能文本超长了~", "core.chat.error.Messages empty": "接口内容为空,可能文本超长了~",

View File

@@ -429,6 +429,8 @@
"core.chat.Start Chat": "開始對話", "core.chat.Start Chat": "開始對話",
"core.chat.Type a message": "輸入問題,按 [Enter] 傳送 / 按 [Ctrl(Alt/Shift) + Enter] 換行", "core.chat.Type a message": "輸入問題,按 [Enter] 傳送 / 按 [Ctrl(Alt/Shift) + Enter] 換行",
"core.chat.Unpin": "取消釘選", "core.chat.Unpin": "取消釘選",
"core.chat.Variable_Visiable_in_test": "該變量在免登錄鏈接中不可見",
"core.chat.Visiable_in_test": "自定義變量在免登錄鏈接中不可見",
"core.chat.You need to a chat app": "您沒有可用的應用程式", "core.chat.You need to a chat app": "您沒有可用的應用程式",
"core.chat.error.Chat error": "對話發生錯誤", "core.chat.error.Chat error": "對話發生錯誤",
"core.chat.error.Messages empty": "API 內容為空,可能是文字過長", "core.chat.error.Messages empty": "API 內容為空,可能是文字過長",

View File

@@ -1,20 +1,26 @@
import React, { useEffect } from 'react'; import React, { useEffect, useMemo } from 'react';
import { Controller, UseFormReturn } from 'react-hook-form'; import { Controller, UseFormReturn } from 'react-hook-form';
import { useTranslation } from 'next-i18next'; 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 ChatAvatar from './ChatAvatar';
import { MessageCardStyle } from '../constants'; 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 MySelect from '@fastgpt/web/components/common/MySelect';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { ChatBoxInputFormType } from '../type.d'; import { ChatBoxInputFormType } from '../type.d';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../Provider';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { VariableItemType } from '@fastgpt/global/core/app/type'; import { VariableItemType } from '@fastgpt/global/core/app/type';
import MyTextarea from '@/components/common/Textarea/MyTextarea'; import MyTextarea from '@/components/common/Textarea/MyTextarea';
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput'; import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; 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 = ({ export const VariableInputItem = ({
item, item,
@@ -108,23 +114,118 @@ export const VariableInputItem = ({
); );
}; };
export const ExternalVariableInputItem = ({
item,
variablesForm,
showTag = false
}: {
item: VariableItemType;
variablesForm: UseFormReturn<any>;
showTag?: boolean;
}) => {
const { t } = useTranslation();
const { register, control } = variablesForm;
return (
<Box key={item.id} mb={4} pl={1}>
<Box
as={'label'}
display={'flex'}
position={'relative'}
mb={1}
alignItems={'center'}
w={'full'}
>
{item.label}
{item.required && (
<Box position={'absolute'} top={'-2px'} left={'-8px'} color={'red.500'}>
*
</Box>
)}
{item.description && <QuestionTip ml={1} label={item.description} />}
{showTag && (
<Flex
color={'primary.600'}
bg={'primary.100'}
px={2}
py={1}
gap={1}
ml={2}
fontSize={'mini'}
rounded={'sm'}
>
<MyIcon name={'common/info'} color={'primary.600'} w={4} />
{t('common:core.chat.Variable_Visiable_in_test')}
</Flex>
)}
</Box>
<Controller
control={control}
name={`variables.${item.key}`}
rules={{
required: item.required,
validate: (value) => {
if (item.valueType === WorkflowIOValueTypeEnum.boolean) {
return value !== undefined;
}
return !!value;
}
}}
render={({ field: { onChange, value } }) => {
if (item.valueType === WorkflowIOValueTypeEnum.string) {
return (
<MyTextarea
autoHeight
minH={40}
maxH={160}
bg={'myGray.50'}
{...register(`variables.${item.key}`, {
required: item.required
})}
/>
);
}
if (item.valueType === WorkflowIOValueTypeEnum.number) {
return <MyNumberInput step={1} bg={'myGray.50'} value={value} onChange={onChange} />;
}
if (item.valueType === WorkflowIOValueTypeEnum.boolean) {
return <Switch isChecked={value} onChange={onChange} />;
}
return <JsonEditor bg={'myGray.50'} resize value={value} onChange={onChange} />;
}}
/>
</Box>
);
};
const VariableInput = ({ const VariableInput = ({
chatForm, chatForm,
chatStarted chatStarted,
showExternalVariables = false
}: { }: {
chatStarted: boolean;
chatForm: UseFormReturn<ChatBoxInputFormType>; chatForm: UseFormReturn<ChatBoxInputFormType>;
chatStarted: boolean;
showExternalVariables?: boolean;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app?.avatar); const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app?.avatar);
const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm); const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm);
const variableList = useContextSelector(ChatBoxContext, (v) => v.variableList); 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; const { getValues, setValue, handleSubmit: handleSubmitChat } = variablesForm;
useEffect(() => { useEffect(() => {
variableList.forEach((item) => { allVariableList.forEach((item) => {
const val = getValues(`variables.${item.key}`); const val = getValues(`variables.${item.key}`);
if (item.defaultValue !== undefined && (val === undefined || val === null || val === '')) { if (item.defaultValue !== undefined && (val === undefined || val === null || val === '')) {
setValue(`variables.${item.key}`, item.defaultValue); setValue(`variables.${item.key}`, item.defaultValue);
@@ -135,6 +236,51 @@ const VariableInput = ({
return ( return (
<Box py={3}> <Box py={3}>
<ChatAvatar src={appAvatar} type={'AI'} /> <ChatAvatar src={appAvatar} type={'AI'} />
{externalVariableList.length > 0 && (
<Box textAlign={'left'}>
<Card
order={2}
mt={2}
w={'400px'}
{...MessageCardStyle}
bg={'white'}
boxShadow={'0 0 8px rgba(0,0,0,0.15)'}
>
<Flex
color={'primary.600'}
bg={'primary.100'}
mb={3}
px={3}
py={1.5}
gap={1}
fontSize={'mini'}
rounded={'sm'}
>
<MyIcon name={'common/info'} color={'primary.600'} w={4} />
{t('common:core.chat.Visiable_in_test')}
</Flex>
{externalVariableList.map((item) => (
<ExternalVariableInputItem key={item.id} item={item} variablesForm={variablesForm} />
))}
{variableList.length === 0 && !chatStarted && (
<Box>
<Button
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
size={'sm'}
maxW={'100px'}
onClick={handleSubmitChat(() => {
chatForm.setValue('chatStarted', true);
})}
>
{t('common:core.chat.Start Chat')}
</Button>
</Box>
)}
</Card>
</Box>
)}
{variableList.length > 0 && (
<Box textAlign={'left'}> <Box textAlign={'left'}>
<Card <Card
order={2} order={2}
@@ -163,6 +309,7 @@ const VariableInput = ({
)} )}
</Card> </Card>
</Box> </Box>
)}
</Box> </Box>
); );
}; };

View File

@@ -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 (
<MyPopover
placement="bottom"
trigger={'click'}
closeOnBlur={true}
Trigger={
<Button variant={'whiteBase'} leftIcon={<MyIcon name={'edit'} w={4} />}>
{t('common:core.module.Variable')}
</Button>
}
>
{({ onClose }) => (
<Box p={4}>
{hasExternalVariable && (
<Box textAlign={'left'}>
<Flex
color={'primary.600'}
bg={'primary.100'}
mb={3}
px={3}
py={1.5}
gap={1}
fontSize={'mini'}
rounded={'sm'}
>
<MyIcon name={'common/info'} color={'primary.600'} w={4} />
{t('common:core.chat.Visiable_in_test')}
</Flex>
{externalVariableList.map((item) => (
<ExternalVariableInputItem
key={item.id}
item={item}
variablesForm={variablesForm}
/>
))}
</Box>
)}
{hasExternalVariable && hasVariable && <MyDivider h={'1px'} />}
{hasVariable && (
<Box textAlign={'left'}>
{variableList.map((item) => (
<VariableInputItem key={item.id} item={item} variablesForm={variablesForm} />
))}
</Box>
)}
<Flex w={'full'} justifyContent={'flex-end'}>
<Button size={'sm'} onClick={onClose}>
{t('common:common.Confirm')}
</Button>
</Flex>
</Box>
)}
</MyPopover>
);
};
export default VariablePopover;

View File

@@ -65,6 +65,7 @@ import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import TimeBox from './components/TimeBox'; import TimeBox from './components/TimeBox';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
const ResponseTags = dynamic(() => import('./components/ResponseTags')); const ResponseTags = dynamic(() => import('./components/ResponseTags'));
const FeedbackModal = dynamic(() => import('./components/FeedbackModal')); const FeedbackModal = dynamic(() => import('./components/FeedbackModal'));
@@ -103,7 +104,8 @@ const ChatBox = ({
showVoiceIcon = true, showVoiceIcon = true,
showEmptyIntro = false, showEmptyIntro = false,
active = true, active = true,
onStartChat onStartChat,
chatType
}: Props) => { }: Props) => {
const ScrollContainerRef = useRef<HTMLDivElement>(null); const ScrollContainerRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation(); const { t } = useTranslation();
@@ -129,6 +131,8 @@ const ChatBox = ({
const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); const chatBoxData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
const ChatBoxRef = useContextSelector(ChatItemContext, (v) => v.ChatBoxRef); const ChatBoxRef = useContextSelector(ChatItemContext, (v) => v.ChatBoxRef);
const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm); const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm);
const setIsVariableVisible = useContextSelector(ChatItemContext, (v) => v.setIsVariableVisible);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const setChatRecords = useContextSelector(ChatRecordContext, (v) => v.setChatRecords); const setChatRecords = useContextSelector(ChatRecordContext, (v) => v.setChatRecords);
const isChatRecordsLoaded = useContextSelector(ChatRecordContext, (v) => v.isChatRecordsLoaded); const isChatRecordsLoaded = useContextSelector(ChatRecordContext, (v) => v.isChatRecordsLoaded);
@@ -150,6 +154,12 @@ const ChatBox = ({
// Workflow running, there are user input or selection // Workflow running, there are user input or selection
const isInteractive = useMemo(() => checkIsInteractiveByHistories(chatRecords), [chatRecords]); 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. // compute variable input is finish.
const chatForm = useForm<ChatBoxInputFormType>({ const chatForm = useForm<ChatBoxInputFormType>({
defaultValues: { defaultValues: {
@@ -162,7 +172,9 @@ const ChatBox = ({
const chatStartedWatch = watch('chatStarted'); const chatStartedWatch = watch('chatStarted');
const chatStarted = const chatStarted =
chatBoxData?.appId === appId && 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) => { 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(() => { const RenderRecords = useMemo(() => {
return ( return (
<ScrollData <ScrollData
@@ -906,8 +945,14 @@ const ChatBox = ({
{showEmpty && <Empty />} {showEmpty && <Empty />}
{!!welcomeText && <WelcomeBox welcomeText={welcomeText} />} {!!welcomeText && <WelcomeBox welcomeText={welcomeText} />}
{/* variable input */} {/* variable input */}
{!!variableList?.length && ( {(!!variableList?.length || !!externalVariableList?.length) && (
<VariableInput chatStarted={chatStarted} chatForm={chatForm} /> <Box id="variable-input">
<VariableInput
chatStarted={chatStarted}
chatForm={chatForm}
showExternalVariables={chatType === 'chat'}
/>
</Box>
)} )}
{/* chat history */} {/* chat history */}
<Box id={'history'}> <Box id={'history'}>
@@ -1006,7 +1051,9 @@ const ChatBox = ({
chatForm, chatForm,
chatRecords, chatRecords,
chatStarted, chatStarted,
chatType,
delOneMessage, delOneMessage,
externalVariableList?.length,
isChatting, isChatting,
onAddUserDislike, onAddUserDislike,
onAddUserLike, onAddUserLike,

View File

@@ -18,6 +18,7 @@ import { ChatBoxInputFormType } from '../../ChatBox/type';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext'; import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
const RenderInput = () => { const RenderInput = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -213,7 +214,14 @@ const RenderInput = () => {
</Box> </Box>
)} )}
{/* Filed */} {/* Filed */}
{formatPluginInputs.map((input) => { {formatPluginInputs
.filter((input) => {
if (outLinkAuthData && Object.keys(outLinkAuthData).length > 0) {
return input.renderTypeList[0] !== FlowNodeInputTypeEnum.customVariable;
}
return true;
})
.map((input) => {
return ( return (
<Controller <Controller
key={`variables.${input.key}`} key={`variables.${input.key}`}

View File

@@ -157,9 +157,6 @@ const RenderPluginInput = ({
const { llmModelList } = useSystemStore(); const { llmModelList } = useSystemStore();
const render = (() => { const render = (() => {
if (inputType === FlowNodeInputTypeEnum.customVariable) {
return null;
}
if (inputType === FlowNodeInputTypeEnum.select && input.list) { if (inputType === FlowNodeInputTypeEnum.select && input.list) {
return ( return (
<MySelect list={input.list} value={value} onChange={onChange} isDisabled={isDisabled} /> <MySelect list={input.list} value={value} onChange={onChange} isDisabled={isDisabled} />
@@ -246,6 +243,21 @@ const RenderPluginInput = ({
<FormLabel fontWeight={'500'}>{t(input.label as any)}</FormLabel> <FormLabel fontWeight={'500'}>{t(input.label as any)}</FormLabel>
</Box> </Box>
{input.description && <QuestionTip ml={2} label={t(input.description as any)} />} {input.description && <QuestionTip ml={2} label={t(input.description as any)} />}
{inputType === FlowNodeInputTypeEnum.customVariable && (
<Flex
color={'primary.600'}
bg={'primary.100'}
px={2}
py={1}
gap={1}
ml={2}
fontSize={'mini'}
rounded={'sm'}
>
<MyIcon name={'common/info'} color={'primary.600'} w={4} />
{t('common:core.chat.Variable_Visiable_in_test')}
</Flex>
)}
</Flex> </Flex>
)} )}

View File

@@ -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 { useTranslation } from 'next-i18next';
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; 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 { useContextSelector } from 'use-context-selector';
import { AppContext } from '../context'; import { AppContext } from '../context';
import { useChatTest } from '../useChatTest'; import { useChatTest } from '../useChatTest';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import ChatItemContextProvider, { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import ChatRecordContextProvider from '@/web/core/chat/context/chatRecordContext'; import ChatRecordContextProvider from '@/web/core/chat/context/chatRecordContext';
import { useChatStore } from '@/web/core/chat/context/useChatStore'; import { useChatStore } from '@/web/core/chat/context/useChatStore';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import { cardStyles } from '../constants'; import { cardStyles } from '../constants';
import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList'; import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList';
import VariablePopover from '@/components/core/chat/ChatContainer/ChatBox/components/VariablePopover';
type Props = { type Props = {
appForm: AppSimpleEditFormType; appForm: AppSimpleEditFormType;
@@ -27,6 +29,8 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
const { appDetail } = useContextSelector(AppContext, (v) => v); const { appDetail } = useContextSelector(AppContext, (v) => v);
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData); const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData); const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
// form2AppWorkflow dependent allDatasets
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
const [workflowData, setWorkflowData] = useSafeState({ const [workflowData, setWorkflowData] = useSafeState({
nodes: appDetail.modules || [], nodes: appDetail.modules || [],
@@ -62,10 +66,12 @@ const ChatTest = ({ appForm, setRenderEdit }: Props) => {
{...cardStyles} {...cardStyles}
boxShadow={'3'} boxShadow={'3'}
> >
<Flex px={[2, 5]}> <Flex px={[2, 5]} pb={2}>
<Box fontSize={['md', 'lg']} fontWeight={'bold'} flex={1} color={'myGray.900'}> <Box fontSize={['md', 'lg']} fontWeight={'bold'} color={'myGray.900'} mr={3}>
{t('app:chat_debug')} {t('app:chat_debug')}
</Box> </Box>
{!isVariableVisible && <VariablePopover showExternalVariables={true} />}
<Box flex={1} />
<MyTooltip label={t('common:core.chat.Restart')}> <MyTooltip label={t('common:core.chat.Restart')}>
<IconButton <IconButton
className="chat" className="chat"

View File

@@ -21,6 +21,7 @@ import ChatRecordContextProvider, {
import { useChatStore } from '@/web/core/chat/context/useChatStore'; import { useChatStore } from '@/web/core/chat/context/useChatStore';
import MyBox from '@fastgpt/web/components/common/MyBox'; import MyBox from '@fastgpt/web/components/common/MyBox';
import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList'; import ChatQuoteList from '@/pageComponents/chat/ChatQuoteList';
import VariablePopover from '@/components/core/chat/ChatContainer/ChatBox/components/VariablePopover';
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
@@ -45,6 +46,7 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData); const quoteData = useContextSelector(ChatItemContext, (v) => v.quoteData);
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData); const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
return ( return (
@@ -115,10 +117,12 @@ const ChatTest = ({ isOpen, nodes = [], edges = [], onClose }: Props) => {
bg={'myGray.25'} bg={'myGray.25'}
borderBottom={'1px solid #F4F4F7'} borderBottom={'1px solid #F4F4F7'}
> >
<Flex fontSize={'16px'} fontWeight={'bold'} flex={1} alignItems={'center'}> <Flex fontSize={'16px'} fontWeight={'bold'} alignItems={'center'} mr={3}>
<MyIcon name={'common/paused'} w={'14px'} mr={2.5} /> <MyIcon name={'common/paused'} w={'14px'} mr={2.5} />
{t('common:core.chat.Run test')} {t('common:core.chat.Run test')}
</Flex> </Flex>
{!isVariableVisible && <VariablePopover showExternalVariables={true} />}
<Box flex={1} />
<MyTooltip label={t('common:core.chat.Restart')}> <MyTooltip label={t('common:core.chat.Restart')}>
<IconButton <IconButton
className="chat" className="chat"

View File

@@ -31,7 +31,10 @@ import { WorkflowContext } from '../../context';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppContext } from '../../../context'; import { AppContext } from '../../../context';
import { VariableInputItem } from '@/components/core/chat/ChatContainer/ChatBox/components/VariableInput'; import {
ExternalVariableInputItem,
VariableInputItem
} from '@/components/core/chat/ChatContainer/ChatBox/components/VariableInput';
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs'; import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import MyTextarea from '@/components/common/Textarea/MyTextarea'; import MyTextarea from '@/components/common/Textarea/MyTextarea';
import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext'; import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext';
@@ -58,13 +61,17 @@ export const useDebug = () => {
const appDetail = useContextSelector(AppContext, (v) => v.appDetail); const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
const filteredVar = useMemo(() => { const { filteredVar, customVar, variables } = useMemo(() => {
const variables = appDetail.chatConfig?.variables; const variables = appDetail.chatConfig?.variables || [];
return variables?.filter((item) => item.type !== VariableInputEnum.custom) || []; return {
filteredVar: variables.filter((item) => item.type !== VariableInputEnum.custom) || [],
customVar: variables.filter((item) => item.type === VariableInputEnum.custom) || [],
variables
};
}, [appDetail.chatConfig?.variables]); }, [appDetail.chatConfig?.variables]);
const [defaultGlobalVariables, setDefaultGlobalVariables] = useState<Record<string, any>>( const [defaultGlobalVariables, setDefaultGlobalVariables] = useState<Record<string, any>>(
filteredVar.reduce( variables.reduce(
(acc, item) => { (acc, item) => {
acc[item.key] = item.defaultValue; acc[item.key] = item.defaultValue;
return acc; return acc;
@@ -241,7 +248,7 @@ export const useDebug = () => {
px={0} px={0}
> >
<Box flex={'1 0 0'} overflow={'auto'} px={6}> <Box flex={'1 0 0'} overflow={'auto'} px={6}>
{filteredVar.length > 0 && ( {variables.length > 0 && (
<LightRowTabs<TabEnum> <LightRowTabs<TabEnum>
gap={3} gap={3}
ml={-2} ml={-2}
@@ -256,6 +263,14 @@ export const useDebug = () => {
/> />
)} )}
<Box display={currentTab === TabEnum.global ? 'block' : 'none'}> <Box display={currentTab === TabEnum.global ? 'block' : 'none'}>
{customVar.map((item) => (
<ExternalVariableInputItem
key={item.id}
item={{ ...item, key: item.key }}
variablesForm={variablesForm}
showTag={true}
/>
))}
{filteredVar.map((item) => ( {filteredVar.map((item) => (
<VariableInputItem <VariableInputItem
key={item.id} key={item.id}
@@ -354,13 +369,15 @@ export const useDebug = () => {
</MyRightDrawer> </MyRightDrawer>
); );
}, [ }, [
defaultGlobalVariables,
filteredVar,
onStartNodeDebug,
runtimeEdges,
runtimeNodeId,
runtimeNodes, runtimeNodes,
t runtimeEdges,
defaultGlobalVariables,
t,
variables.length,
customVar,
filteredVar,
runtimeNodeId,
onStartNodeDebug
]); ]);
return { return {

View File

@@ -297,8 +297,10 @@ const InputTypeConfig = ({
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}> <FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('common:core.module.Default Value')} {t('common:core.module.Default Value')}
</FormLabel> </FormLabel>
<Flex alignItems={'start'} flex={1} h={10}> <Flex alignItems={'center'} flex={1} h={10}>
{inputType === FlowNodeInputTypeEnum.numberInput && ( {(inputType === FlowNodeInputTypeEnum.numberInput ||
(inputType === VariableInputEnum.custom &&
valueType === WorkflowIOValueTypeEnum.number)) && (
<MyNumberInput <MyNumberInput
value={defaultValue} value={defaultValue}
min={min} min={min}
@@ -310,7 +312,8 @@ const InputTypeConfig = ({
/> />
)} )}
{(inputType === FlowNodeInputTypeEnum.input || {(inputType === FlowNodeInputTypeEnum.input ||
inputType === VariableInputEnum.custom) && ( (inputType === VariableInputEnum.custom &&
valueType === WorkflowIOValueTypeEnum.string)) && (
<MyTextarea <MyTextarea
{...register('defaultValue')} {...register('defaultValue')}
bg={'myGray.50'} bg={'myGray.50'}
@@ -319,7 +322,13 @@ const InputTypeConfig = ({
maxH={100} maxH={100}
/> />
)} )}
{inputType === FlowNodeInputTypeEnum.JSONEditor && ( {(inputType === FlowNodeInputTypeEnum.JSONEditor ||
(inputType === VariableInputEnum.custom &&
![
WorkflowIOValueTypeEnum.number,
WorkflowIOValueTypeEnum.string,
WorkflowIOValueTypeEnum.boolean
].includes(valueType))) && (
<JsonEditor <JsonEditor
bg={'myGray.50'} bg={'myGray.50'}
resize resize
@@ -330,7 +339,9 @@ const InputTypeConfig = ({
defaultValue={defaultValue} defaultValue={defaultValue}
/> />
)} )}
{inputType === FlowNodeInputTypeEnum.switch && ( {(inputType === FlowNodeInputTypeEnum.switch ||
(inputType === VariableInputEnum.custom &&
valueType === WorkflowIOValueTypeEnum.boolean)) && (
<Switch {...register('defaultValue')} /> <Switch {...register('defaultValue')} />
)} )}
{inputType === FlowNodeInputTypeEnum.select && ( {inputType === FlowNodeInputTypeEnum.select && (

View File

@@ -140,7 +140,7 @@ export const useChatTest = ({
appId={appId} appId={appId}
chatId={chatId} chatId={chatId}
showMarkIcon showMarkIcon
chatType="chat" chatType={'chat'}
onStartChat={startChat} onStartChat={startChat}
/> />
) )

View File

@@ -22,6 +22,7 @@ import {
import { getMyApps } from '@/web/core/app/api'; import { getMyApps } from '@/web/core/app/api';
import SelectOneResource from '@/components/common/folder/SelectOneResource'; import SelectOneResource from '@/components/common/folder/SelectOneResource';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext'; import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import VariablePopover from '@/components/core/chat/ChatContainer/ChatBox/components/VariablePopover';
const ChatHeader = ({ const ChatHeader = ({
history, history,
@@ -38,7 +39,10 @@ const ChatHeader = ({
const { isPc } = useSystem(); const { isPc } = useSystem();
const chatData = useContextSelector(ChatItemContext, (v) => v.chatBoxData); const chatData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
const isVariableVisible = useContextSelector(ChatItemContext, (v) => v.isVariableVisible);
const isPlugin = chatData.app.type === AppTypeEnum.plugin; const isPlugin = chatData.app.type === AppTypeEnum.plugin;
const router = useRouter();
const isChat = router.pathname === '/chat';
return isPc && isPlugin ? null : ( return isPc && isPlugin ? null : (
<Flex <Flex
@@ -68,9 +72,13 @@ const ChatHeader = ({
/> />
)} )}
<Flex gap={2} alignItems={'center'}>
{!isVariableVisible && <VariablePopover showExternalVariables={isChat} />}
{/* control */} {/* control */}
{!isPlugin && <ToolMenu history={history} />} {!isPlugin && <ToolMenu history={history} />}
</Flex> </Flex>
</Flex>
); );
}; };

View File

@@ -74,6 +74,8 @@ type ChatItemContextType = {
quoteData?: QuoteDataType; quoteData?: QuoteDataType;
setQuoteData: React.Dispatch<React.SetStateAction<QuoteDataType | undefined>>; setQuoteData: React.Dispatch<React.SetStateAction<QuoteDataType | undefined>>;
isVariableVisible: boolean;
setIsVariableVisible: React.Dispatch<React.SetStateAction<boolean>>;
} & ContextProps; } & ContextProps;
export const ChatItemContext = createContext<ChatItemContextType>({ export const ChatItemContext = createContext<ChatItemContextType>({
@@ -97,6 +99,10 @@ export const ChatItemContext = createContext<ChatItemContextType>({
quoteData: undefined, quoteData: undefined,
setQuoteData: function (value: React.SetStateAction<QuoteDataType | undefined>): void { setQuoteData: function (value: React.SetStateAction<QuoteDataType | undefined>): void {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
},
isVariableVisible: true,
setIsVariableVisible: function (value: React.SetStateAction<boolean>): void {
throw new Error('Function not implemented.');
} }
}); });
@@ -116,6 +122,8 @@ const ChatItemContextProvider = ({
const ChatBoxRef = useRef<ChatComponentRef>(null); const ChatBoxRef = useRef<ChatComponentRef>(null);
const variablesForm = useForm<ChatBoxInputFormType>(); const variablesForm = useForm<ChatBoxInputFormType>();
const [quoteData, setQuoteData] = useState<QuoteDataType>(); const [quoteData, setQuoteData] = useState<QuoteDataType>();
const [isVariableVisible, setIsVariableVisible] = useState(true);
const [chatBoxData, setChatBoxData] = useState<ChatBoxDataType>({ const [chatBoxData, setChatBoxData] = useState<ChatBoxDataType>({
...defaultChatData ...defaultChatData
}); });
@@ -172,7 +180,9 @@ const ChatItemContextProvider = ({
showNodeStatus, showNodeStatus,
quoteData, quoteData,
setQuoteData setQuoteData,
isVariableVisible,
setIsVariableVisible
}; };
}, [ }, [
chatBoxData, chatBoxData,
@@ -187,7 +197,9 @@ const ChatItemContextProvider = ({
// isShowFullText, // isShowFullText,
showNodeStatus, showNodeStatus,
quoteData, quoteData,
setQuoteData setQuoteData,
isVariableVisible,
setIsVariableVisible
]); ]);
return <ChatItemContext.Provider value={contextValue}>{children}</ChatItemContext.Provider>; return <ChatItemContext.Provider value={contextValue}>{children}</ChatItemContext.Provider>;