mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-27 08:25:07 +00:00
4.8.8 test fix (#2149)
* perf: code comment * feat: system plugin input guide * perf: variable avatar * feat: feishu webhook * perf: select tool config tip * perf: rename variable * fix: per inherit error * perf: docker-compose oneapi version and i18n * perf: ui tip bug * fix: ts * perf: pg log * perf: editor color * perf: update init
This commit is contained in:
@@ -52,13 +52,14 @@
|
||||
"react-dom": "18.3.1",
|
||||
"react-hook-form": "7.43.1",
|
||||
"react-i18next": "14.1.2",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"reactflow": "^11.7.4",
|
||||
"rehype-katex": "^6.0.2",
|
||||
"remark-breaks": "^3.0.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-katex": "^7.0.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"request-ip": "^3.3.0",
|
||||
"sass": "^1.58.3",
|
||||
"use-context-selector": "^1.4.4",
|
||||
|
@@ -36,4 +36,4 @@ const MdImage = ({ src }: { src?: string }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(MdImage);
|
||||
export default MdImage;
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import RemarkMath from 'remark-math';
|
||||
import RemarkBreaks from 'remark-breaks';
|
||||
import RehypeKatex from 'rehype-katex';
|
||||
import RemarkGfm from 'remark-gfm';
|
||||
import RemarkMath from 'remark-math'; // Math syntax
|
||||
import RemarkBreaks from 'remark-breaks'; // Line break
|
||||
import RehypeKatex from 'rehype-katex'; // Math render
|
||||
import RemarkGfm from 'remark-gfm'; // Special markdown syntax
|
||||
import RehypeExternalLinks from 'rehype-external-links';
|
||||
|
||||
import styles from './index.module.scss';
|
||||
import dynamic from 'next/dynamic';
|
||||
@@ -15,6 +16,7 @@ import { useTranslation } from 'next-i18next';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { MARKDOWN_QUOTE_SIGN } from '@fastgpt/global/core/chat/constants';
|
||||
import { CodeClassNameEnum } from './utils';
|
||||
|
||||
const CodeLight = dynamic(() => import('./CodeLight'), { ssr: false });
|
||||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
|
||||
@@ -24,15 +26,6 @@ const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'), { ssr:
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
|
||||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
|
||||
|
||||
export enum CodeClassName {
|
||||
guide = 'guide',
|
||||
questionGuide = 'questionGuide',
|
||||
mermaid = 'mermaid',
|
||||
echarts = 'echarts',
|
||||
quote = 'quote',
|
||||
files = 'files'
|
||||
}
|
||||
|
||||
const Markdown = ({
|
||||
source = '',
|
||||
showAnimation = false
|
||||
@@ -51,10 +44,13 @@ const Markdown = ({
|
||||
[]
|
||||
);
|
||||
|
||||
const formatSource = source
|
||||
// .replace(/\\n/g, '\n')
|
||||
.replace(/(http[s]?:\/\/[^\s,。]+)([。,])/g, '$1 $2')
|
||||
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1');
|
||||
const formatSource = useMemo(() => {
|
||||
const formatSource = source
|
||||
.replace(/(http[s]?:\/\/[^\s,。]+)([。,])/g, '$1 $2') // Follow the link with a space
|
||||
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1');
|
||||
|
||||
return formatSource;
|
||||
}, [source]);
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
@@ -62,9 +58,8 @@ const Markdown = ({
|
||||
${showAnimation ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''}
|
||||
`}
|
||||
remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]}
|
||||
rehypePlugins={[RehypeKatex]}
|
||||
rehypePlugins={[RehypeKatex, [RehypeExternalLinks, { target: '_blank' }]]}
|
||||
components={components}
|
||||
linkTarget={'_blank'}
|
||||
>
|
||||
{formatSource}
|
||||
</ReactMarkdown>
|
||||
@@ -73,6 +68,7 @@ const Markdown = ({
|
||||
|
||||
export default React.memo(Markdown);
|
||||
|
||||
/* Custom dom */
|
||||
const Code = React.memo(function Code(e: any) {
|
||||
const { inline, className, children } = e;
|
||||
|
||||
@@ -82,17 +78,16 @@ const Code = React.memo(function Code(e: any) {
|
||||
const strChildren = String(children);
|
||||
|
||||
const Component = useMemo(() => {
|
||||
if (codeType === CodeClassName.mermaid) {
|
||||
if (codeType === CodeClassNameEnum.mermaid) {
|
||||
return <MermaidCodeBlock code={strChildren} />;
|
||||
}
|
||||
|
||||
if (codeType === CodeClassName.guide) {
|
||||
if (codeType === CodeClassNameEnum.guide) {
|
||||
return <ChatGuide text={strChildren} />;
|
||||
}
|
||||
if (codeType === CodeClassName.questionGuide) {
|
||||
if (codeType === CodeClassNameEnum.questionGuide) {
|
||||
return <QuestionGuide text={strChildren} />;
|
||||
}
|
||||
if (codeType === CodeClassName.echarts) {
|
||||
if (codeType === CodeClassNameEnum.echarts) {
|
||||
return <EChartsCodeBlock code={strChildren} />;
|
||||
}
|
||||
|
||||
@@ -105,7 +100,6 @@ const Code = React.memo(function Code(e: any) {
|
||||
|
||||
return Component;
|
||||
});
|
||||
|
||||
const Image = React.memo(function Image({ src }: { src?: string }) {
|
||||
return <MdImage src={src} />;
|
||||
});
|
||||
|
77
projects/app/src/components/Markdown/utils.ts
Normal file
77
projects/app/src/components/Markdown/utils.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
export enum CodeClassNameEnum {
|
||||
guide = 'guide',
|
||||
questionGuide = 'questionGuide',
|
||||
mermaid = 'mermaid',
|
||||
echarts = 'echarts',
|
||||
quote = 'quote',
|
||||
files = 'files',
|
||||
latex = 'latex'
|
||||
}
|
||||
|
||||
function htmlTableToLatex(html: string) {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
const table = doc.querySelector('table');
|
||||
|
||||
if (!table) return '';
|
||||
|
||||
let latex = '\\begin{tabular}{';
|
||||
|
||||
// 获取列数
|
||||
const columns = table.querySelectorAll('tr:first-child th, tr:first-child td').length;
|
||||
latex += '|' + 'c|'.repeat(columns) + '}\n\\hline\n';
|
||||
|
||||
// 创建一个二维数组来跟踪单元格合并情况
|
||||
const cellTracker = Array.from({ length: table.rows.length }, () => Array(columns).fill(false));
|
||||
|
||||
// 遍历行
|
||||
table.querySelectorAll('tr').forEach((row, rowIndex) => {
|
||||
const cells = row.querySelectorAll('th, td');
|
||||
let cellTexts: string[] = [];
|
||||
let colIndex = 0;
|
||||
|
||||
cells.forEach((cell) => {
|
||||
// 跳过已经被合并的单元格
|
||||
while (cellTracker[rowIndex][colIndex]) {
|
||||
colIndex++;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const rowspan = parseInt(cell.getAttribute('rowspan') || 1, 10);
|
||||
// @ts-ignore
|
||||
const colspan = parseInt(cell.getAttribute('colspan') || 1, 10);
|
||||
|
||||
// 添加单元格内容
|
||||
let cellText = cell.textContent?.trim() || '';
|
||||
if (colspan > 1) {
|
||||
cellText = `\\multicolumn{${colspan}}{|c|}{${cellText}}`;
|
||||
}
|
||||
if (rowspan > 1) {
|
||||
cellText = `\\multirow{${rowspan}}{*}{${cellText}}`;
|
||||
}
|
||||
cellTexts.push(cellText);
|
||||
|
||||
// 标记合并的单元格
|
||||
for (let i = 0; i < rowspan; i++) {
|
||||
for (let j = 0; j < colspan; j++) {
|
||||
cellTracker[rowIndex + i][colIndex + j] = true;
|
||||
}
|
||||
}
|
||||
|
||||
colIndex += colspan;
|
||||
});
|
||||
|
||||
latex += cellTexts.join(' & ') + ' \\\\\n\\hline\n';
|
||||
});
|
||||
|
||||
latex += '\\end{tabular}';
|
||||
|
||||
return `\`\`\`${CodeClassNameEnum.latex}
|
||||
${latex}
|
||||
\`\`\``;
|
||||
}
|
||||
|
||||
export function convertHtmlTablesToLatex(input: string) {
|
||||
const tableRegex = /<table[\s\S]*?<\/table>/gi;
|
||||
return input.replace(tableRegex, (match) => htmlTableToLatex(match));
|
||||
}
|
@@ -16,6 +16,7 @@ import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
|
||||
type Props = TextareaProps & {
|
||||
title?: string;
|
||||
iconSrc?: string;
|
||||
// variables: string[];
|
||||
};
|
||||
|
||||
@@ -24,7 +25,11 @@ const MyTextarea = React.forwardRef<HTMLTextAreaElement, Props>(function MyTexta
|
||||
const TextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { title = t('common:core.app.edit.Prompt Editor'), ...childProps } = props;
|
||||
const {
|
||||
title = t('common:core.app.edit.Prompt Editor'),
|
||||
iconSrc = 'modal/edit',
|
||||
...childProps
|
||||
} = props;
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
@@ -32,7 +37,7 @@ const MyTextarea = React.forwardRef<HTMLTextAreaElement, Props>(function MyTexta
|
||||
<>
|
||||
<Editor textareaRef={TextareaRef} {...childProps} onOpenModal={onOpen} />
|
||||
{isOpen && (
|
||||
<MyModal iconSrc="/imgs/modal/edit.svg" title={title} isOpen onClose={onClose}>
|
||||
<MyModal iconSrc={iconSrc} title={title} isOpen onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Editor
|
||||
textareaRef={ModalTextareaRef}
|
||||
|
@@ -96,9 +96,7 @@ const VariableEdit = ({
|
||||
<Box>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name={'core/app/simpleMode/variable'} w={'20px'} />
|
||||
<FormLabel ml={2} fontWeight={'medium'}>
|
||||
{t('common:core.module.Variable')}
|
||||
</FormLabel>
|
||||
<FormLabel ml={2}>{t('common:core.module.Variable')}</FormLabel>
|
||||
<ChatFunctionTip type={'variable'} />
|
||||
<Box flex={1} />
|
||||
<Button
|
||||
|
@@ -17,6 +17,8 @@ const WelcomeTextConfig = (props: TextareaProps) => {
|
||||
<ChatFunctionTip type={'welcome'} />
|
||||
</Flex>
|
||||
<MyTextarea
|
||||
iconSrc={'core/app/simpleMode/chat'}
|
||||
title={t('common:core.app.Welcome Text')}
|
||||
mt={2}
|
||||
rows={6}
|
||||
fontSize={'sm'}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import Markdown, { CodeClassName } from '@/components/Markdown';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import { CodeClassNameEnum } from '@/components/Markdown/utils';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
@@ -41,7 +42,7 @@ const AIResponseBox = ({ value, index, chat, isLastChild, isChatting, questionGu
|
||||
index === chat.value.length - 1
|
||||
) {
|
||||
source = `${source}
|
||||
\`\`\`${CodeClassName.questionGuide}
|
||||
\`\`\`${CodeClassNameEnum.questionGuide}
|
||||
${JSON.stringify(questionGuides)}`;
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@ const SearchParamsTip = ({
|
||||
limit = 1500,
|
||||
responseEmptyText,
|
||||
usingReRank = false,
|
||||
datasetSearchUsingExtensionQuery,
|
||||
queryExtensionModel
|
||||
}: {
|
||||
searchMode: `${DatasetSearchModeEnum}`;
|
||||
@@ -21,6 +22,7 @@ const SearchParamsTip = ({
|
||||
limit?: number;
|
||||
responseEmptyText?: string;
|
||||
usingReRank?: boolean;
|
||||
datasetSearchUsingExtensionQuery?: boolean;
|
||||
queryExtensionModel?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -32,11 +34,11 @@ const SearchParamsTip = ({
|
||||
|
||||
const extensionModelName = useMemo(
|
||||
() =>
|
||||
queryExtensionModel
|
||||
datasetSearchUsingExtensionQuery
|
||||
? llmModelList.find((item) => item.model === queryExtensionModel)?.name ??
|
||||
llmModelList[0]?.name
|
||||
: undefined,
|
||||
[llmModelList, queryExtensionModel]
|
||||
[datasetSearchUsingExtensionQuery, llmModelList, queryExtensionModel]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@@ -52,7 +52,7 @@ const ConfigPerModal = ({
|
||||
>
|
||||
<ModalBody>
|
||||
<HStack>
|
||||
<Avatar src={avatar} w={'1.75rem'} />
|
||||
<Avatar src={avatar} w={'1.75rem'} borderRadius={'md'} />
|
||||
<Box>{name}</Box>
|
||||
</HStack>
|
||||
{!isInheritPermission && (
|
||||
|
46
projects/app/src/pages/api/admin/initv488.ts
Normal file
46
projects/app/src/pages/api/admin/initv488.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
await authCert({ req, authRoot: true });
|
||||
|
||||
await MongoDataset.updateMany(
|
||||
{
|
||||
inheritPermission: { $exists: false }
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
inheritPermission: true
|
||||
}
|
||||
}
|
||||
);
|
||||
await MongoDataset.updateMany(
|
||||
{
|
||||
defaultPermission: { $exists: false }
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
defaultPermission: DatasetDefaultPermissionVal
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res, {
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
@@ -89,7 +89,7 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
||||
defaultPermission: updatedDefaultPermission
|
||||
}),
|
||||
// Not root, update default permission
|
||||
...(isDefaultPermissionChanged && { inheritPermission: false }),
|
||||
...(app.parentId && isDefaultPermissionChanged && { inheritPermission: false }),
|
||||
...(teamTags && { teamTags }),
|
||||
...(formatNodes && {
|
||||
modules: formatNodes
|
||||
|
@@ -15,7 +15,7 @@ import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import {
|
||||
filterPluginInputVariables,
|
||||
removePluginInputVariables,
|
||||
updatePluginInputByVariables
|
||||
} from '@fastgpt/global/core/workflow/utils';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
@@ -66,7 +66,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Plugin need to replace inputs
|
||||
if (isPlugin) {
|
||||
nodes = updatePluginInputByVariables(nodes, variables);
|
||||
variables = filterPluginInputVariables(variables, nodes);
|
||||
variables = removePluginInputVariables(variables, nodes);
|
||||
} else {
|
||||
if (!userInput) {
|
||||
throw new Error('Params Error');
|
||||
|
@@ -24,7 +24,7 @@ async function handler(req: NextApiRequest) {
|
||||
searchMode,
|
||||
usingReRank,
|
||||
|
||||
datasetSearchUsingExtensionQuery = false,
|
||||
datasetSearchUsingExtensionQuery = true,
|
||||
datasetSearchExtensionModel,
|
||||
datasetSearchExtensionBg = ''
|
||||
} = req.body as SearchTestProps;
|
||||
|
@@ -78,7 +78,7 @@ async function handler(
|
||||
defaultPermission: updatedDefaultPermission
|
||||
}),
|
||||
// update the defaultPermission
|
||||
...(isDefaultPermissionChanged && { inheritPermission: false })
|
||||
...(dataset.parentId && isDefaultPermissionChanged && { inheritPermission: false })
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
|
@@ -55,7 +55,7 @@ import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import {
|
||||
filterPluginInputVariables,
|
||||
removePluginInputVariables,
|
||||
updatePluginInputByVariables
|
||||
} from '@fastgpt/global/core/workflow/utils';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
@@ -238,7 +238,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
)
|
||||
: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes));
|
||||
|
||||
const runtimeVariables = filterPluginInputVariables(
|
||||
const runtimeVariables = removePluginInputVariables(
|
||||
variables,
|
||||
storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes))
|
||||
);
|
||||
|
@@ -113,8 +113,8 @@ const EditForm = ({
|
||||
...item,
|
||||
parent: {
|
||||
id: 'VARIABLE_NODE_ID',
|
||||
label: '全局变量',
|
||||
avatar: '/imgs/workflow/variable.png'
|
||||
label: t('common:core.module.Variable'),
|
||||
avatar: 'core/workflow/template/variable'
|
||||
}
|
||||
})),
|
||||
[appForm.chatConfig.variables, t]
|
||||
@@ -230,6 +230,7 @@ const EditForm = ({
|
||||
similarity={appForm.dataset.similarity}
|
||||
limit={appForm.dataset.limit}
|
||||
usingReRank={appForm.dataset.usingReRank}
|
||||
datasetSearchUsingExtensionQuery={appForm.dataset.datasetSearchUsingExtensionQuery}
|
||||
queryExtensionModel={appForm.dataset.datasetSearchExtensionModel}
|
||||
/>
|
||||
</Box>
|
||||
|
@@ -92,7 +92,12 @@ const Header = ({
|
||||
)}
|
||||
<Flex pt={[2, 3]} alignItems={'flex-start'} position={'relative'}>
|
||||
<Box flex={'1'}>
|
||||
<FolderPath paths={paths} hoverStyle={{ color: 'primary.600' }} onClick={onclickRoute} />
|
||||
<FolderPath
|
||||
rootName={t('app:all_apps')}
|
||||
paths={paths}
|
||||
hoverStyle={{ color: 'primary.600' }}
|
||||
onClick={onclickRoute}
|
||||
/>
|
||||
</Box>
|
||||
{isPc && (
|
||||
<Box position={'absolute'} left={'50%'} transform={'translateX(-50%)'}>
|
||||
|
@@ -6,6 +6,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
HStack,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputLeftElement,
|
||||
@@ -287,6 +288,8 @@ const RenderList = React.memo(function RenderList({
|
||||
</MyTooltip>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Plugin input config */}
|
||||
{!!configTool && (
|
||||
<MyModal
|
||||
isOpen
|
||||
@@ -295,6 +298,19 @@ const RenderList = React.memo(function RenderList({
|
||||
overflow={'auto'}
|
||||
>
|
||||
<ModalBody>
|
||||
<HStack mb={4} spacing={1} fontSize={'sm'}>
|
||||
<MyIcon name={'common/info'} w={'1.25rem'} />
|
||||
<Box flex={1}>{t('app:tool_input_param_tip')}</Box>
|
||||
{configTool.inputExplanationUrl && (
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
color={'primary.500'}
|
||||
onClick={() => window.open(configTool.inputExplanationUrl, '_blank')}
|
||||
>
|
||||
{t('app:workflow.Input guide')}
|
||||
</Box>
|
||||
)}
|
||||
</HStack>
|
||||
{configTool.inputs
|
||||
.filter((item) => !item.toolDescription)
|
||||
.map((input) => {
|
||||
|
@@ -334,7 +334,7 @@ const RenderList = React.memo(function RenderList({
|
||||
const { computedNewNodeName } = useWorkflowUtils();
|
||||
|
||||
const formatTemplates = useMemo<NodeTemplateListType>(() => {
|
||||
const copy: NodeTemplateListType = cloneDeep(workflowNodeTemplateList(t));
|
||||
const copy: NodeTemplateListType = cloneDeep(workflowNodeTemplateList);
|
||||
templates.forEach((item) => {
|
||||
const index = copy.findIndex((template) => template.type === item.templateType);
|
||||
if (index === -1) return;
|
||||
|
@@ -1,12 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Box, Flex, FlexProps } from '@chakra-ui/react';
|
||||
import { Box, StackProps, HStack } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const IOTitle = ({
|
||||
text,
|
||||
inputExplanationUrl,
|
||||
...props
|
||||
}: { text?: 'Input' | 'Output' | string; inputExplanationUrl?: string } & StackProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const IOTitle = ({ text, ...props }: { text?: 'Input' | 'Output' | string } & FlexProps) => {
|
||||
return (
|
||||
<Flex fontSize={'md'} alignItems={'center'} fontWeight={'medium'} mb={3} {...props}>
|
||||
<Box w={'3px'} h={'14px'} borderRadius={'13px'} bg={'primary.600'} mr={1.5} />
|
||||
{text}
|
||||
</Flex>
|
||||
<HStack fontSize={'md'} alignItems={'center'} fontWeight={'medium'} mb={3} {...props}>
|
||||
<Box w={'3px'} h={'14px'} borderRadius={'13px'} bg={'primary.600'} />
|
||||
<Box>{text}</Box>
|
||||
<Box flex={1} />
|
||||
|
||||
{inputExplanationUrl && (
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
color={'primary.500'}
|
||||
onClick={() => window.open(inputExplanationUrl, '_blank')}
|
||||
>
|
||||
{t('app:workflow.Input guide')}
|
||||
</Box>
|
||||
)}
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -37,7 +37,10 @@ const NodeSimple = ({
|
||||
{filterHiddenInputs.length > 0 && (
|
||||
<>
|
||||
<Container>
|
||||
<IOTitle text={t('common:common.Input')} />
|
||||
<IOTitle
|
||||
text={t('common:common.Input')}
|
||||
inputExplanationUrl={data.inputExplanationUrl}
|
||||
/>
|
||||
<RenderInput nodeId={nodeId} flowInputList={commonInputs} />
|
||||
</Container>
|
||||
</>
|
||||
|
@@ -80,27 +80,36 @@ const NodeCard = (props: Props) => {
|
||||
|
||||
const node = nodeList.find((node) => node.nodeId === nodeId);
|
||||
const { openConfirm: onOpenConfirmSync, ConfirmModal: ConfirmSyncModal } = useConfirm({
|
||||
content: appT('module.Confirm Sync')
|
||||
content: t('app:module.Confirm Sync')
|
||||
});
|
||||
|
||||
const { data: newNodeVersion, runAsync: getNodeVersion } = useRequest2(
|
||||
const { data: nodeTemplate, runAsync: getNodeLatestTemplate } = useRequest2(
|
||||
async () => {
|
||||
if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) {
|
||||
if (!node?.pluginId) return;
|
||||
const template = await getPreviewPluginNode({ appId: node.pluginId });
|
||||
return template.version;
|
||||
|
||||
// Focus update plugin latest inputExplanationUrl
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'attr',
|
||||
key: 'inputExplanationUrl',
|
||||
value: template.inputExplanationUrl
|
||||
});
|
||||
|
||||
return template;
|
||||
} else {
|
||||
const template = moduleTemplatesFlat.find(
|
||||
(item) => item.flowNodeType === node?.flowNodeType
|
||||
);
|
||||
return template?.version;
|
||||
return template;
|
||||
}
|
||||
},
|
||||
{
|
||||
manual: false
|
||||
}
|
||||
);
|
||||
const hasNewVersion = newNodeVersion && newNodeVersion !== node?.version;
|
||||
const hasNewVersion = nodeTemplate && nodeTemplate.version !== node?.version;
|
||||
|
||||
const { runAsync: onClickSyncVersion } = useRequest2(
|
||||
async () => {
|
||||
@@ -119,10 +128,10 @@ const NodeCard = (props: Props) => {
|
||||
node: getLatestNodeTemplate(node, template)
|
||||
});
|
||||
}
|
||||
await getNodeVersion();
|
||||
await getNodeLatestTemplate();
|
||||
},
|
||||
{
|
||||
refreshDeps: [node, nodeId, onResetNode, getNodeVersion]
|
||||
refreshDeps: [node, nodeId, onResetNode, getNodeLatestTemplate]
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { Box, Button, Flex, useDisclosure } from '@chakra-ui/react';
|
||||
import { Flex, useDisclosure } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
@@ -54,7 +54,8 @@ const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => {
|
||||
if (data[input.key] !== undefined) {
|
||||
setData((state) => ({
|
||||
...state,
|
||||
[input.key]: input.value
|
||||
// @ts-ignore
|
||||
[input.key]: input.value || state[input.key]
|
||||
}));
|
||||
}
|
||||
});
|
||||
@@ -82,6 +83,7 @@ const SelectDatasetParam = ({ inputs = [], nodeId }: RenderInputProps) => {
|
||||
similarity={data.similarity}
|
||||
limit={data.limit}
|
||||
usingReRank={data.usingReRank}
|
||||
datasetSearchUsingExtensionQuery={data.datasetSearchUsingExtensionQuery}
|
||||
queryExtensionModel={data.datasetSearchExtensionModel}
|
||||
/>
|
||||
</>
|
||||
|
@@ -10,7 +10,7 @@ const TextInput = ({ item, nodeId }: RenderInputProps) => {
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<Input
|
||||
placeholder={item.placeholder}
|
||||
placeholder={item.placeholder ?? item.description}
|
||||
defaultValue={item.value}
|
||||
bg={'white'}
|
||||
px={3}
|
||||
|
@@ -14,7 +14,6 @@ import { AppUpdateParams } from '@/global/core/app/api';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { useThrottleEffect } from 'ahooks';
|
||||
const MoveModal = dynamic(() => import('@/components/common/folder/MoveModal'));
|
||||
|
||||
type AppListContextType = {
|
||||
|
@@ -131,10 +131,10 @@ const MobileDrawer = ({
|
||||
}}
|
||||
list={[
|
||||
...(isTeamChat
|
||||
? [{ label: t('common:all_apps'), value: TabEnum.recently }]
|
||||
? [{ label: t('app:all_apps'), value: TabEnum.recently }]
|
||||
: [
|
||||
{ label: t('common:core.chat.Recent use'), value: TabEnum.recently },
|
||||
{ label: t('common:all_apps'), value: TabEnum.app }
|
||||
{ label: t('app:all_apps'), value: TabEnum.app }
|
||||
])
|
||||
]}
|
||||
value={currentTab}
|
||||
|
@@ -71,7 +71,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
|
||||
usingReRank: false,
|
||||
limit: 5000,
|
||||
similarity: 0,
|
||||
datasetSearchUsingExtensionQuery: false,
|
||||
datasetSearchUsingExtensionQuery: true,
|
||||
datasetSearchExtensionModel: llmModelList[0].model,
|
||||
datasetSearchExtensionBg: ''
|
||||
}
|
||||
@@ -432,6 +432,7 @@ const TestResults = React.memo(function TestResults({
|
||||
similarity={datasetTestItem.similarity}
|
||||
limit={datasetTestItem.limit}
|
||||
usingReRank={datasetTestItem.usingReRank}
|
||||
datasetSearchUsingExtensionQuery={!!datasetTestItem.queryExtensionModel}
|
||||
queryExtensionModel={datasetTestItem.queryExtensionModel}
|
||||
/>
|
||||
</Box>
|
||||
|
@@ -68,7 +68,11 @@ function DatasetContextProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const { parentId = null } = router.query as { parentId?: string | null };
|
||||
|
||||
const { data: myDatasets = [], runAsync: loadMyDatasets } = useRequest2(
|
||||
const {
|
||||
data: myDatasets = [],
|
||||
runAsync: loadMyDatasets,
|
||||
loading: isFetchingDatasets
|
||||
} = useRequest2(
|
||||
() =>
|
||||
getDatasets({
|
||||
parentId
|
||||
@@ -83,7 +87,7 @@ function DatasetContextProvider({ children }: { children: React.ReactNode }) {
|
||||
() => (parentId ? getDatasetById(parentId) : Promise.resolve(undefined)),
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [parentId, myDatasets]
|
||||
refreshDeps: [parentId]
|
||||
}
|
||||
);
|
||||
|
||||
@@ -95,16 +99,8 @@ function DatasetContextProvider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
);
|
||||
|
||||
const { runAsync: refetchDatasets, loading: isFetchingDatasets } = useRequest2(
|
||||
() => loadMyDatasets(),
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [parentId]
|
||||
}
|
||||
);
|
||||
|
||||
const { runAsync: onUpdateDataset } = useRequest2(putDatasetById, {
|
||||
onSuccess: () => Promise.all([refetchDatasets(), refetchPaths(), loadMyDatasets()])
|
||||
onSuccess: () => Promise.all([refetchFolderDetail(), refetchPaths(), loadMyDatasets()])
|
||||
});
|
||||
|
||||
const onMoveDataset = useCallback(
|
||||
@@ -138,7 +134,6 @@ function DatasetContextProvider({ children }: { children: React.ReactNode }) {
|
||||
});
|
||||
|
||||
const contextValue = {
|
||||
refetchDatasets,
|
||||
isFetchingDatasets,
|
||||
setMoveDatasetId,
|
||||
paths,
|
||||
|
@@ -40,8 +40,8 @@ export const getGlobalVariableNode = ({
|
||||
flowNodeType: FlowNodeTypeEnum.emptyNode,
|
||||
sourceHandle: getHandleConfig(false, false, false, false),
|
||||
targetHandle: getHandleConfig(false, false, false, false),
|
||||
avatar: '/imgs/workflow/variable.png',
|
||||
name: '全局变量',
|
||||
avatar: 'core/workflow/template/variable',
|
||||
name: t('common:core.module.Variable'),
|
||||
intro: '',
|
||||
unique: true,
|
||||
forbidDelete: true,
|
||||
|
Reference in New Issue
Block a user