4.7 doc update (#1068)

* fix: plugin update

* feat: get current time plugin

* fix: ts

* perf: select app ux

* fix: ts

* perf: max w

* move code

* perf: inform tip

* fix: inform

* doc

* fix: tool handle

* perf: tmp file store

* doc

* fix: message file selector

* feat: doc

* perf: switch trigger

* doc

* fix: openapi import

* rount the number

* parse openapi schema

* fix empty line after variables (#64)

* doc image

* image size

* doc

* doc

* catch error

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-03-27 12:50:07 +08:00
committed by GitHub
parent c7e6448272
commit 6b7b03c245
160 changed files with 1393 additions and 558 deletions

View File

@@ -84,7 +84,9 @@
"charsPointsPrice": 0,
"defaultToken": 700,
"maxToken": 3000,
"weight": 100
"weight": 100,
"dbConfig": {},
"queryConfig": {}
}
],
"reRankModels": [],

View File

@@ -9,7 +9,6 @@
"lint": "next lint"
},
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@bany/curl-to-json": "^1.2.8",
"@chakra-ui/anatomy": "2.2.1",
"@chakra-ui/icons": "2.1.1",

View File

@@ -1,7 +1,13 @@
### FastGPT V4.7
1. 新增 - 工具调用模块可以让LLM模型根据用户意图动态的选择其他模型或插件执行。
2. 优化 - 高级编排性能
3. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro)
4. [使用文档](https://doc.fastgpt.in/docs/intro/)
5. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/)
2. 新增 - 分类和内容提取支持 functionCall 模式。部分模型支持 functionCall 不支持 ToolCall也可以使用了。需要把 LLM 模型配置文件里的 `functionCall` 设置为 `true` `toolChoice`设置为 `false`。如果 `toolChoice` 为 true会走 tool 模式。
3. 新增 - HTTP插件可实现OpenAPI快速生成插件。
4. 优化 - 高级编排性能。
5. 优化 - AI模型选择。
6. 优化 - 手动输入知识库弹窗。
7. 优化 - 变量输入弹窗。
8. 优化 - 浏览器读取文件自动推断编码,减少乱码情况。
9. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro)
10. [使用文档](https://doc.fastgpt.in/docs/intro/)
11. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/)

View File

@@ -405,7 +405,7 @@ export const FlowProvider = ({
toolInputs: inputs.filter((item) => isTool && item.toolDescription),
commonInputs: inputs.filter((item) => {
if (!isTool) return true;
return !item.toolDescription && item.key !== ModuleInputKeyEnum.switch;
return !item.toolDescription;
})
};
},

View File

@@ -4,7 +4,7 @@ import { BoxProps } from '@chakra-ui/react';
const Container = ({ children, ...props }: BoxProps) => {
return (
<Box px={'16px'} py={'10px'} position={'relative'} {...props}>
<Box px={4} py={'10px'} position={'relative'} {...props}>
{children}
</Box>
);

View File

@@ -16,6 +16,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import { ToolTargetHandle } from './ToolHandle';
import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea';
import TriggerAndFinish from './RenderInput/templates/TriggerAndFinish';
type Props = FlowModuleItemType & {
children?: React.ReactNode | React.ReactNode[] | string;
@@ -208,6 +209,8 @@ const NodeCard = (props: Props) => {
</Button>
)}
</Flex>
{/* switch */}
<TriggerAndFinish moduleId={moduleId} isTool={moduleIsTool} />
</Box>
);
}, [

View File

@@ -13,10 +13,6 @@ const RenderList: {
types: `${FlowNodeInputTypeEnum}`[];
Component: React.ComponentType<RenderInputProps>;
}[] = [
{
types: [FlowNodeInputTypeEnum.triggerAndFinish],
Component: dynamic(() => import('./templates/TriggerAndFinish'))
},
{
types: [FlowNodeInputTypeEnum.input],
Component: dynamic(() => import('./templates/TextInput'))

View File

@@ -7,10 +7,18 @@ import SourceHandle from '../../SourceHandle';
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import { useFlowProviderStore } from '../../../../FlowProvider';
const TriggerAndFinish = ({ moduleId }: RenderInputProps) => {
const TriggerAndFinish = ({ moduleId, isTool }: { moduleId: string; isTool: boolean }) => {
const { t } = useTranslation();
const { nodes } = useFlowProviderStore();
const inputs = useMemo(
() => nodes.find((node) => node.data.moduleId === moduleId)?.data?.inputs || [],
[moduleId, nodes]
);
const hasSwitch = useMemo(
() => inputs.some((input) => input.key === ModuleInputKeyEnum.switch),
[inputs]
);
const outputs = useMemo(
() => nodes.find((node) => node.data.moduleId === moduleId)?.data?.outputs || [],
[moduleId, nodes]
@@ -20,8 +28,8 @@ const TriggerAndFinish = ({ moduleId }: RenderInputProps) => {
[outputs]
);
const Render = useMemo(
() => (
const Render = useMemo(() => {
return (
<Flex
className="nodrag"
cursor={'default'}
@@ -30,21 +38,24 @@ const TriggerAndFinish = ({ moduleId }: RenderInputProps) => {
position={'relative'}
>
<Box position={'relative'}>
<TargetHandle handleKey={ModuleInputKeyEnum.switch} valueType={'any'} />
{t('core.module.input.label.switch')}
{!isTool && (
<Box mt={2}>
<TargetHandle handleKey={ModuleInputKeyEnum.switch} valueType={'any'} />
{t('core.module.input.label.switch')}
</Box>
)}
</Box>
{hasFinishOutput && (
<Box position={'relative'}>
<Box position={'relative'} mt={2}>
{t('core.module.output.label.running done')}
<SourceHandle handleKey={ModuleOutputKeyEnum.finish} valueType={'boolean'} />
</Box>
)}
</Flex>
),
[hasFinishOutput, t]
);
);
}, [hasFinishOutput, isTool, t]);
return Render;
return hasSwitch ? Render : null;
};
export default React.memo(TriggerAndFinish);

View File

@@ -28,8 +28,8 @@ const SourceHandle = ({ handleKey, valueType, ...props }: Props) => {
<Box
position={'absolute'}
top={'50%'}
right={'-20px'}
transform={'translate(50%,-50%)'}
right={'-18px'}
transform={'translate(0,-50%)'}
{...props}
>
<MyTooltip

View File

@@ -27,8 +27,8 @@ const TargetHandle = ({ handleKey, valueType, ...props }: Props) => {
<Box
position={'absolute'}
top={'50%'}
left={'-20px'}
transform={'translate(50%,-50%)'}
left={'-18px'}
transform={'translate(0,-50%)'}
{...props}
>
<MyTooltip

View File

@@ -41,6 +41,7 @@ export const ToolTargetHandle = ({ moduleId }: ToolHandleProps) => {
h={'14px'}
border={'4px solid #5E8FFF'}
transform={'translate(-40%,-30%) rotate(45deg)'}
pointerEvents={'none'}
/>
</Handle>
</MyTooltip>
@@ -98,6 +99,7 @@ export const ToolSourceHandle = ({ moduleId }: ToolHandleProps) => {
h={'14px'}
border={'4px solid #5E8FFF'}
transform={'translate(-40%,-30%) rotate(45deg)'}
pointerEvents={'none'}
/>
</Handle>
</MyTooltip>

View File

@@ -4,7 +4,6 @@ import { connectToDatabase } from '@/service/mongo';
import type { CreateOnePluginParams } from '@fastgpt/global/core/plugin/controller';
import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user';
import { MongoPlugin } from '@fastgpt/service/core/plugin/schema';
import { checkTeamPluginLimit } from '@fastgpt/service/support/permission/teamLimit';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { httpApiSchema2Plugins } from '@fastgpt/global/core/plugin/httpPlugin/utils';
@@ -31,7 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
{ session }
);
const childrenPlugins = httpApiSchema2Plugins({
const childrenPlugins = await httpApiSchema2Plugins({
parentId,
apiSchemaStr: body.metadata?.apiSchemaStr,
customHeader: body.metadata?.customHeaders

View File

@@ -1,15 +1,13 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import SwaggerParser from '@apidevtools/swagger-parser';
import { loadOpenAPISchemaFromUrl } from '@fastgpt/global/common/string/swagger';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const apiURL = req.body.url as string;
const api = await SwaggerParser.validate(apiURL);
return jsonRes(res, {
data: api
data: await loadOpenAPISchemaFromUrl(apiURL)
});
} catch (err) {
jsonRes(res, {

View File

@@ -75,7 +75,7 @@ const updateHttpChildrenPlugin = async ({
'_id metadata'
);
const schemaPlugins = httpApiSchema2Plugins({
const schemaPlugins = await httpApiSchema2Plugins({
parentId: parent.id,
apiSchemaStr: parent.metadata?.apiSchemaStr,
customHeader: parent.metadata?.customHeaders

View File

@@ -13,8 +13,7 @@ import {
Tbody,
Tr,
Td,
IconButton,
useDisclosure
IconButton
} from '@chakra-ui/react';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useForm } from 'react-hook-form';
@@ -37,12 +36,12 @@ import {
import { str2OpenApiSchema } from '@fastgpt/global/core/plugin/httpPlugin/utils';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { AddIcon } from '@chakra-ui/icons';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { EditFormType } from './type';
import { FolderImgUrl } from '@fastgpt/global/common/file/image/constants';
import HttpInput from '@fastgpt/web/components/common/Input/HttpInput';
import { HttpHeaders } from '@/components/core/module/Flow/components/nodes/NodeHttp';
import { OpenApiJsonSchema } from '@fastgpt/global/core/plugin/httpPlugin/type';
export const defaultHttpPlugin: CreateOnePluginParams = {
avatar: FolderImgUrl,
@@ -85,20 +84,7 @@ const HttpPluginEditModal = ({
defaultValues: defaultPlugin
});
const apiSchemaStr = watch('metadata.apiSchemaStr');
const apiData = useMemo(() => {
if (!apiSchemaStr) {
return { pathData: [], serverPath: '' };
}
try {
return str2OpenApiSchema(apiSchemaStr);
} catch (err) {
toast({
status: 'warning',
title: t('plugin.Invalid Schema')
});
return { pathData: [], serverPath: '' };
}
}, [apiSchemaStr, t, toast]);
const [apiData, setApiData] = useState<OpenApiJsonSchema>({ pathData: [], serverPath: '' });
const { mutate: onCreate, isLoading: isCreating } = useRequest({
mutationFn: async (data: CreateOnePluginParams) => {
@@ -201,6 +187,23 @@ const HttpPluginEditModal = ({
[customHeaders]
);
useEffect(() => {
(async () => {
if (!apiSchemaStr) {
return setApiData({ pathData: [], serverPath: '' });
}
try {
setApiData(await str2OpenApiSchema(apiSchemaStr));
} catch (err) {
toast({
status: 'warning',
title: t('plugin.Invalid Schema')
});
setApiData({ pathData: [], serverPath: '' });
}
})();
}, [apiSchemaStr, t, toast]);
return (
<>
<MyModal

View File

@@ -39,6 +39,9 @@ const ExtraPlan = () => {
const onclickBuyDatasetSize = useCallback(
async ({ datasetSize, month }: { datasetSize: number; month: number }) => {
try {
datasetSize = Math.ceil(datasetSize);
month = Math.ceil(month);
const datasetSizePayAmount = datasetSize * month * extraDatasetPrice;
if (datasetSizePayAmount === 0) {
return toast({
@@ -80,6 +83,8 @@ const ExtraPlan = () => {
const onclickBuyExtraPoints = useCallback(
async ({ points }: { points: number }) => {
try {
points = Math.ceil(points);
const month = 1;
const payAmount = points * month * extraPointsPrice;

View File

@@ -11,6 +11,8 @@ import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { initGlobal } from './common/system';
import { startMongoWatch } from './common/system/volumnMongoWatch';
import { startTrainingQueue } from './core/dataset/training/utils';
import { clearDirFiles } from '@fastgpt/service/common/file/utils';
import { tmpFileDirPath } from '@fastgpt/service/common/file/constants';
/**
* connect MongoDB and init data
@@ -32,6 +34,9 @@ export function connectToDatabase(): Promise<void> {
// start queue
startTrainingQueue(true);
// clear tmp files
clearDirFiles(tmpFileDirPath);
}
});
}

View File

@@ -23,6 +23,8 @@ type StreamResponseType = {
responseText: string;
[DispatchNodeResponseKeyEnum.nodeResponse]: ChatHistoryItemResType[];
};
class FatalError extends Error {}
export const streamFetch = ({
url = '/api/v1/chat/completions',
data,
@@ -206,9 +208,12 @@ export const streamFetch = ({
onclose() {
finished = true;
},
onerror(e) {
onerror(err) {
if (err instanceof FatalError) {
throw err;
}
clearTimeout(timeoutId);
failedFinish(getErrText(e));
failedFinish(getErrText(err));
},
openWhenHidden: true
});

View File

@@ -15,7 +15,10 @@ export function checkChatSupportSelectFileByChatModels(models: string[] = []) {
}
export function checkChatSupportSelectFileByModules(modules: ModuleItemType[] = []) {
const chatModules = modules.filter((item) => item.flowType === FlowNodeTypeEnum.chatNode);
const chatModules = modules.filter(
(item) =>
item.flowType === FlowNodeTypeEnum.chatNode || item.flowType === FlowNodeTypeEnum.tools
);
const models: string[] = chatModules.map(
(item) => item.inputs.find((item) => item.key === 'model')?.value || ''
);