mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-15 15:41:05 +00:00
4.8.10 workflow perf (#2596)
* perf: run plugin variables init * perf: init free plan * perf: dataset data ui * perf: workflow theme * perf: plugin input modal ui * perf: workflow dispatch * fix: account ui * feat: 4810 doc
This commit is contained in:
@@ -55,30 +55,32 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
|
|||||||
6. 新增 - 工作流版本支持重命名
|
6. 新增 - 工作流版本支持重命名
|
||||||
7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。
|
7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。
|
||||||
8. 新增 - 插件增加使用说明配置。
|
8. 新增 - 插件增加使用说明配置。
|
||||||
9. 商业版新增 - 飞书机器人接入
|
9. 新增 - 工作流导出导入,支持直接导出和导入 JSON 文件,便于交流。
|
||||||
10. 商业版新增 - 公众号接入接入
|
10. 商业版新增 - 飞书机器人接入
|
||||||
11. 商业版新增 - 自助开票申请
|
11. 商业版新增 - 公众号接入接入
|
||||||
12. 商业版新增 - SSO 定制
|
12. 商业版新增 - 自助开票申请
|
||||||
13. 优化 - SSE 响应优化。
|
13. 商业版新增 - SSO 定制
|
||||||
14. 优化 - 无 SSL 证书情况下,优化复制。
|
14. 优化 - 工作流循环校验,避免 skip 循环空转。同时支持分支完全并发执行。
|
||||||
15. 优化 - 单选框打开后自动滚动到选中的位置。
|
15. 优化 - SSE 响应优化。
|
||||||
16. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
|
16. 优化 - 无 SSL 证书情况下,优化复制。
|
||||||
17. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
|
17. 优化 - 单选框打开后自动滚动到选中的位置。
|
||||||
18. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
|
18. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
|
||||||
19. 优化 - 知识库列表 UI。
|
19. 优化 - 节点选择,避免切换 tab 时候,path 加载报错。
|
||||||
20. 优化 - 知识库详情页 UI。
|
20. 优化 - 最新 React Markdown 组件,支持 Base64 图片。
|
||||||
21. 优化 - 支持无网络配置情况下运行。
|
21. 优化 - 知识库列表 UI。
|
||||||
22. 优化 - 部分全局变量,增加数据类型约束。
|
22. 优化 - 知识库详情页 UI。
|
||||||
23. 修复 - 全局变量 key 可能重复。
|
23. 优化 - 支持无网络配置情况下运行。
|
||||||
24. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
|
24. 优化 - 部分全局变量,增加数据类型约束。
|
||||||
25. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
|
25. 修复 - 全局变量 key 可能重复。
|
||||||
26. 修复 - 选择 Milvus 部署时,无法导出知识库。
|
26. 修复 - Prompt 模式调用工具,stream=false 模式下,会携带 0: 开头标记。
|
||||||
27. 修复 - 创建 APP 副本,无法复制系统配置。
|
27. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
|
||||||
28. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
|
28. 修复 - 选择 Milvus 部署时,无法导出知识库。
|
||||||
29. 修复 - 内容提取的数据类型与输出数据类型未一致。
|
29. 修复 - 创建 APP 副本,无法复制系统配置。
|
||||||
30. 修复 - 工作流运行时间统计错误。
|
30. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
|
||||||
31. 修复 - stream 模式下,工具调用有可能出现 undefined
|
31. 修复 - 内容提取的数据类型与输出数据类型未一致。
|
||||||
32. 修复 - 全局变量在 API 中无法持久化。
|
32. 修复 - 工作流运行时间统计错误。
|
||||||
33. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
|
33. 修复 - stream 模式下,工具调用有可能出现 undefined
|
||||||
34. 修复 - 知识库标签重复加载。
|
34. 修复 - 全局变量在 API 中无法持久化。
|
||||||
35. 修复 - Debug 模式下,循环调用边问题。
|
35. 修复 - OpenAPI,detail=false模式下,不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
|
||||||
|
36. 修复 - 知识库标签重复加载。
|
||||||
|
37. 修复 - Debug 模式下,循环调用边问题。
|
||||||
|
@@ -26,8 +26,8 @@ export type ChatDispatchProps = {
|
|||||||
res?: NextApiResponse;
|
res?: NextApiResponse;
|
||||||
requestOrigin?: string;
|
requestOrigin?: string;
|
||||||
mode: 'test' | 'chat' | 'debug';
|
mode: 'test' | 'chat' | 'debug';
|
||||||
teamId: string;
|
teamId: string; // App teamId
|
||||||
tmbId: string;
|
tmbId: string; // App tmbId
|
||||||
user: UserModelSchema;
|
user: UserModelSchema;
|
||||||
app: AppDetailType | AppSchema;
|
app: AppDetailType | AppSchema;
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
|
@@ -63,7 +63,7 @@ import {
|
|||||||
InteractiveNodeResponseItemType,
|
InteractiveNodeResponseItemType,
|
||||||
UserSelectInteractive
|
UserSelectInteractive
|
||||||
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
||||||
import { dispatchRunAppNode } from './agent/runApp';
|
import { dispatchRunAppNode } from './plugin/runApp';
|
||||||
|
|
||||||
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||||
@@ -186,7 +186,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
function nodeOutput(
|
function nodeOutput(
|
||||||
node: RuntimeNodeItemType,
|
node: RuntimeNodeItemType,
|
||||||
result: Record<string, any> = {}
|
result: Record<string, any> = {}
|
||||||
): RuntimeNodeItemType[] {
|
): {
|
||||||
|
nextStepActiveNodes: RuntimeNodeItemType[];
|
||||||
|
nextStepSkipNodes: RuntimeNodeItemType[];
|
||||||
|
} {
|
||||||
pushStore(node, result);
|
pushStore(node, result);
|
||||||
|
|
||||||
// Assign the output value to the next node
|
// Assign the output value to the next node
|
||||||
@@ -211,16 +214,32 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const nextStepNodes = runtimeNodes.filter((node) => {
|
const nextStepActiveNodes: RuntimeNodeItemType[] = [];
|
||||||
return targetEdges.some((item) => item.target === node.nodeId);
|
const nextStepSkipNodes: RuntimeNodeItemType[] = [];
|
||||||
|
runtimeNodes.forEach((node) => {
|
||||||
|
if (targetEdges.some((item) => item.target === node.nodeId && item.status === 'active')) {
|
||||||
|
nextStepActiveNodes.push(node);
|
||||||
|
}
|
||||||
|
if (targetEdges.some((item) => item.target === node.nodeId && item.status === 'skipped')) {
|
||||||
|
nextStepSkipNodes.push(node);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (props.mode === 'debug') {
|
if (props.mode === 'debug') {
|
||||||
debugNextStepRunNodes = debugNextStepRunNodes.concat(nextStepNodes);
|
debugNextStepRunNodes = debugNextStepRunNodes.concat([
|
||||||
return [];
|
...nextStepActiveNodes,
|
||||||
|
...nextStepSkipNodes
|
||||||
|
]);
|
||||||
|
return {
|
||||||
|
nextStepActiveNodes: [],
|
||||||
|
nextStepSkipNodes: []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return nextStepNodes;
|
return {
|
||||||
|
nextStepActiveNodes,
|
||||||
|
nextStepSkipNodes
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Have interactive result, computed edges and node outputs */
|
/* Have interactive result, computed edges and node outputs */
|
||||||
@@ -281,69 +300,82 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
/* Check node run/skip or wait */
|
/* Check node run/skip or wait */
|
||||||
function checkNodeCanRun(nodes: RuntimeNodeItemType[] = []): Promise<any> {
|
async function checkNodeCanRun(
|
||||||
return Promise.all(
|
node: RuntimeNodeItemType,
|
||||||
nodes.map(async (node) => {
|
skippedNodeIdList = new Set<string>()
|
||||||
const status = checkNodeRunStatus({
|
): Promise<RuntimeNodeItemType[]> {
|
||||||
node,
|
if (res?.closed || props.maxRunTimes <= 0) return [];
|
||||||
runtimeEdges
|
// Thread avoidance
|
||||||
});
|
await surrenderProcess();
|
||||||
|
|
||||||
if (res?.closed || props.maxRunTimes <= 0) return;
|
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id });
|
||||||
|
|
||||||
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id });
|
// Get node run status by edges
|
||||||
|
const status = checkNodeRunStatus({
|
||||||
// Thread avoidance
|
node,
|
||||||
await surrenderProcess();
|
runtimeEdges
|
||||||
|
|
||||||
if (status === 'run') {
|
|
||||||
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
|
|
||||||
return nodeRunWithActive(node);
|
|
||||||
}
|
|
||||||
if (status === 'skip') {
|
|
||||||
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
|
|
||||||
return nodeRunWithSkip(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
})
|
|
||||||
).then((result) => {
|
|
||||||
props.maxRunTimes--;
|
|
||||||
|
|
||||||
const flat = result.flat().filter(Boolean) as unknown as {
|
|
||||||
node: RuntimeNodeItemType;
|
|
||||||
runStatus: 'run' | 'skip';
|
|
||||||
result: Record<string, any>;
|
|
||||||
}[];
|
|
||||||
// If there are no running nodes, the workflow is complete
|
|
||||||
if (flat.length === 0) return;
|
|
||||||
|
|
||||||
// Update the node output at the end of the run and get the next nodes
|
|
||||||
const nextNodes = flat.map((item) => nodeOutput(item.node, item.result)).flat();
|
|
||||||
// Remove repeat nodes(Make sure that the node is only executed once)
|
|
||||||
const filterNextNodes = nextNodes.filter(
|
|
||||||
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
|
|
||||||
);
|
|
||||||
|
|
||||||
// In the current version, only one interactive node is allowed at the same time
|
|
||||||
const haveInteractiveResponse = flat
|
|
||||||
.map((response) => {
|
|
||||||
const interactiveResponse = response.result?.[DispatchNodeResponseKeyEnum.interactive];
|
|
||||||
if (interactiveResponse) {
|
|
||||||
chatAssistantResponse.push(
|
|
||||||
handleInteractiveResult({
|
|
||||||
entryNodeIds: [response.node.nodeId],
|
|
||||||
interactiveResponse
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
if (haveInteractiveResponse.length > 0) return;
|
|
||||||
|
|
||||||
return checkNodeCanRun(filterNextNodes);
|
|
||||||
});
|
});
|
||||||
|
const nodeRunResult = await (() => {
|
||||||
|
if (status === 'run') {
|
||||||
|
props.maxRunTimes--;
|
||||||
|
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
|
||||||
|
return nodeRunWithActive(node);
|
||||||
|
}
|
||||||
|
if (status === 'skip' && !skippedNodeIdList.has(node.nodeId)) {
|
||||||
|
props.maxRunTimes -= 0.1;
|
||||||
|
skippedNodeIdList.add(node.nodeId);
|
||||||
|
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
|
||||||
|
return nodeRunWithSkip(node);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (!nodeRunResult) return [];
|
||||||
|
|
||||||
|
// Update the node output at the end of the run and get the next nodes
|
||||||
|
let { nextStepActiveNodes, nextStepSkipNodes } = nodeOutput(
|
||||||
|
nodeRunResult.node,
|
||||||
|
nodeRunResult.result
|
||||||
|
);
|
||||||
|
// Remove repeat nodes(Make sure that the node is only executed once)
|
||||||
|
nextStepActiveNodes = nextStepActiveNodes.filter(
|
||||||
|
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
|
||||||
|
);
|
||||||
|
nextStepSkipNodes = nextStepSkipNodes.filter(
|
||||||
|
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
|
||||||
|
);
|
||||||
|
|
||||||
|
// In the current version, only one interactive node is allowed at the same time
|
||||||
|
const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
|
||||||
|
if (interactiveResponse) {
|
||||||
|
chatAssistantResponse.push(
|
||||||
|
handleInteractiveResult({
|
||||||
|
entryNodeIds: [nodeRunResult.node.nodeId],
|
||||||
|
interactiveResponse
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run next nodes(先运行 run 的,再运行 skip 的)
|
||||||
|
const nextStepActiveNodesResults = (
|
||||||
|
await Promise.all(nextStepActiveNodes.map((node) => checkNodeCanRun(node)))
|
||||||
|
).flat();
|
||||||
|
|
||||||
|
// 如果已经 active 运行过,不再执行 skip(active 中有闭环)
|
||||||
|
nextStepSkipNodes = nextStepSkipNodes.filter(
|
||||||
|
(node) => !nextStepActiveNodesResults.some((item) => item.nodeId === node.nodeId)
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextStepSkipNodesResults = (
|
||||||
|
await Promise.all(nextStepSkipNodes.map((node) => checkNodeCanRun(node, skippedNodeIdList)))
|
||||||
|
).flat();
|
||||||
|
|
||||||
|
return [
|
||||||
|
...nextStepActiveNodes,
|
||||||
|
...nextStepSkipNodes,
|
||||||
|
...nextStepActiveNodesResults,
|
||||||
|
...nextStepSkipNodesResults
|
||||||
|
];
|
||||||
}
|
}
|
||||||
/* Inject data into module input */
|
/* Inject data into module input */
|
||||||
function getNodeRunParams(node: RuntimeNodeItemType) {
|
function getNodeRunParams(node: RuntimeNodeItemType) {
|
||||||
@@ -396,7 +428,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
async function nodeRunWithActive(node: RuntimeNodeItemType) {
|
async function nodeRunWithActive(node: RuntimeNodeItemType): Promise<{
|
||||||
|
node: RuntimeNodeItemType;
|
||||||
|
runStatus: 'run';
|
||||||
|
result: Record<string, any>;
|
||||||
|
}> {
|
||||||
// push run status messages
|
// push run status messages
|
||||||
if (node.showStatus) {
|
if (node.showStatus) {
|
||||||
props.workflowStreamResponse?.({
|
props.workflowStreamResponse?.({
|
||||||
@@ -465,8 +501,12 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
async function nodeRunWithSkip(node: RuntimeNodeItemType) {
|
async function nodeRunWithSkip(node: RuntimeNodeItemType): Promise<{
|
||||||
// 其后所有target的节点,都设置为skip
|
node: RuntimeNodeItemType;
|
||||||
|
runStatus: 'skip';
|
||||||
|
result: Record<string, any>;
|
||||||
|
}> {
|
||||||
|
// Set target edges status to skipped
|
||||||
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
|
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
|
||||||
nodeRunAfterHook(node);
|
nodeRunAfterHook(node);
|
||||||
|
|
||||||
@@ -486,7 +526,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
// runtimeNodes.forEach((item) => {
|
// runtimeNodes.forEach((item) => {
|
||||||
// item.isEntry = false;
|
// item.isEntry = false;
|
||||||
// });
|
// });
|
||||||
await checkNodeCanRun(entryNodes);
|
await Promise.all(entryNodes.map((node) => checkNodeCanRun(node)));
|
||||||
|
|
||||||
// focus try to run pluginOutput
|
// focus try to run pluginOutput
|
||||||
const pluginOutputModule = runtimeNodes.find(
|
const pluginOutputModule = runtimeNodes.find(
|
||||||
|
@@ -64,7 +64,11 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||||||
|
|
||||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||||
...props,
|
...props,
|
||||||
variables: filterSystemVariables(props.variables),
|
|
||||||
|
variables: {
|
||||||
|
...filterSystemVariables(props.variables),
|
||||||
|
appId: String(plugin.id)
|
||||||
|
},
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
|
runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
|
||||||
});
|
});
|
||||||
|
@@ -50,7 +50,7 @@ export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initTeamStandardPlan2Free = async ({
|
export const initTeamFreePlan = async ({
|
||||||
teamId,
|
teamId,
|
||||||
session
|
session
|
||||||
}: {
|
}: {
|
||||||
@@ -59,23 +59,28 @@ export const initTeamStandardPlan2Free = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
const freePoints = global?.subPlans?.standard?.[StandardSubLevelEnum.free]?.totalPoints || 100;
|
const freePoints = global?.subPlans?.standard?.[StandardSubLevelEnum.free]?.totalPoints || 100;
|
||||||
|
|
||||||
const teamStandardSub = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard });
|
const freePlan = await MongoTeamSub.findOne({
|
||||||
|
teamId,
|
||||||
|
type: SubTypeEnum.standard,
|
||||||
|
currentSubLevel: StandardSubLevelEnum.free
|
||||||
|
});
|
||||||
|
|
||||||
if (teamStandardSub) {
|
// Reset one month free plan
|
||||||
teamStandardSub.currentMode = SubModeEnum.month;
|
if (freePlan) {
|
||||||
teamStandardSub.nextMode = SubModeEnum.month;
|
freePlan.currentMode = SubModeEnum.month;
|
||||||
teamStandardSub.startTime = new Date();
|
freePlan.nextMode = SubModeEnum.month;
|
||||||
teamStandardSub.expiredTime = addMonths(new Date(), 1);
|
freePlan.startTime = new Date();
|
||||||
|
freePlan.expiredTime = addMonths(new Date(), 1);
|
||||||
|
|
||||||
teamStandardSub.currentSubLevel = StandardSubLevelEnum.free;
|
freePlan.currentSubLevel = StandardSubLevelEnum.free;
|
||||||
teamStandardSub.nextSubLevel = StandardSubLevelEnum.free;
|
freePlan.nextSubLevel = StandardSubLevelEnum.free;
|
||||||
|
|
||||||
teamStandardSub.totalPoints = freePoints;
|
freePlan.totalPoints = freePoints;
|
||||||
teamStandardSub.surplusPoints =
|
freePlan.surplusPoints =
|
||||||
teamStandardSub.surplusPoints && teamStandardSub.surplusPoints < 0
|
freePlan.surplusPoints && freePlan.surplusPoints < 0
|
||||||
? teamStandardSub.surplusPoints + freePoints
|
? freePlan.surplusPoints + freePoints
|
||||||
: freePoints;
|
: freePoints;
|
||||||
return teamStandardSub.save({ session });
|
return freePlan.save({ session });
|
||||||
}
|
}
|
||||||
|
|
||||||
return MongoTeamSub.create(
|
return MongoTeamSub.create(
|
||||||
@@ -123,13 +128,14 @@ export const getTeamPlanStatus = async ({
|
|||||||
|
|
||||||
// Free user, first login after expiration. The free subscription plan will be reset
|
// Free user, first login after expiration. The free subscription plan will be reset
|
||||||
if (
|
if (
|
||||||
standardPlan &&
|
(standardPlan &&
|
||||||
standardPlan.expiredTime &&
|
standardPlan.expiredTime &&
|
||||||
standardPlan.currentSubLevel === StandardSubLevelEnum.free &&
|
standardPlan.currentSubLevel === StandardSubLevelEnum.free &&
|
||||||
dayjs(standardPlan.expiredTime).isBefore(new Date())
|
dayjs(standardPlan.expiredTime).isBefore(new Date())) ||
|
||||||
|
teamStandardPlans.length === 0
|
||||||
) {
|
) {
|
||||||
console.log('Init free stand plan', { teamId });
|
console.log('Init free stand plan', { teamId });
|
||||||
await initTeamStandardPlan2Free({ teamId });
|
await initTeamFreePlan({ teamId });
|
||||||
return getTeamPlanStatus({ teamId });
|
return getTeamPlanStatus({ teamId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,7 +33,7 @@ export type Props = {
|
|||||||
icon?: IconNameType | string;
|
icon?: IconNameType | string;
|
||||||
label: string | React.ReactNode;
|
label: string | React.ReactNode;
|
||||||
description?: string;
|
description?: string;
|
||||||
onClick: () => any;
|
onClick?: () => any;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
@@ -170,8 +170,10 @@ const MyMenu = ({
|
|||||||
{...menuItemStyles}
|
{...menuItemStyles}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsOpen(false);
|
if (child.onClick) {
|
||||||
child.onClick && child.onClick();
|
setIsOpen(false);
|
||||||
|
child.onClick();
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
color={child.isActive ? 'primary.700' : 'myGray.600'}
|
color={child.isActive ? 'primary.700' : 'myGray.600'}
|
||||||
whiteSpace={'pre-wrap'}
|
whiteSpace={'pre-wrap'}
|
||||||
|
@@ -105,7 +105,7 @@ const MultipleRowSelect = ({
|
|||||||
justifyContent={'space-between'}
|
justifyContent={'space-between'}
|
||||||
width={'100%'}
|
width={'100%'}
|
||||||
rightIcon={<ChevronDownIcon />}
|
rightIcon={<ChevronDownIcon />}
|
||||||
variant={'whiteFlow'}
|
variant={'whiteBase'}
|
||||||
_active={{
|
_active={{
|
||||||
transform: 'none'
|
transform: 'none'
|
||||||
}}
|
}}
|
||||||
|
@@ -194,28 +194,6 @@ const Button = defineStyleConfig({
|
|||||||
color: 'myGray.600 !important'
|
color: 'myGray.600 !important'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
whiteFlow: {
|
|
||||||
color: 'myGray.600',
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor: 'myGray.200',
|
|
||||||
height: '40px',
|
|
||||||
bg: 'white',
|
|
||||||
px: '12px',
|
|
||||||
py: '0',
|
|
||||||
borderRadius: '6px',
|
|
||||||
transition: 'background 0.1s',
|
|
||||||
_hover: {
|
|
||||||
color: 'primary.600',
|
|
||||||
background: 'primary.1',
|
|
||||||
borderColor: 'primary.300'
|
|
||||||
},
|
|
||||||
_active: {
|
|
||||||
color: 'primary.600'
|
|
||||||
},
|
|
||||||
_disabled: {
|
|
||||||
color: 'myGray.600 !important'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
whiteDanger: {
|
whiteDanger: {
|
||||||
color: 'myGray.600',
|
color: 'myGray.600',
|
||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
|
@@ -68,7 +68,7 @@ const SettingLLMModel = ({
|
|||||||
<Button
|
<Button
|
||||||
w={'100%'}
|
w={'100%'}
|
||||||
justifyContent={'flex-start'}
|
justifyContent={'flex-start'}
|
||||||
variant={'whiteFlow'}
|
variant={'whiteBase'}
|
||||||
bg={bg}
|
bg={bg}
|
||||||
_active={{
|
_active={{
|
||||||
transform: 'none'
|
transform: 'none'
|
||||||
|
@@ -67,9 +67,9 @@ const Account = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box py={[3, '28px']} maxW={['95vw', '1080px']} px={[5, 10]} mx={'auto'}>
|
<Box py={[3, '28px']} px={[5, 10]} mx={'auto'}>
|
||||||
{isPc ? (
|
{isPc ? (
|
||||||
<Flex justifyContent={'center'}>
|
<Flex justifyContent={'center'} maxW={'1080px'}>
|
||||||
<Box flex={'0 0 330px'}>
|
<Box flex={'0 0 330px'}>
|
||||||
<MyInfo onOpenContact={onOpenContact} />
|
<MyInfo onOpenContact={onOpenContact} />
|
||||||
<Box mt={9}>
|
<Box mt={9}>
|
||||||
@@ -77,7 +77,7 @@ const Account = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{!!standardPlan && (
|
{!!standardPlan && (
|
||||||
<Box ml={'45px'} flex={'1 0 0'} maxW={'600px'}>
|
<Box ml={'45px'} flex={'1'} maxW={'600px'}>
|
||||||
<PlanUsage />
|
<PlanUsage />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -437,7 +437,7 @@ const PlanUsage = () => {
|
|||||||
borderColor={'borderColor.low'}
|
borderColor={'borderColor.low'}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
>
|
>
|
||||||
<Flex px={[5, 7]} py={[3, 6]} whiteSpace={'nowrap'}>
|
<Flex px={[5, 7]} pt={[3, 6]}>
|
||||||
<Box flex={'1 0 0'}>
|
<Box flex={'1 0 0'}>
|
||||||
<Box color={'myGray.600'} fontSize="sm">
|
<Box color={'myGray.600'} fontSize="sm">
|
||||||
{t('common:support.wallet.subscription.Current plan')}
|
{t('common:support.wallet.subscription.Current plan')}
|
||||||
@@ -445,24 +445,26 @@ const PlanUsage = () => {
|
|||||||
<Box fontWeight={'bold'} fontSize="lg">
|
<Box fontWeight={'bold'} fontSize="lg">
|
||||||
{t(planName as any)}
|
{t(planName as any)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{isFreeTeam ? (
|
|
||||||
<>
|
|
||||||
<Box mt="2" color={'#485264'} fontSize="sm">
|
|
||||||
{t('common:info.free_plan')}
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Flex mt="2" color={'#485264'} fontSize="xs">
|
|
||||||
<Box>{t('common:support.wallet.Plan expired time')}:</Box>
|
|
||||||
<Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
<Button onClick={() => router.push('/price')} w={'8rem'} size="sm">
|
<Button onClick={() => router.push('/price')} w={'8rem'} size="sm">
|
||||||
{t('common:support.wallet.subscription.Upgrade plan')}
|
{t('common:support.wallet.subscription.Upgrade plan')}
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Box px={[5, 7]} pb={[3, 6]}>
|
||||||
|
{isFreeTeam ? (
|
||||||
|
<>
|
||||||
|
<Box mt="2" color={'#485264'} fontSize="sm">
|
||||||
|
{t('common:info.free_plan')}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Flex mt="2" color={'#485264'} fontSize="xs">
|
||||||
|
<Box>{t('common:support.wallet.Plan expired time')}:</Box>
|
||||||
|
<Box ml={2}>{formatTime2YMD(standardPlan?.expiredTime)}</Box>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Box py={3} borderTopWidth={'1px'} borderTopColor={'borderColor.base'}>
|
<Box py={3} borderTopWidth={'1px'} borderTopColor={'borderColor.base'}>
|
||||||
<Box py={[0, 3]} px={[5, 7]} overflow={'auto'}>
|
<Box py={[0, 3]} px={[5, 7]} overflow={'auto'}>
|
||||||
<StandardPlanContentList
|
<StandardPlanContentList
|
||||||
@@ -479,7 +481,7 @@ const PlanUsage = () => {
|
|||||||
borderColor={'borderColor.low'}
|
borderColor={'borderColor.low'}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
px={[5, 10]}
|
px={[5, 10]}
|
||||||
pt={[2, 4]}
|
pt={4}
|
||||||
pb={[4, 7]}
|
pb={[4, 7]}
|
||||||
>
|
>
|
||||||
<Flex>
|
<Flex>
|
||||||
|
@@ -180,12 +180,11 @@ const UsageTable = () => {
|
|||||||
))}
|
))}
|
||||||
</Tbody>
|
</Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
|
{!isLoading && usages.length === 0 && (
|
||||||
|
<EmptyTip text={t('common:user.no_usage_records')}></EmptyTip>
|
||||||
|
)}
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
|
||||||
{!isLoading && usages.length === 0 && (
|
|
||||||
<EmptyTip text={t('common:user.no_usage_records')}></EmptyTip>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Loading loading={isLoading} fixed={false} />
|
<Loading loading={isLoading} fixed={false} />
|
||||||
{!!usageDetail && (
|
{!!usageDetail && (
|
||||||
<UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} />
|
<UsageDetail usage={usageDetail} onClose={() => setUsageDetail(undefined)} />
|
||||||
|
@@ -125,7 +125,7 @@ const Header = () => {
|
|||||||
try {
|
try {
|
||||||
localStorage.removeItem(`${appDetail._id}-past`);
|
localStorage.removeItem(`${appDetail._id}-past`);
|
||||||
localStorage.removeItem(`${appDetail._id}-future`);
|
localStorage.removeItem(`${appDetail._id}-future`);
|
||||||
router.push('/app/list');
|
router.back();
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}, [appDetail._id, router]);
|
}, [appDetail._id, router]);
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ const RouteTab = () => {
|
|||||||
|
|
||||||
const setCurrentTab = useCallback(
|
const setCurrentTab = useCallback(
|
||||||
(tab: TabEnum) => {
|
(tab: TabEnum) => {
|
||||||
router.push({
|
router.replace({
|
||||||
query: {
|
query: {
|
||||||
...router.query,
|
...router.query,
|
||||||
currentTab: tab
|
currentTab: tab
|
||||||
@@ -41,7 +41,7 @@ const RouteTab = () => {
|
|||||||
]
|
]
|
||||||
: [])
|
: [])
|
||||||
],
|
],
|
||||||
[appDetail.permission.hasManagePer, appT]
|
[appDetail.permission.hasManagePer, appDetail.type, appT]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -125,7 +125,7 @@ const Header = () => {
|
|||||||
try {
|
try {
|
||||||
localStorage.removeItem(`${appDetail._id}-past`);
|
localStorage.removeItem(`${appDetail._id}-past`);
|
||||||
localStorage.removeItem(`${appDetail._id}-future`);
|
localStorage.removeItem(`${appDetail._id}-future`);
|
||||||
router.push('/app/list');
|
router.back();
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}, [appDetail._id, router]);
|
}, [appDetail._id, router]);
|
||||||
|
|
||||||
|
@@ -74,8 +74,7 @@ const AppCard = ({
|
|||||||
label: ExportPopover({
|
label: ExportPopover({
|
||||||
chatConfig: appDetail.chatConfig,
|
chatConfig: appDetail.chatConfig,
|
||||||
appName: appDetail.name
|
appName: appDetail.name
|
||||||
}),
|
})
|
||||||
onClick: () => {}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -192,7 +191,7 @@ function ExportPopover({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
const { flowData2StoreDataAndCheck } = useContextSelector(WorkflowContext, (v) => v);
|
const { flowData2StoreDataAndCheck } = useContextSelector(WorkflowContext, (v) => v);
|
||||||
const data = flowData2StoreDataAndCheck();
|
|
||||||
const onExportWorkflow = useCallback(async () => {
|
const onExportWorkflow = useCallback(async () => {
|
||||||
const data = flowData2StoreDataAndCheck();
|
const data = flowData2StoreDataAndCheck();
|
||||||
if (data) {
|
if (data) {
|
||||||
@@ -251,7 +250,10 @@ function ExportPopover({
|
|||||||
}}
|
}}
|
||||||
borderRadius={'xs'}
|
borderRadius={'xs'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
const data = flowData2StoreDataAndCheck();
|
||||||
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
fileDownload({
|
fileDownload({
|
||||||
text: JSON.stringify(
|
text: JSON.stringify(
|
||||||
{
|
{
|
||||||
|
@@ -312,6 +312,7 @@ const FieldEditModal = ({
|
|||||||
title={isEdit ? t('workflow:edit_input') : t('workflow:add_new_input')}
|
title={isEdit ? t('workflow:edit_input') : t('workflow:add_new_input')}
|
||||||
maxW={['90vw', '1028px']}
|
maxW={['90vw', '1028px']}
|
||||||
w={'100%'}
|
w={'100%'}
|
||||||
|
isCentered
|
||||||
>
|
>
|
||||||
<Flex h={'560px'}>
|
<Flex h={'560px'}>
|
||||||
<Stack gap={4} p={8}>
|
<Stack gap={4} p={8}>
|
||||||
|
@@ -50,7 +50,7 @@ const SelectAppRender = ({ item, nodeId }: RenderInputProps) => {
|
|||||||
<>
|
<>
|
||||||
<Box onClick={onOpenSelectApp}>
|
<Box onClick={onOpenSelectApp}>
|
||||||
{!value ? (
|
{!value ? (
|
||||||
<Button variant={'whiteFlow'} w={'100%'}>
|
<Button variant={'whiteBase'} w={'100%'}>
|
||||||
{t('common:core.module.Select app')}
|
{t('common:core.module.Select app')}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
@@ -58,7 +58,7 @@ const SelectAppRender = ({ item, nodeId }: RenderInputProps) => {
|
|||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
w={'100%'}
|
w={'100%'}
|
||||||
justifyContent={loading ? 'center' : 'flex-start'}
|
justifyContent={loading ? 'center' : 'flex-start'}
|
||||||
variant={'whiteFlow'}
|
variant={'whiteBase'}
|
||||||
leftIcon={<Avatar src={appDetail?.avatar} w={6} />}
|
leftIcon={<Avatar src={appDetail?.avatar} w={6} />}
|
||||||
>
|
>
|
||||||
{appDetail?.name}
|
{appDetail?.name}
|
||||||
|
@@ -179,7 +179,7 @@ const DataCard = () => {
|
|||||||
<Flex align={'center'} color={'myGray.500'}>
|
<Flex align={'center'} color={'myGray.500'}>
|
||||||
<MyIcon name="common/list" mr={2} w={'18px'} />
|
<MyIcon name="common/list" mr={2} w={'18px'} />
|
||||||
<Box as={'span'} fontSize={['sm', '14px']} fontWeight={'500'}>
|
<Box as={'span'} fontSize={['sm', '14px']} fontWeight={'500'}>
|
||||||
{t('core.dataset.data.Total Amount', { total })}
|
{t('common:core.dataset.data.Total Amount', { total })}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box flex={1} mr={1} />
|
<Box flex={1} mr={1} />
|
||||||
@@ -204,7 +204,7 @@ const DataCard = () => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
{/* data */}
|
{/* data */}
|
||||||
<Box flex={'1 0 0'} overflow={'auto'} px={5}>
|
<Box flex={'1 0 0'} overflow={'auto'} px={5} pb={5}>
|
||||||
<Flex flexDir={'column'} gap={2}>
|
<Flex flexDir={'column'} gap={2}>
|
||||||
{datasetDataList.map((item, index) => (
|
{datasetDataList.map((item, index) => (
|
||||||
<Card
|
<Card
|
||||||
|
Reference in New Issue
Block a user