v4.6.4-Outlink (#589)

This commit is contained in:
Archer
2023-12-12 14:42:20 +08:00
committed by GitHub
parent d2d7eac9e0
commit e18c79ca71
79 changed files with 1094 additions and 762 deletions

View File

@@ -6,11 +6,13 @@ import { useTranslation } from 'next-i18next';
import { updateChatUserFeedback } from '@/web/core/chat/api';
const FeedbackModal = ({
appId,
chatId,
chatItemId,
onSuccess,
onClose
}: {
appId: string;
chatId: string;
chatItemId: string;
onSuccess: (e: string) => void;
@@ -23,6 +25,7 @@ const FeedbackModal = ({
mutationFn: async () => {
const val = ref.current?.value || t('core.chat.feedback.No Content');
return updateChatUserFeedback({
appId,
chatId,
chatItemId,
userBadFeedback: val

View File

@@ -429,7 +429,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
>
{isChatting ? (
<MyIcon
className={styles.stopIcon}
animation={'zoomStopIcon 0.4s infinite alternate'}
width={['22px', '25px']}
height={['22px', '25px']}
cursor={'pointer'}

View File

@@ -1,7 +1,3 @@
.stopIcon {
animation: zoomStopIcon 0.4s infinite alternate;
}
.statusAnimation {
animation: statusBox 0.8s linear infinite alternate;
}

View File

@@ -102,9 +102,13 @@ type Props = {
userGuideModule?: ModuleItemType;
showFileSelector?: boolean;
active?: boolean; // can use
// not chat test params
appId?: string;
chatId?: string;
shareId?: string;
outLinkUid?: string;
onUpdateVariable?: (e: Record<string, any>) => void;
onStartChat?: (e: StartChatFnProps) => Promise<{
responseText: string;
@@ -125,6 +129,7 @@ const ChatBox = (
userGuideModule,
showFileSelector,
active = true,
appId,
chatId,
shareId,
outLinkUid,
@@ -711,7 +716,7 @@ const ChatBox = (
return;
}
return () => {
if (!item.dataId || !chatId) return;
if (!item.dataId || !chatId || !appId) return;
const isGoodFeedback = !!item.userGoodFeedback;
setChatHistory((state) =>
@@ -726,6 +731,7 @@ const ChatBox = (
);
try {
updateChatUserFeedback({
appId,
chatId,
chatItemId: item.dataId,
shareId,
@@ -738,7 +744,7 @@ const ChatBox = (
onCloseUserLike={
feedbackType === FeedbackTypeEnum.admin
? () => {
if (!item.dataId || !chatId) return;
if (!item.dataId || !chatId || !appId) return;
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === item.dataId
@@ -747,6 +753,7 @@ const ChatBox = (
)
);
updateChatUserFeedback({
appId,
chatId,
chatItemId: item.dataId,
userGoodFeedback: undefined
@@ -760,7 +767,7 @@ const ChatBox = (
}
if (item.userBadFeedback) {
return () => {
if (!item.dataId || !chatId) return;
if (!item.dataId || !chatId || !appId) return;
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === item.dataId
@@ -770,6 +777,7 @@ const ChatBox = (
);
try {
updateChatUserFeedback({
appId,
chatId,
chatItemId: item.dataId,
shareId,
@@ -886,8 +894,9 @@ const ChatBox = (
/>
) : null}
{/* user feedback modal */}
{!!feedbackId && chatId && (
{!!feedbackId && chatId && appId && (
<FeedbackModal
appId={appId}
chatId={chatId}
chatItemId={feedbackId}
onClose={() => setFeedbackId(undefined)}
@@ -915,8 +924,9 @@ const ChatBox = (
)
);
try {
if (!chatId) return;
if (!chatId || !appId) return;
updateChatUserFeedback({
appId,
chatId,
chatItemId: readFeedbackData.chatItemId
});
@@ -948,8 +958,9 @@ const ChatBox = (
)
);
if (readFeedbackData && chatId) {
if (readFeedbackData && chatId && appId) {
updateChatUserFeedback({
appId,
chatId,
chatItemId: readFeedbackData.chatItemId,
userBadFeedback: undefined

View File

@@ -10,6 +10,7 @@ import {
Image
} from '@chakra-ui/react';
import MyIcon from '../Icon';
import { useSystemStore } from '@/web/common/system/useSystemStore';
export interface MyModalProps extends ModalContentProps {
iconSrc?: string;
@@ -30,12 +31,13 @@ const MyModal = ({
maxW = ['90vw', '600px'],
...props
}: MyModalProps) => {
const { isPc } = useSystemStore();
return (
<Modal
isOpen={isOpen}
onClose={() => onClose && onClose()}
autoFocus={false}
isCentered={isCentered}
isCentered={isPc ? isCentered : true}
>
<ModalOverlay />
<ModalContent

View File

@@ -5,11 +5,12 @@ import { useTranslation } from 'next-i18next';
// @ts-ignore
interface Props extends GridProps {
list: { icon?: string; title: string; desc?: string; value: string | number }[];
list: { icon?: string; title: string | React.ReactNode; desc?: string; value: any }[];
iconSize?: string;
align?: 'top' | 'center';
value: string | number;
onChange: (e: string | number) => void;
value: any;
hiddenCircle?: boolean;
onChange: (e: any) => void;
}
const MyRadio = ({
@@ -17,6 +18,8 @@ const MyRadio = ({
value,
align = 'center',
iconSize = '18px',
hiddenCircle = false,
p,
onChange,
...props
}: Props) => {
@@ -32,7 +35,8 @@ const MyRadio = ({
userSelect={'none'}
py={3}
pl={'14px'}
pr={'36px'}
pr={hiddenCircle ? '14px' : '36px'}
p={p !== undefined ? `${p} !important` : undefined}
border={theme.borders.sm}
borderWidth={'1.5px'}
borderRadius={'md'}
@@ -50,6 +54,7 @@ const MyRadio = ({
})}
_after={{
content: '""',
display: hiddenCircle ? 'none' : 'block',
position: 'absolute',
right: '14px',
w: '16px',
@@ -79,8 +84,8 @@ const MyRadio = ({
)}
</>
)}
<Box pr={2}>
<Box>{t(item.title)}</Box>
<Box pr={hiddenCircle ? 0 : 2} color={'myGray.800'}>
<Box>{typeof item.title === 'string' ? t(item.title) : item.title}</Box>
{!!item.desc && (
<Box fontSize={['xs', 'sm']} color={'myGray.500'}>
{t(item.desc)}

View File

@@ -10,13 +10,13 @@ import React, {
} from 'react';
import { Box, Flex, IconButton } from '@chakra-ui/react';
import MyIcon from '@/components/Icon';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { streamFetch } from '@/web/common/api/fetch';
import MyTooltip from '@/components/MyTooltip';
import { useUserStore } from '@/web/support/user/useUserStore';
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
import { getGuideModule } from '@fastgpt/global/core/module/utils';
import { checkChatSupportSelectFileByModules } from '@/web/core/chat/utils';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
export type ChatTestComponentRef = {
resetChatTest: () => void;
@@ -40,10 +40,18 @@ const ChatTest = (
const startChat = useCallback(
async ({ chatList, controller, generatingMessage, variables }: StartChatFnProps) => {
const historyMaxLen =
modules
?.find((item) => item.flowType === FlowNodeTypeEnum.historyNode)
?.inputs?.find((item) => item.key === 'maxContext')?.value || 0;
let historyMaxLen = 6;
modules.forEach((module) => {
module.inputs.forEach((input) => {
if (
(input.key === ModuleInputKeyEnum.history ||
input.key === ModuleInputKeyEnum.historyMaxAmount) &&
typeof input.value === 'number'
) {
historyMaxLen = Math.max(historyMaxLen, input.value);
}
});
});
const history = chatList.slice(-historyMaxLen - 2, -2);
// 流请求,获取数据

View File

@@ -28,7 +28,7 @@ import React, {
import { customAlphabet } from 'nanoid';
import { appModule2FlowEdge, appModule2FlowNode } from '@/utils/adapt';
import { useToast } from '@/web/common/hooks/useToast';
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { ModuleDataTypeEnum } from '@fastgpt/global/core/module/constants';
import { useTranslation } from 'next-i18next';
import { ModuleItemType } from '@fastgpt/global/core/module/type.d';
@@ -449,9 +449,9 @@ export function flowNode2Modules({
flowType: item.data.flowType,
showStatus: item.data.showStatus,
position: item.position,
inputs: item.data.inputs.map((item) => ({
...item,
connected: Boolean(item.value ?? item.connected ?? item.type !== FlowNodeInputTypeEnum.target)
inputs: item.data.inputs.map((input) => ({
...input,
connected: false
})),
outputs: item.data.outputs.map((item) => ({
...item,
@@ -462,10 +462,11 @@ export function flowNode2Modules({
// update inputs and outputs
modules.forEach((module) => {
module.inputs.forEach((input) => {
input.connected =
input.connected ||
!!edges.find((edge) => edge.target === module.moduleId && edge.targetHandle === input.key);
input.connected = !!edges.find(
(edge) => edge.target === module.moduleId && edge.targetHandle === input.key
);
});
module.outputs.forEach((output) => {
output.targets = edges
.filter(

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { useMemo, useState } from 'react';
import {
Box,
Button,
@@ -12,9 +12,7 @@ import {
Flex,
Switch,
Input,
Grid,
FormControl,
useTheme,
Image,
Table,
Thead,
@@ -39,6 +37,7 @@ import MyTooltip from '@/components/MyTooltip';
import { variableTip } from '@fastgpt/global/core/module/template/tip';
import { useTranslation } from 'next-i18next';
import { useToast } from '@/web/common/hooks/useToast';
import MyRadio from '@/components/common/MyRadio';
const VariableEdit = ({
variables,
@@ -49,26 +48,28 @@ const VariableEdit = ({
}) => {
const { t } = useTranslation();
const { toast } = useToast();
const theme = useTheme();
const [refresh, setRefresh] = useState(false);
const VariableTypeList = [
{
label: t('core.module.variable.input type'),
icon: 'core/app/variable/input',
key: VariableInputEnum.input
},
{
label: t('core.module.variable.textarea type'),
icon: 'core/app/variable/textarea',
key: VariableInputEnum.textarea
},
{
label: t('core.module.variable.select type'),
icon: 'core/app/variable/select',
key: VariableInputEnum.select
}
];
const VariableTypeList = useMemo(
() => [
{
title: t('core.module.variable.input type'),
icon: 'core/app/variable/input',
value: VariableInputEnum.input
},
{
title: t('core.module.variable.textarea type'),
icon: 'core/app/variable/textarea',
value: VariableInputEnum.textarea
},
{
title: t('core.module.variable.select type'),
icon: 'core/app/variable/select',
value: VariableInputEnum.select
}
],
[t]
);
const { isOpen: isOpenEdit, onOpen: onOpenEdit, onClose: onCloseEdit } = useDisclosure();
const {
@@ -102,9 +103,9 @@ const VariableEdit = ({
const formatVariables = useMemo(() => {
return variables.map((item) => ({
...item,
icon: VariableTypeList.find((type) => type.key === item.type)?.icon
icon: VariableTypeList.find((type) => type.value === item.type)?.icon
}));
}, [variables]);
}, [VariableTypeList, variables]);
return (
<Box>
@@ -206,38 +207,18 @@ const VariableEdit = ({
<Box mt={5} mb={2}>
{t('core.module.Field Type')}
</Box>
<Grid gridTemplateColumns={'repeat(3,1fr)'} gridGap={4}>
{VariableTypeList.map((item) => (
<Flex
key={item.key}
px={3}
py={3}
border={theme.borders.base}
borderRadius={'md'}
cursor={'pointer'}
{...(item.key === getValuesEdit('variable.type')
? {
bg: 'myBlue.100',
borderColor: 'myBlue.600',
color: 'myBlue.600',
fontWeight: 'bold'
}
: {
color: 'myGray.600',
_hover: {
boxShadow: 'md'
},
onClick: () => {
setValuesEdit('variable.type', item.key);
setRefresh(!refresh);
}
})}
>
<MyIcon name={item.icon as any} w={'16px'} />
<Box ml={2}>{item.label}</Box>
</Flex>
))}
</Grid>
<MyRadio
gridGap={4}
gridTemplateColumns={'repeat(3,1fr)'}
value={getValuesEdit('variable.type')}
list={VariableTypeList}
color={'myGray.600'}
hiddenCircle
onChange={(e) => {
setValuesEdit('variable.type', e as any);
setRefresh(!refresh);
}}
/>
{getValuesEdit('variable.type') === VariableInputEnum.input && (
<>

View File

@@ -97,7 +97,7 @@ const NodeCQNode = ({ data }: NodeProps<FlowModuleItemType>) => {
});
}}
/>
<SourceHandle handleKey={item.key} valueType={ModuleDataTypeEnum.boolean} />
<SourceHandle handleKey={item.key} valueType={ModuleDataTypeEnum.string} />
</Box>
</Box>
))}

View File

@@ -29,7 +29,7 @@ import TargetHandle from './TargetHandle';
import MyIcon from '@/components/Icon';
import { useTranslation } from 'next-i18next';
import type { AIChatModuleProps } from '@fastgpt/global/core/module/node/type.d';
import { chatModelList } from '@/web/common/system/staticData';
import { chatModelList, cqModelList } from '@/web/common/system/staticData';
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d';
@@ -229,8 +229,11 @@ const RenderInput = ({
{item.type === FlowNodeInputTypeEnum.aiSettings && (
<AISetting inputs={sortInputs} item={item} moduleId={moduleId} />
)}
{item.type === FlowNodeInputTypeEnum.selectChatModel && (
<SelectChatModelRender inputs={sortInputs} item={item} moduleId={moduleId} />
{[
FlowNodeInputTypeEnum.selectChatModel,
FlowNodeInputTypeEnum.selectCQModel
].includes(item.type as any) && (
<SelectAIModelRender inputs={sortInputs} item={item} moduleId={moduleId} />
)}
{item.type === FlowNodeInputTypeEnum.selectDataset && (
<SelectDatasetRender item={item} moduleId={moduleId} />
@@ -446,12 +449,21 @@ const AISetting = React.memo(function AISetting({ inputs = [], moduleId }: Rende
);
});
const SelectChatModelRender = React.memo(function SelectChatModelRender({
const SelectAIModelRender = React.memo(function SelectAIModelRender({
inputs = [],
item,
moduleId
}: RenderProps) {
const modelList = chatModelList || [];
const modelList = (() => {
if (item.type === FlowNodeInputTypeEnum.selectChatModel) return chatModelList;
if (item.type === FlowNodeInputTypeEnum.selectCQModel) return cqModelList;
return [];
})().map((item) => ({
model: item.model,
name: item.name,
maxResponse: item.maxResponse,
price: item.price
}));
const onChangeModel = useCallback(
(e: string) => {