Compare commits
6 Commits
v4.8.15-al
...
v4.8.15-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15dc7b220e | ||
|
|
c64d629a6a | ||
|
|
1a294c1fd3 | ||
|
|
a4f6128a89 | ||
|
|
021ec0595d | ||
|
|
6ceee7cb5e |
@@ -27,9 +27,6 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
|
|||||||
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
|
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
|
||||||
<img height="21" src="https://img.shields.io/badge/相关项目-7d09f1?style=flat-square" alt="project">
|
<img height="21" src="https://img.shields.io/badge/相关项目-7d09f1?style=flat-square" alt="project">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/labring/FastGPT/blob/main/LICENSE">
|
|
||||||
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409bd33f6d4
|
https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409bd33f6d4
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ PDF 是一个相对复杂的文件格式,在 FastGPT 内置的 pdf 解析器
|
|||||||
|
|
||||||
参考文档 [Marker 安装教程](https://github.com/labring/FastGPT/tree/main/python/pdf-marker),安装 Marker 模型。封装的 API 已经适配了 FastGPT 自定义解析服务。
|
参考文档 [Marker 安装教程](https://github.com/labring/FastGPT/tree/main/python/pdf-marker),安装 Marker 模型。封装的 API 已经适配了 FastGPT 自定义解析服务。
|
||||||
|
|
||||||
这里介绍快速 Docker 按照的方法:
|
这里介绍快速 Docker 安装的方法:
|
||||||
|
|
||||||
```
|
```dockerfile
|
||||||
|
docker pull crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:latest
|
||||||
|
docker run --gpus all -itd -p 7231:7231 --name model_pdf_v1 crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 添加 FastGPT 环境变量
|
### 2. 添加 FastGPT 环境变量
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ milvus版本使用:v4.8.14-milvus-fix 镜像。
|
|||||||
2. 新增 - 重写 chatContext,对话测试也会有日志,并且刷新后不会丢失对话。
|
2. 新增 - 重写 chatContext,对话测试也会有日志,并且刷新后不会丢失对话。
|
||||||
3. 新增 - 分享链接支持配置是否允许查看原文。
|
3. 新增 - 分享链接支持配置是否允许查看原文。
|
||||||
4. 新增 - 新的 doc2x 插件。
|
4. 新增 - 新的 doc2x 插件。
|
||||||
5. 新增 - 繁体中文-台湾。
|
5. 新增 - 繁体中文。
|
||||||
6. 新增 - 分析链接和 chat api 支持传入自定义 uid。
|
6. 新增 - 分析链接和 chat api 支持传入自定义 uid。
|
||||||
7. 商业版新增 - 微软 oauth 登录
|
7. 商业版新增 - 微软 oauth 登录
|
||||||
8. 优化 - 工作流 ui 细节。
|
8. 优化 - 工作流 ui 细节。
|
||||||
|
|||||||
@@ -46,3 +46,4 @@ weight: 809
|
|||||||
14. 修复 - 语言播放鉴权问题。
|
14. 修复 - 语言播放鉴权问题。
|
||||||
15. 修复 - 插件应用知识库引用上限始终为 3000
|
15. 修复 - 插件应用知识库引用上限始终为 3000
|
||||||
16. 修复 - 工作流编辑记录存储上限,去掉本地存储,增加异常离开时,强制自动保存。
|
16. 修复 - 工作流编辑记录存储上限,去掉本地存储,增加异常离开时,强制自动保存。
|
||||||
|
17. 修复 - 工作流特殊变量替换问题。($开头的字符串无法替换)
|
||||||
@@ -4,3 +4,15 @@ export const delay = (ms: number) =>
|
|||||||
resolve('');
|
resolve('');
|
||||||
}, ms);
|
}, ms);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const retryFn = async <T>(fn: () => Promise<T>, retryTimes = 3): Promise<T> => {
|
||||||
|
try {
|
||||||
|
return fn();
|
||||||
|
} catch (error) {
|
||||||
|
if (retryTimes > 0) {
|
||||||
|
await delay(500);
|
||||||
|
return retryFn(fn, retryTimes - 1);
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
3
packages/global/core/dataset/type.d.ts
vendored
3
packages/global/core/dataset/type.d.ts
vendored
@@ -34,6 +34,9 @@ export type DatasetSchemaType = {
|
|||||||
inheritPermission: boolean;
|
inheritPermission: boolean;
|
||||||
apiServer?: APIFileServer;
|
apiServer?: APIFileServer;
|
||||||
|
|
||||||
|
syncSchedule?: { cronString: string; timezone: string };
|
||||||
|
syncNextTime?: Date;
|
||||||
|
|
||||||
// abandon
|
// abandon
|
||||||
externalReadUrl?: string;
|
externalReadUrl?: string;
|
||||||
defaultPermission?: number;
|
defaultPermission?: number;
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ export function replaceEditorVariable({
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, 'g');
|
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, 'g');
|
||||||
text = text.replace(regex, formatVal);
|
text = text.replace(regex, () => formatVal);
|
||||||
});
|
});
|
||||||
|
|
||||||
return text || '';
|
return text || '';
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ const SystemPluginSchema = new Schema({
|
|||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
isActive: {
|
isActive: {
|
||||||
type: Boolean,
|
type: Boolean
|
||||||
required: true
|
|
||||||
},
|
},
|
||||||
inputConfig: {
|
inputConfig: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
|||||||
2
packages/service/core/app/plugin/type.d.ts
vendored
2
packages/service/core/app/plugin/type.d.ts
vendored
@@ -13,7 +13,7 @@ export type SystemPluginConfigSchemaType = {
|
|||||||
hasTokenFee: boolean;
|
hasTokenFee: boolean;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
pluginOrder: number;
|
pluginOrder: number;
|
||||||
inputConfig: SystemPluginTemplateItemType['inputConfig'];
|
inputConfig?: SystemPluginTemplateItemType['inputConfig'];
|
||||||
|
|
||||||
customConfig?: {
|
customConfig?: {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -121,6 +121,6 @@ const AppSchema = new Schema({
|
|||||||
|
|
||||||
AppSchema.index({ teamId: 1, updateTime: -1 });
|
AppSchema.index({ teamId: 1, updateTime: -1 });
|
||||||
AppSchema.index({ teamId: 1, type: 1 });
|
AppSchema.index({ teamId: 1, type: 1 });
|
||||||
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
|
AppSchema.index({ scheduledTriggerConfig: 1, scheduledTriggerNextTime: -1 });
|
||||||
|
|
||||||
export const MongoApp = getMongoModel<AppType>(AppCollectionName, AppSchema);
|
export const MongoApp = getMongoModel<AppType>(AppCollectionName, AppSchema);
|
||||||
|
|||||||
@@ -91,6 +91,18 @@ const DatasetSchema = new Schema({
|
|||||||
type: Object
|
type: Object
|
||||||
},
|
},
|
||||||
|
|
||||||
|
syncSchedule: {
|
||||||
|
cronString: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
timezone: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
syncNextTime: {
|
||||||
|
type: Date
|
||||||
|
},
|
||||||
|
|
||||||
// abandoned
|
// abandoned
|
||||||
externalReadUrl: {
|
externalReadUrl: {
|
||||||
type: String
|
type: String
|
||||||
@@ -100,6 +112,7 @@ const DatasetSchema = new Schema({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
DatasetSchema.index({ teamId: 1 });
|
DatasetSchema.index({ teamId: 1 });
|
||||||
|
DatasetSchema.index({ syncSchedule: 1, syncNextTime: -1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { i18nT } from '../../i18n/utils';
|
import { i18nT } from '../../i18n/utils';
|
||||||
|
import type { PluginGroupSchemaType, TGroupType } from '../../../service/core/app/plugin/type';
|
||||||
|
|
||||||
export const workflowNodeTemplateList = [
|
export const workflowNodeTemplateList = [
|
||||||
{
|
{
|
||||||
@@ -49,10 +50,7 @@ export const workflowNodeTemplateList = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const systemPluginTemplateList: {
|
export const systemPluginTemplateList: TGroupType[] = [
|
||||||
typeId: string;
|
|
||||||
typeName: string;
|
|
||||||
}[] = [
|
|
||||||
{
|
{
|
||||||
typeId: FlowNodeTemplateTypeEnum.tools,
|
typeId: FlowNodeTemplateTypeEnum.tools,
|
||||||
typeName: i18nT('common:navbar.Tools')
|
typeName: i18nT('common:navbar.Tools')
|
||||||
@@ -74,7 +72,7 @@ export const systemPluginTemplateList: {
|
|||||||
typeName: i18nT('common:common.Other')
|
typeName: i18nT('common:common.Other')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
export const defaultGroup = {
|
export const defaultGroup: PluginGroupSchemaType = {
|
||||||
groupId: 'systemPlugin',
|
groupId: 'systemPlugin',
|
||||||
groupAvatar: 'common/navbar/pluginLight',
|
groupAvatar: 'common/navbar/pluginLight',
|
||||||
groupName: i18nT('common:core.module.template.System Plugin'),
|
groupName: i18nT('common:core.module.template.System Plugin'),
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ export const useI18nLng = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const currentLng = i18n?.language;
|
const currentLng = i18n?.language;
|
||||||
await i18n?.changeLanguage?.(lang);
|
if (!currentLng) return;
|
||||||
|
|
||||||
|
await i18n?.changeLanguage?.(lang);
|
||||||
if (currentLng !== lang) {
|
if (currentLng !== lang) {
|
||||||
window?.location?.reload?.();
|
window?.location?.reload?.();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ const Navbar = ({ unread }: { unread: number }) => {
|
|||||||
borderRadius={'50%'}
|
borderRadius={'50%'}
|
||||||
overflow={'hidden'}
|
overflow={'hidden'}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
onClick={() => router.push('/account')}
|
onClick={() => router.push('/account/info')}
|
||||||
>
|
>
|
||||||
<Avatar w={'2rem'} h={'2rem'} src={userInfo?.avatar} borderRadius={'50%'} />
|
<Avatar w={'2rem'} h={'2rem'} src={userInfo?.avatar} borderRadius={'50%'} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ const AccountContainer = ({
|
|||||||
<Box mb={3}>
|
<Box mb={3}>
|
||||||
<LightRowTabs<TabEnum>
|
<LightRowTabs<TabEnum>
|
||||||
m={'auto'}
|
m={'auto'}
|
||||||
|
w={'100%'}
|
||||||
size={isPc ? 'md' : 'sm'}
|
size={isPc ? 'md' : 'sm'}
|
||||||
list={tabList.map((item) => ({
|
list={tabList.map((item) => ({
|
||||||
value: item.value,
|
value: item.value,
|
||||||
|
|||||||
@@ -61,10 +61,9 @@ const AiPointsModal = dynamic(() =>
|
|||||||
|
|
||||||
const Info = () => {
|
const Info = () => {
|
||||||
const { isPc } = useSystem();
|
const { isPc } = useSystem();
|
||||||
const { teamPlanStatus } = useUserStore();
|
const { teamPlanStatus, initUserInfo } = useUserStore();
|
||||||
const standardPlan = teamPlanStatus?.standardConstants;
|
const standardPlan = teamPlanStatus?.standardConstants;
|
||||||
const { isOpen: isOpenContact, onClose: onCloseContact, onOpen: onOpenContact } = useDisclosure();
|
const { isOpen: isOpenContact, onClose: onCloseContact, onOpen: onOpenContact } = useDisclosure();
|
||||||
const { initUserInfo } = useUserStore();
|
|
||||||
|
|
||||||
useQuery(['init'], initUserInfo);
|
useQuery(['init'], initUserInfo);
|
||||||
|
|
||||||
@@ -112,15 +111,13 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { feConfigs } = useSystemStore();
|
const { feConfigs } = useSystemStore();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userInfo, updateUserInfo } = useUserStore();
|
const { userInfo, updateUserInfo, teamPlanStatus } = useUserStore();
|
||||||
const { reset } = useForm<UserUpdateParams>({
|
const { reset } = useForm<UserUpdateParams>({
|
||||||
defaultValues: userInfo as UserType
|
defaultValues: userInfo as UserType
|
||||||
});
|
});
|
||||||
const { teamPlanStatus } = useUserStore();
|
|
||||||
const standardPlan = teamPlanStatus?.standardConstants;
|
const standardPlan = teamPlanStatus?.standardConstants;
|
||||||
const { isPc } = useSystem();
|
const { isPc } = useSystem();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isOpenConversionModal,
|
isOpen: isOpenConversionModal,
|
||||||
@@ -305,12 +302,14 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
<Flex mt={6} alignItems={'center'}>
|
{feConfigs.isPlus && (
|
||||||
<Box {...labelStyles}>{t('account_info:user_team_team_name')}: </Box>
|
<Flex mt={6} alignItems={'center'}>
|
||||||
<Flex flex={'1 0 0'} w={0} align={'center'}>
|
<Box {...labelStyles}>{t('account_info:user_team_team_name')}: </Box>
|
||||||
<TeamSelector height={'28px'} w={'100%'} showManage />
|
<Flex flex={'1 0 0'} w={0} align={'center'}>
|
||||||
|
<TeamSelector height={'28px'} w={'100%'} showManage />
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
)}
|
||||||
{feConfigs?.isPlus && (userInfo?.team?.balance ?? 0) > 0 && (
|
{feConfigs?.isPlus && (userInfo?.team?.balance ?? 0) > 0 && (
|
||||||
<Box mt={6} whiteSpace={'nowrap'}>
|
<Box mt={6} whiteSpace={'nowrap'}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
|
|||||||
@@ -292,63 +292,7 @@ export const useWorkflow = () => {
|
|||||||
const { getIntersectingNodes } = useReactFlow();
|
const { getIntersectingNodes } = useReactFlow();
|
||||||
const { isDowningCtrl } = useKeyboard();
|
const { isDowningCtrl } = useKeyboard();
|
||||||
|
|
||||||
// Loop node size and position
|
const { resetParentNodeSizeAndPosition } = useLoopNode();
|
||||||
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
|
|
||||||
const { childNodes, loopNode } = nodes.reduce(
|
|
||||||
(acc, node) => {
|
|
||||||
if (node.data.parentNodeId === parentId) {
|
|
||||||
acc.childNodes.push(node);
|
|
||||||
}
|
|
||||||
if (node.id === parentId) {
|
|
||||||
acc.loopNode = node;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{ childNodes: [] as Node[], loopNode: undefined as Node<FlowNodeItemType> | undefined }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!loopNode) return;
|
|
||||||
|
|
||||||
const rect = getNodesBounds(childNodes);
|
|
||||||
// Calculate parent node size with minimum width/height constraints
|
|
||||||
const width = Math.max(rect.width + 80, 840);
|
|
||||||
const height = Math.max(rect.height + 80, 600);
|
|
||||||
|
|
||||||
const offsetHeight =
|
|
||||||
loopNode.data.inputs.find((input) => input.key === NodeInputKeyEnum.loopNodeInputHeight)
|
|
||||||
?.value ?? 83;
|
|
||||||
|
|
||||||
// Update parentNode size and position
|
|
||||||
onChangeNode({
|
|
||||||
nodeId: parentId,
|
|
||||||
type: 'updateInput',
|
|
||||||
key: NodeInputKeyEnum.nodeWidth,
|
|
||||||
value: {
|
|
||||||
...Input_Template_Node_Width,
|
|
||||||
value: width
|
|
||||||
}
|
|
||||||
});
|
|
||||||
onChangeNode({
|
|
||||||
nodeId: parentId,
|
|
||||||
type: 'updateInput',
|
|
||||||
key: NodeInputKeyEnum.nodeHeight,
|
|
||||||
value: {
|
|
||||||
...Input_Template_Node_Height,
|
|
||||||
value: height
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Update parentNode position
|
|
||||||
onNodesChange([
|
|
||||||
{
|
|
||||||
id: parentId,
|
|
||||||
type: 'position',
|
|
||||||
position: {
|
|
||||||
x: rect.x - 70,
|
|
||||||
y: rect.y - offsetHeight - 240
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* helper line */
|
/* helper line */
|
||||||
const [helperLineHorizontal, setHelperLineHorizontal] = useState<THelperLine>();
|
const [helperLineHorizontal, setHelperLineHorizontal] = useState<THelperLine>();
|
||||||
@@ -704,7 +648,7 @@ export const useWorkflow = () => {
|
|||||||
chatConfig: appDetail.chatConfig
|
chatConfig: appDetail.chatConfig
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[nodes, edges, appDetail.chatConfig, pushPastSnapshot],
|
[nodes, edges, appDetail.chatConfig],
|
||||||
{ wait: 500 }
|
{ wait: 500 }
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -721,7 +665,73 @@ export const useWorkflow = () => {
|
|||||||
helperLineVertical,
|
helperLineVertical,
|
||||||
onNodeDragStop,
|
onNodeDragStop,
|
||||||
onPaneContextMenu,
|
onPaneContextMenu,
|
||||||
onPaneClick,
|
onPaneClick
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLoopNode = () => {
|
||||||
|
const nodes = useContextSelector(WorkflowInitContext, (state) => state.nodes);
|
||||||
|
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (state) => state.onNodesChange);
|
||||||
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
|
|
||||||
|
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
|
||||||
|
const { childNodes, loopNode } = nodes.reduce(
|
||||||
|
(acc, node) => {
|
||||||
|
if (node.data.parentNodeId === parentId) {
|
||||||
|
acc.childNodes.push(node);
|
||||||
|
}
|
||||||
|
if (node.id === parentId) {
|
||||||
|
acc.loopNode = node;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ childNodes: [] as Node[], loopNode: undefined as Node<FlowNodeItemType> | undefined }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!loopNode) return;
|
||||||
|
|
||||||
|
const rect = getNodesBounds(childNodes);
|
||||||
|
// Calculate parent node size with minimum width/height constraints
|
||||||
|
const width = Math.max(rect.width + 80, 840);
|
||||||
|
const height = Math.max(rect.height + 80, 600);
|
||||||
|
|
||||||
|
const offsetHeight =
|
||||||
|
loopNode.data.inputs.find((input) => input.key === NodeInputKeyEnum.loopNodeInputHeight)
|
||||||
|
?.value ?? 83;
|
||||||
|
|
||||||
|
// Update parentNode size and position
|
||||||
|
onChangeNode({
|
||||||
|
nodeId: parentId,
|
||||||
|
type: 'updateInput',
|
||||||
|
key: NodeInputKeyEnum.nodeWidth,
|
||||||
|
value: {
|
||||||
|
...Input_Template_Node_Width,
|
||||||
|
value: width
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onChangeNode({
|
||||||
|
nodeId: parentId,
|
||||||
|
type: 'updateInput',
|
||||||
|
key: NodeInputKeyEnum.nodeHeight,
|
||||||
|
value: {
|
||||||
|
...Input_Template_Node_Height,
|
||||||
|
value: height
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Update parentNode position
|
||||||
|
onNodesChange([
|
||||||
|
{
|
||||||
|
id: parentId,
|
||||||
|
type: 'position',
|
||||||
|
position: {
|
||||||
|
x: rect.x - 70,
|
||||||
|
y: rect.y - offsetHeight - 240
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
resetParentNodeSizeAndPosition
|
resetParentNodeSizeAndPosition
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
|||||||
import { AppContext } from '../../../../context';
|
import { AppContext } from '../../../../context';
|
||||||
import { isValidArrayReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
import { isValidArrayReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { ReferenceArrayValueType } from '@fastgpt/global/core/workflow/type/io';
|
import { ReferenceArrayValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||||
import { useWorkflow } from '../../hooks/useWorkflow';
|
import { useLoopNode } from '../../hooks/useWorkflow';
|
||||||
import { useSize } from 'ahooks';
|
import { useSize } from 'ahooks';
|
||||||
|
|
||||||
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||||
@@ -41,7 +41,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||||
|
|
||||||
const { resetParentNodeSizeAndPosition } = useWorkflow();
|
const { resetParentNodeSizeAndPosition } = useLoopNode();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
nodeWidth,
|
nodeWidth,
|
||||||
@@ -50,8 +50,12 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
loopNodeInputHeight = Input_Template_LOOP_NODE_OFFSET
|
loopNodeInputHeight = Input_Template_LOOP_NODE_OFFSET
|
||||||
} = useMemo(() => {
|
} = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
nodeWidth: inputs.find((input) => input.key === NodeInputKeyEnum.nodeWidth)?.value,
|
nodeWidth: Number(
|
||||||
nodeHeight: inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value,
|
inputs.find((input) => input.key === NodeInputKeyEnum.nodeWidth)?.value?.toFixed(0)
|
||||||
|
),
|
||||||
|
nodeHeight: Number(
|
||||||
|
inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value?.toFixed(0)
|
||||||
|
),
|
||||||
loopInputArray: inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray),
|
loopInputArray: inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray),
|
||||||
loopNodeInputHeight: inputs.find(
|
loopNodeInputHeight: inputs.find(
|
||||||
(input) => input.key === NodeInputKeyEnum.loopNodeInputHeight
|
(input) => input.key === NodeInputKeyEnum.loopNodeInputHeight
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { postWorkflowDebug } from '@/web/core/workflow/api';
|
|||||||
import {
|
import {
|
||||||
checkWorkflowNodeAndConnection,
|
checkWorkflowNodeAndConnection,
|
||||||
compareSnapshot,
|
compareSnapshot,
|
||||||
simplifyWorkflowNodes,
|
|
||||||
storeEdgesRenderEdge,
|
storeEdgesRenderEdge,
|
||||||
storeNode2FlowNode
|
storeNode2FlowNode
|
||||||
} from '@/web/core/workflow/utils';
|
} from '@/web/core/workflow/utils';
|
||||||
@@ -832,11 +831,11 @@ const WorkflowContextProvider = ({
|
|||||||
const undo = useMemoizedFn(() => {
|
const undo = useMemoizedFn(() => {
|
||||||
if (past.length > 1) {
|
if (past.length > 1) {
|
||||||
forbiddenSaveSnapshot.current = true;
|
forbiddenSaveSnapshot.current = true;
|
||||||
|
// Current version is the first one, so we need to reset the second one
|
||||||
const firstPast = past[0];
|
const firstPast = past[1];
|
||||||
resetSnapshot(firstPast);
|
resetSnapshot(firstPast);
|
||||||
|
|
||||||
setFuture((future) => [firstPast, ...future]);
|
setFuture((future) => [past[0], ...future]);
|
||||||
setPast((past) => past.slice(1));
|
setPast((past) => past.slice(1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -937,10 +936,10 @@ const WorkflowContextProvider = ({
|
|||||||
if (isInit && past.length === 0) {
|
if (isInit && past.length === 0) {
|
||||||
setPast([
|
setPast([
|
||||||
{
|
{
|
||||||
|
nodes: nodes,
|
||||||
|
edges: edges,
|
||||||
title: t(`app:app.version_initial`),
|
title: t(`app:app.version_initial`),
|
||||||
isSaved: true,
|
isSaved: true,
|
||||||
nodes: simplifyWorkflowNodes(nodes),
|
|
||||||
edges,
|
|
||||||
chatConfig: e.chatConfig || appDetail.chatConfig
|
chatConfig: e.chatConfig || appDetail.chatConfig
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ import { TimerIdEnum } from '@fastgpt/service/common/system/timerLock/constants'
|
|||||||
import { addHours } from 'date-fns';
|
import { addHours } from 'date-fns';
|
||||||
import { getScheduleTriggerApp } from '@/service/core/app/utils';
|
import { getScheduleTriggerApp } from '@/service/core/app/utils';
|
||||||
|
|
||||||
|
// Try to run train every minute
|
||||||
const setTrainingQueueCron = () => {
|
const setTrainingQueueCron = () => {
|
||||||
setCron('*/1 * * * *', () => {
|
setCron('*/1 * * * *', () => {
|
||||||
startTrainingQueue();
|
startTrainingQueue();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Clear tmp upload files every ten minutes
|
||||||
const setClearTmpUploadFilesCron = () => {
|
const setClearTmpUploadFilesCron = () => {
|
||||||
// Clear tmp upload files every ten minutes
|
// Clear tmp upload files every ten minutes
|
||||||
setCron('*/10 * * * *', () => {
|
setCron('*/10 * * * *', () => {
|
||||||
@@ -61,6 +63,7 @@ const clearInvalidDataCron = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Run app timer trigger every hour
|
||||||
const scheduleTriggerAppCron = () => {
|
const scheduleTriggerAppCron = () => {
|
||||||
setCron('0 */1 * * *', async () => {
|
setCron('0 */1 * * *', async () => {
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { pushChatUsage } from '@/service/support/wallet/usage/push';
|
|||||||
import { defaultApp } from '@/web/core/app/constants';
|
import { defaultApp } from '@/web/core/app/constants';
|
||||||
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
|
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
|
||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
import { delay } from '@fastgpt/global/common/system/utils';
|
import { delay, retryFn } from '@fastgpt/global/common/system/utils';
|
||||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import {
|
import {
|
||||||
getWorkflowEntryNodeIds,
|
getWorkflowEntryNodeIds,
|
||||||
@@ -18,63 +18,72 @@ import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
|
|||||||
|
|
||||||
export const getScheduleTriggerApp = async () => {
|
export const getScheduleTriggerApp = async () => {
|
||||||
// 1. Find all the app
|
// 1. Find all the app
|
||||||
const apps = await MongoApp.find({
|
const apps = await retryFn(() => {
|
||||||
scheduledTriggerConfig: { $ne: null },
|
return MongoApp.find({
|
||||||
scheduledTriggerNextTime: { $lte: new Date() }
|
scheduledTriggerConfig: { $ne: null },
|
||||||
|
scheduledTriggerNextTime: { $lte: new Date() }
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Run apps
|
// 2. Run apps
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
apps.map(async (app) => {
|
apps.map(async (app) => {
|
||||||
if (!app.scheduledTriggerConfig) return;
|
|
||||||
// random delay 0 ~ 60s
|
|
||||||
await delay(Math.floor(Math.random() * 60 * 1000));
|
|
||||||
const { user } = await getUserChatInfoAndAuthTeamPoints(app.tmbId);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { flowUsages } = await dispatchWorkFlow({
|
if (!app.scheduledTriggerConfig) return;
|
||||||
chatId: getNanoid(),
|
// random delay 0 ~ 60s
|
||||||
user,
|
await delay(Math.floor(Math.random() * 60 * 1000));
|
||||||
mode: 'chat',
|
const { user } = await getUserChatInfoAndAuthTeamPoints(app.tmbId);
|
||||||
runningAppInfo: {
|
|
||||||
id: String(app._id),
|
await retryFn(async () => {
|
||||||
teamId: String(app.teamId),
|
if (!app.scheduledTriggerConfig) return;
|
||||||
tmbId: String(app.tmbId)
|
|
||||||
},
|
const { flowUsages } = await dispatchWorkFlow({
|
||||||
uid: String(app.tmbId),
|
chatId: getNanoid(),
|
||||||
runtimeNodes: storeNodes2RuntimeNodes(app.modules, getWorkflowEntryNodeIds(app.modules)),
|
user,
|
||||||
runtimeEdges: initWorkflowEdgeStatus(app.edges),
|
mode: 'chat',
|
||||||
variables: {},
|
runningAppInfo: {
|
||||||
query: [
|
id: String(app._id),
|
||||||
{
|
teamId: String(app.teamId),
|
||||||
type: ChatItemValueTypeEnum.text,
|
tmbId: String(app.tmbId)
|
||||||
text: {
|
},
|
||||||
content: app.scheduledTriggerConfig?.defaultPrompt
|
uid: String(app.tmbId),
|
||||||
|
runtimeNodes: storeNodes2RuntimeNodes(
|
||||||
|
app.modules,
|
||||||
|
getWorkflowEntryNodeIds(app.modules)
|
||||||
|
),
|
||||||
|
runtimeEdges: initWorkflowEdgeStatus(app.edges),
|
||||||
|
variables: {},
|
||||||
|
query: [
|
||||||
|
{
|
||||||
|
type: ChatItemValueTypeEnum.text,
|
||||||
|
text: {
|
||||||
|
content: app.scheduledTriggerConfig?.defaultPrompt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
],
|
chatConfig: defaultApp.chatConfig,
|
||||||
chatConfig: defaultApp.chatConfig,
|
histories: [],
|
||||||
histories: [],
|
stream: false,
|
||||||
stream: false,
|
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
||||||
maxRunTimes: WORKFLOW_MAX_RUN_TIMES
|
});
|
||||||
});
|
pushChatUsage({
|
||||||
pushChatUsage({
|
appName: app.name,
|
||||||
appName: app.name,
|
appId: app._id,
|
||||||
appId: app._id,
|
teamId: String(app.teamId),
|
||||||
teamId: String(app.teamId),
|
tmbId: String(app.tmbId),
|
||||||
tmbId: String(app.tmbId),
|
source: UsageSourceEnum.cronJob,
|
||||||
source: UsageSourceEnum.cronJob,
|
flowUsages
|
||||||
flowUsages
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update next time
|
||||||
|
app.scheduledTriggerNextTime = getNextTimeByCronStringAndTimezone(
|
||||||
|
app.scheduledTriggerConfig
|
||||||
|
);
|
||||||
|
await app.save();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addLog.error('Schedule trigger error', error);
|
addLog.warn('Schedule trigger error', { error });
|
||||||
}
|
}
|
||||||
|
|
||||||
// update next time
|
|
||||||
app.scheduledTriggerNextTime = getNextTimeByCronStringAndTimezone(app.scheduledTriggerConfig);
|
|
||||||
await app.save();
|
|
||||||
|
|
||||||
return;
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
|||||||
import { ParentIdType, ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
|
import { ParentIdType, ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
|
||||||
import { GetSystemPluginTemplatesBody } from '@/pages/api/core/app/plugin/getSystemPluginTemplates';
|
import { GetSystemPluginTemplatesBody } from '@/pages/api/core/app/plugin/getSystemPluginTemplates';
|
||||||
import { PluginGroupSchemaType } from '@fastgpt/service/core/app/plugin/type';
|
import { PluginGroupSchemaType } from '@fastgpt/service/core/app/plugin/type';
|
||||||
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
|
import { defaultGroup } from '@fastgpt/web/core/workflow/constants';
|
||||||
|
|
||||||
/* ============ team plugin ============== */
|
/* ============ team plugin ============== */
|
||||||
export const getTeamPlugTemplates = (data?: ListAppBody) =>
|
export const getTeamPlugTemplates = (data?: ListAppBody) =>
|
||||||
@@ -41,8 +43,11 @@ export const getTeamPlugTemplates = (data?: ListAppBody) =>
|
|||||||
export const getSystemPlugTemplates = (data: GetSystemPluginTemplatesBody) =>
|
export const getSystemPlugTemplates = (data: GetSystemPluginTemplatesBody) =>
|
||||||
POST<NodeTemplateListItemType[]>('/core/app/plugin/getSystemPluginTemplates', data);
|
POST<NodeTemplateListItemType[]>('/core/app/plugin/getSystemPluginTemplates', data);
|
||||||
|
|
||||||
export const getPluginGroups = () =>
|
export const getPluginGroups = () => {
|
||||||
GET<PluginGroupSchemaType[]>('/proApi/core/app/plugin/getPluginGroups');
|
return useSystemStore.getState()?.feConfigs?.isPlus
|
||||||
|
? GET<PluginGroupSchemaType[]>('/proApi/core/app/plugin/getPluginGroups')
|
||||||
|
: Promise.resolve([defaultGroup]);
|
||||||
|
};
|
||||||
|
|
||||||
export const getSystemPluginPaths = (parentId: ParentIdType) => {
|
export const getSystemPluginPaths = (parentId: ParentIdType) => {
|
||||||
if (!parentId) return Promise.resolve<ParentTreePathItemType[]>([]);
|
if (!parentId) return Promise.resolve<ParentTreePathItemType[]>([]);
|
||||||
|
|||||||
@@ -619,7 +619,6 @@ export const compareSnapshot = (
|
|||||||
return nodes
|
return nodes
|
||||||
.filter((node) => {
|
.filter((node) => {
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
if (FlowNodeTypeEnum.systemConfig === node.type) return;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
@@ -634,7 +633,8 @@ export const compareSnapshot = (
|
|||||||
key: input.key,
|
key: input.key,
|
||||||
selectedTypeIndex: input.selectedTypeIndex ?? 0,
|
selectedTypeIndex: input.selectedTypeIndex ?? 0,
|
||||||
renderTypeLis: input.renderTypeList,
|
renderTypeLis: input.renderTypeList,
|
||||||
valueType: input.valueType,
|
// set to arrayAny for loopInputArray to skip valueType comparison
|
||||||
|
// valueType: input.key === NodeInputKeyEnum.loopInputArray ? 'arrayAny' : input.valueType,
|
||||||
value: input.value ?? undefined
|
value: input.value ?? undefined
|
||||||
})),
|
})),
|
||||||
outputs: node.data.outputs.map((item: FlowNodeOutputItemType) => ({
|
outputs: node.data.outputs.map((item: FlowNodeOutputItemType) => ({
|
||||||
@@ -661,13 +661,3 @@ export const compareSnapshot = (
|
|||||||
|
|
||||||
return isEqual(node1, node2);
|
return isEqual(node1, node2);
|
||||||
};
|
};
|
||||||
|
|
||||||
// remove node size
|
|
||||||
export const simplifyWorkflowNodes = (nodes: Node[]) => {
|
|
||||||
return nodes.map((node) => ({
|
|
||||||
id: node.id,
|
|
||||||
type: node.type,
|
|
||||||
position: node.position,
|
|
||||||
data: node.data
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -72,20 +72,22 @@ export PROCESSES_PER_GPU="1"
|
|||||||
|
|
||||||
# 镜像打包和部署
|
# 镜像打包和部署
|
||||||
|
|
||||||
## 打包镜像
|
## 本地构建镜像
|
||||||
|
|
||||||
在 `pdf-marker` 根目录下执行:
|
1. 在 `pdf-marker` 根目录下执行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo docker build -t model_pdf -f Dockerfile .
|
sudo docker build -t model_pdf -f Dockerfile .
|
||||||
|
```
|
||||||
|
2. 运行容器
|
||||||
|
```bash
|
||||||
|
sudo docker run --gpus all -itd -p 7231:7231 --name model_pdf_v1 model_pdf
|
||||||
|
```
|
||||||
|
## 快速构建镜像
|
||||||
|
```dockerfile
|
||||||
|
docker pull crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:latest
|
||||||
|
docker run --gpus all -itd -p 7231:7231 --name model_pdf_v1 crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## 运行容器
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo docker run --gpus all -itd -p 7231:7231 --name model_pdf_v1 model_pdf
|
|
||||||
```
|
|
||||||
|
|
||||||
# 访问示例
|
# 访问示例
|
||||||
|
|
||||||
用Post方法访问端口为 `7321 ` 的 `v1/parse/file` 服务
|
用Post方法访问端口为 `7321 ` 的 `v1/parse/file` 服务
|
||||||
|
|||||||
Reference in New Issue
Block a user