Commit 9959707f by Archer Committed by GitHub

V4.14.9 fix issue (#6573)

* fix: session error

* fix: session error

* fix: workflow runtime and add e2b
parent f057a2ae
...@@ -5,17 +5,19 @@ description: 'FastGPT V4.14.9 更新说明' ...@@ -5,17 +5,19 @@ description: 'FastGPT V4.14.9 更新说明'
### 环境变量更新 ### 环境变量更新
1. 调整 FastGPT 环境变量:CODE_SANDBOX_URL 和 SANDBOX_TOKEN,改名成 CODE_SANDBOX_URL 和 CODE_SANDBOX_TOKEN:
```bash ```bash
# 调整 FastGPT 环境变量:CODE_SANDBOX_URL 和 SANDBOX_TOKEN,改名成 CODE_SANDBOX_URL 和 CODE_SANDBOX_TOKEN
SANDBOX_URL=代码运行沙盒的地址 SANDBOX_URL=代码运行沙盒的地址
SANDBOX_TOKEN=代码运行沙盒的凭证(可以为空,4.14.8 新增加了鉴权) SANDBOX_TOKEN=代码运行沙盒的凭证(可以为空,4.14.8 新增加了鉴权)
# 新增 Agent sandbox 沙盒环境变量 # 新增 Agent sandbox 沙盒环境变量
AGENT_SANDBOX_PROVIDER= AGENT_SANDBOX_PROVIDER=
AGENT_SANDBOX_SEALOS_BASEURL= AGENT_SANDBOX_SEALOS_BASEURL=
AGENT_SANDBOX_SEALOS_TOKEN= AGENT_SANDBOX_SEALOS_TOKEN=
``` ```
2. 默认开启了内网安全检查,如需关闭,需设置环境变量`CHECK_INTERNAL_IP=false`
## 接口变更 ## 接口变更
`/api/core/chat/getPaginationRecords` 接口,增加返回`useAgentSandbox:boolean`字段,代表本轮对话,是否使用了虚拟机工具。即将移除`llmModuleAccount`和`historyPreviewLength`字段,如使用该字段,请尽快适配。 `/api/core/chat/getPaginationRecords` 接口,增加返回`useAgentSandbox:boolean`字段,代表本轮对话,是否使用了虚拟机工具。即将移除`llmModuleAccount`和`historyPreviewLength`字段,如使用该字段,请尽快适配。
...@@ -34,6 +36,7 @@ AGENT_SANDBOX_SEALOS_TOKEN= ...@@ -34,6 +36,7 @@ AGENT_SANDBOX_SEALOS_TOKEN=
2. HTTP 工具,增加 SSRF 防御。 2. HTTP 工具,增加 SSRF 防御。
3. 兼容更多 MCP JsonSchema 字段。 3. 兼容更多 MCP JsonSchema 字段。
4. 优化部分工作流运行池逻辑,减少计算复杂度 4. 优化部分工作流运行池逻辑,减少计算复杂度
5. 调整工作流 runtime,用 Tarjan SCC 算法替代 DSC 进行 edges 分组,解决工作流复杂循环无法运行问题。
## 🐛 修复 ## 🐛 修复
...@@ -46,3 +49,4 @@ AGENT_SANDBOX_SEALOS_TOKEN= ...@@ -46,3 +49,4 @@ AGENT_SANDBOX_SEALOS_TOKEN=
7. 分享链接关闭状态显示后,会导致历史记录里的 AI 回复内容无法正常展示。 7. 分享链接关闭状态显示后,会导致历史记录里的 AI 回复内容无法正常展示。
8. 修复工作流预览模式下,重新打开预览弹窗,会丢失表单输入内容。 8. 修复工作流预览模式下,重新打开预览弹窗,会丢失表单输入内容。
9. 修复订阅套餐自定义字段未生效 9. 修复订阅套餐自定义字段未生效
10. login接口,存在异步 session 问题,会出现报错日志。
...@@ -80,10 +80,10 @@ ...@@ -80,10 +80,10 @@
"document/content/docs/introduction/guide/dashboard/workflow/question_classify.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/guide/dashboard/workflow/question_classify.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/reply.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/introduction/guide/dashboard/workflow/reply.en.mdx": "2026-02-26T22:14:30+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/reply.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/guide/dashboard/workflow/reply.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/sandbox-v2.en.mdx": "2026-03-11T15:10:01+08:00", "document/content/docs/introduction/guide/dashboard/workflow/sandbox-v2.en.mdx": "2026-03-16T17:09:25+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/sandbox-v2.mdx": "2026-03-11T15:10:01+08:00", "document/content/docs/introduction/guide/dashboard/workflow/sandbox-v2.mdx": "2026-03-16T17:09:25+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/sandbox.en.mdx": "2026-03-11T15:10:01+08:00", "document/content/docs/introduction/guide/dashboard/workflow/sandbox.en.mdx": "2026-03-16T17:09:25+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/sandbox.mdx": "2026-03-11T15:10:01+08:00", "document/content/docs/introduction/guide/dashboard/workflow/sandbox.mdx": "2026-03-16T17:09:25+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/text_editor.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/introduction/guide/dashboard/workflow/text_editor.en.mdx": "2026-02-26T22:14:30+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/text_editor.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/guide/dashboard/workflow/text_editor.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/guide/dashboard/workflow/tfswitch.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/introduction/guide/dashboard/workflow/tfswitch.en.mdx": "2026-02-26T22:14:30+08:00",
...@@ -138,8 +138,8 @@ ...@@ -138,8 +138,8 @@
"document/content/docs/introduction/opensource/license.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/introduction/opensource/license.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/openapi/app.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/openapi/app.en.mdx": "2026-02-26T22:14:30+08:00",
"document/content/docs/openapi/app.mdx": "2026-02-12T18:45:30+08:00", "document/content/docs/openapi/app.mdx": "2026-02-12T18:45:30+08:00",
"document/content/docs/openapi/chat.en.mdx": "2026-03-13T18:08:05+08:00", "document/content/docs/openapi/chat.en.mdx": "2026-03-16T17:09:25+08:00",
"document/content/docs/openapi/chat.mdx": "2026-03-11T15:10:01+08:00", "document/content/docs/openapi/chat.mdx": "2026-03-16T17:09:25+08:00",
"document/content/docs/openapi/dataset.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/openapi/dataset.en.mdx": "2026-02-26T22:14:30+08:00",
"document/content/docs/openapi/dataset.mdx": "2026-02-12T18:45:30+08:00", "document/content/docs/openapi/dataset.mdx": "2026-02-12T18:45:30+08:00",
"document/content/docs/openapi/index.en.mdx": "2026-02-26T22:14:30+08:00", "document/content/docs/openapi/index.en.mdx": "2026-02-26T22:14:30+08:00",
...@@ -308,8 +308,8 @@ ...@@ -308,8 +308,8 @@
"document/content/docs/self-host/upgrading/outdated/48.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/48.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/481.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/481.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/481.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/481.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4810.en.mdx": "2026-03-13T18:08:05+08:00", "document/content/docs/self-host/upgrading/outdated/4810.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4810.mdx": "2026-03-13T18:08:05+08:00", "document/content/docs/self-host/upgrading/outdated/4810.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4811.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4811.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4811.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4811.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4812.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4812.en.mdx": "2026-03-03T17:39:47+08:00",
...@@ -328,8 +328,8 @@ ...@@ -328,8 +328,8 @@
"document/content/docs/self-host/upgrading/outdated/4818.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4818.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4819.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4819.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4819.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4819.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/482.en.mdx": "2026-03-13T18:08:05+08:00", "document/content/docs/self-host/upgrading/outdated/482.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/482.mdx": "2026-03-13T18:08:05+08:00", "document/content/docs/self-host/upgrading/outdated/482.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4820.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4820.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4820.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4820.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/4821.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/4821.en.mdx": "2026-03-03T17:39:47+08:00",
......
...@@ -28,9 +28,19 @@ import type { localeType } from '../../../common/i18n/type'; ...@@ -28,9 +28,19 @@ import type { localeType } from '../../../common/i18n/type';
import { type UserChatItemValueItemType } from '../../chat/type'; import { type UserChatItemValueItemType } from '../../chat/type';
import type { DatasetSearchModeEnum } from '../../dataset/constants'; import type { DatasetSearchModeEnum } from '../../dataset/constants';
import type { ChatRoleEnum } from '../../chat/constants'; import type { ChatRoleEnum } from '../../chat/constants';
import type { MCPClient } from '../../../../service/core/app/mcp';
import z from 'zod'; import z from 'zod';
/*
1. 输入线分类:普通线(实际上就是从 start 直接过来的分支)和递归线(可以追溯到自身的分支)
2. 递归线,会根据最近的一个 target 分支进行分类,同一个分支的属于一组
2. 起始线全部非 waiting 执行,或递归线任意一组全部非 waiting 执行
*/
// 节点边分组结构(简化版:不再区分 common 和 recursive)
export type NodeEdgeGroups = RuntimeEdgeItemType[][]; // 二维数组,每组代表一个独立的逻辑路径
// 预构建的 Map
export type NodeEdgeGroupsMap = Map<string, NodeEdgeGroups>;
export type ExternalProviderType = { export type ExternalProviderType = {
openaiAccount?: OpenaiAccountType; openaiAccount?: OpenaiAccountType;
externalWorkflowVariables?: Record<string, string>; externalWorkflowVariables?: Record<string, string>;
......
...@@ -290,129 +290,6 @@ export const filterWorkflowEdges = (edges: RuntimeEdgeItemType[]) => { ...@@ -290,129 +290,6 @@ export const filterWorkflowEdges = (edges: RuntimeEdgeItemType[]) => {
}; };
/* /*
1. 输入线分类:普通线(实际上就是从 start 直接过来的分支)和递归线(可以追溯到自身的分支)
2. 递归线,会根据最近的一个 target 分支进行分类,同一个分支的属于一组
2. 起始线全部非 waiting 执行,或递归线任意一组全部非 waiting 执行
*/
export const checkNodeRunStatus = ({
nodesMap,
node,
runtimeEdges
}: {
nodesMap: Map<string, RuntimeNodeItemType>;
node: RuntimeNodeItemType;
runtimeEdges: RuntimeEdgeItemType[];
}) => {
const isStartNode = (nodeType: string) => {
const map: Record<any, boolean> = {
[FlowNodeTypeEnum.workflowStart]: true,
[FlowNodeTypeEnum.pluginInput]: true,
[FlowNodeTypeEnum.loopStart]: true
};
return !!map[nodeType];
};
const splitNodeEdges = (targetNode: RuntimeNodeItemType) => {
const commonEdges: RuntimeEdgeItemType[] = [];
const recursiveEdgeGroupsMap = new Map<string, RuntimeEdgeItemType[]>();
const sourceEdges = runtimeEdges.filter((item) => item.target === targetNode.nodeId);
sourceEdges.forEach((sourceEdge) => {
const stack: Array<{
edge: RuntimeEdgeItemType;
visited: Set<string>;
}> = [
{
edge: sourceEdge,
visited: new Set([targetNode.nodeId])
}
];
const MAX_DEPTH = 3000;
let iterations = 0;
while (stack.length > 0 && iterations < MAX_DEPTH) {
iterations++;
const { edge, visited } = stack.pop()!;
// Start node
const sourceNode = nodesMap.get(edge.source);
if (!sourceNode) continue;
if (isStartNode(sourceNode.flowNodeType) || sourceEdge.sourceHandle === 'selectedTools') {
commonEdges.push(sourceEdge);
continue;
}
// Circle detected
if (edge.source === targetNode.nodeId) {
recursiveEdgeGroupsMap.set(edge.target, [
...(recursiveEdgeGroupsMap.get(edge.target) || []),
sourceEdge
]);
continue;
}
if (visited.has(edge.source)) {
continue; // 已访问过此节点,跳过(避免子环干扰)
}
const newVisited = new Set(visited);
newVisited.add(edge.source);
// 查找目标节点的 source edges 并加入栈中
const nextEdges = runtimeEdges.filter((item) => item.target === edge.source);
for (const nextEdge of nextEdges) {
stack.push({
edge: nextEdge,
visited: newVisited
});
}
}
});
return { commonEdges, recursiveEdgeGroups: Array.from(recursiveEdgeGroupsMap.values()) };
};
// Classify edges
const { commonEdges, recursiveEdgeGroups } = splitNodeEdges(node);
// Entry
if (commonEdges.length === 0 && recursiveEdgeGroups.length === 0) {
return 'run';
}
// check active(其中一组边,至少有一个 active,且没有 waiting 即可运行)
if (
commonEdges.some((item) => item.status === 'active') &&
commonEdges.every((item) => item.status !== 'waiting')
) {
return 'run';
}
if (
recursiveEdgeGroups.some(
(item) =>
item.some((item) => item.status === 'active') &&
item.every((item) => item.status !== 'waiting')
)
) {
return 'run';
}
// check skip(其中一组边,全是 skiped 则跳过运行)
if (commonEdges.length > 0 && commonEdges.every((item) => item.status === 'skipped')) {
return 'skip';
}
if (
recursiveEdgeGroups.length > 0 &&
recursiveEdgeGroups.some((item) => item.every((item) => item.status === 'skipped'))
) {
return 'skip';
}
return 'wait';
};
/*
Get the value of the reference variable/node output Get the value of the reference variable/node output
1. [string,string] 1. [string,string]
2. [string,string][] 2. [string,string][]
......
...@@ -845,10 +845,12 @@ const createChatCompletion = async ({ ...@@ -845,10 +845,12 @@ const createChatCompletion = async ({
const response = await ai.chat.completions.create(body, { const response = await ai.chat.completions.create(body, {
...options, ...options,
...(modelData.requestUrl ? { path: modelData.requestUrl } : {}), ...(modelData.requestUrl && !userKey ? { path: modelData.requestUrl } : {}),
headers: { headers: {
...options?.headers, ...options?.headers,
...(modelData.requestAuth ? { Authorization: `Bearer ${modelData.requestAuth}` } : {}) ...(modelData.requestAuth && !userKey
? { Authorization: `Bearer ${modelData.requestAuth}` }
: {})
} }
}); });
......
...@@ -66,6 +66,23 @@ export class SandboxClient { ...@@ -66,6 +66,23 @@ export class SandboxClient {
}, },
createConfig: undefined createConfig: undefined
}; };
} else if (providerName === 'opensandbox') {
return {
provider: 'opensandbox' as const,
config: {
baseUrl: env.AGENT_SANDBOX_OPENSANDBOX_BASEURL,
token: env.AGENT_SANDBOX_OPENSANDBOX_TOKEN,
sandboxId: this.sandboxId
}
};
} else if (providerName === 'e2b') {
return {
provider: 'e2b' as const,
config: {
apiKey: env.AGENT_SANDBOX_E2B_API_KEY,
sandboxId: this.sandboxId
}
};
} else if (!providerName) { } else if (!providerName) {
throw new Error( throw new Error(
'AGENT_SANDBOX_PROVIDER is not configured. Please set it in your environment variables.' 'AGENT_SANDBOX_PROVIDER is not configured. Please set it in your environment variables.'
......
...@@ -52,9 +52,9 @@ SandboxInstanceSchema.index( ...@@ -52,9 +52,9 @@ SandboxInstanceSchema.index(
{ {
unique: true, unique: true,
partialFilterExpression: { partialFilterExpression: {
appId: { $exists: true, $ne: null }, appId: { $exists: true },
userId: { $exists: true, $ne: null }, userId: { $exists: true },
chatId: { $exists: true, $ne: null } chatId: { $exists: true }
} }
} }
); );
......
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
import type { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
export type EdgeIndex = {
bySource: Map<string, RuntimeEdgeItemType[]>;
byTarget: Map<string, RuntimeEdgeItemType[]>;
};
export type EdgeType = 'tree' | 'back' | 'forward' | 'cross';
export interface SCCResult {
nodeToSCC: Map<string, number>;
sccSizes: Map<number, number>;
}
/**
* 使用 Tarjan 算法找出所有强连通分量(SCC)
* SCC 大小 > 1 的节点表示在循环中
*/
export function findSCCs(runtimeNodes: RuntimeNodeItemType[], edgeIndex: EdgeIndex): SCCResult {
const nodeToSCC = new Map<string, number>();
const sccSizes = new Map<number, number>();
let sccId = 0;
const stack: string[] = [];
const inStack = new Set<string>();
const lowLink = new Map<string, number>();
const discoveryTime = new Map<string, number>();
let time = 0;
function tarjan(nodeId: string) {
discoveryTime.set(nodeId, time);
lowLink.set(nodeId, time);
time++;
stack.push(nodeId);
inStack.add(nodeId);
const outEdges = edgeIndex.bySource.get(nodeId) || [];
for (const edge of outEdges) {
const targetId = edge.target;
if (!discoveryTime.has(targetId)) {
tarjan(targetId);
lowLink.set(nodeId, Math.min(lowLink.get(nodeId)!, lowLink.get(targetId)!));
} else if (inStack.has(targetId)) {
lowLink.set(nodeId, Math.min(lowLink.get(nodeId)!, discoveryTime.get(targetId)!));
}
}
// 如果是 SCC 的根节点
if (lowLink.get(nodeId) === discoveryTime.get(nodeId)) {
const sccNodes: string[] = [];
let w: string;
do {
w = stack.pop()!;
inStack.delete(w);
nodeToSCC.set(w, sccId);
sccNodes.push(w);
} while (w !== nodeId);
sccSizes.set(sccId, sccNodes.length);
sccId++;
}
}
// 从所有未访问节点开始
for (const node of runtimeNodes) {
if (!discoveryTime.has(node.nodeId)) {
tarjan(node.nodeId);
}
}
return { nodeToSCC, sccSizes };
}
/**
* 判断节点是否在循环中
*/
export function isNodeInCycle(
nodeId: string,
nodeToSCC: Map<string, number>,
sccSizes: Map<number, number>
): boolean {
const sccId = nodeToSCC.get(nodeId);
if (sccId === undefined) return false;
const size = sccSizes.get(sccId) || 0;
return size > 1;
}
/**
* 对整个工作流图进行一次 DFS,标记每条边的类型
*
* 边类型:
* - tree: 树边(DFS 树中的边)
* - back: 回边(从后代指向当前路径上祖先的边)→ 循环边
* - forward: 前向边(从祖先指向后代的非树边)
* - cross: 跨边(连接不同子树的边)
*/
export function classifyEdgesByDFS(
runtimeNodes: RuntimeNodeItemType[],
edgeIndex: EdgeIndex
): Map<string, EdgeType> {
const edgeTypes = new Map<string, EdgeType>();
const visited = new Set<string>();
const inStack = new Set<string>();
const finished = new Set<string>();
const discoveryTime = new Map<string, number>();
const finishTime = new Map<string, number>();
let time = 0;
function dfs(nodeId: string) {
visited.add(nodeId);
inStack.add(nodeId);
discoveryTime.set(nodeId, ++time);
const outEdges = edgeIndex.bySource.get(nodeId) || [];
for (const edge of outEdges) {
const edgeKey = `${edge.source}-${edge.target}-${edge.sourceHandle || 'default'}`;
const targetId = edge.target;
if (!visited.has(targetId)) {
// 目标节点未访问 → 树边
edgeTypes.set(edgeKey, 'tree');
dfs(targetId);
} else if (inStack.has(targetId)) {
// 目标节点在当前 DFS 路径上 → 回边(循环边)
edgeTypes.set(edgeKey, 'back');
} else if (discoveryTime.get(edge.source)! < discoveryTime.get(targetId)!) {
// 从祖先指向后代的非树边 → 前向边
edgeTypes.set(edgeKey, 'forward');
} else {
// 跨边
edgeTypes.set(edgeKey, 'cross');
}
}
inStack.delete(nodeId);
finished.add(nodeId);
finishTime.set(nodeId, ++time);
}
// 从所有入口节点开始 DFS
const entryNodes = runtimeNodes.filter((node) => {
const inEdges = edgeIndex.byTarget.get(node.nodeId) || [];
return inEdges.length === 0;
});
for (const node of entryNodes) {
if (!visited.has(node.nodeId)) {
dfs(node.nodeId);
}
}
// 处理孤立节点
for (const node of runtimeNodes) {
if (!visited.has(node.nodeId)) {
dfs(node.nodeId);
}
}
return edgeTypes;
}
/**
* 获取边的类型
*/
export function getEdgeType(
edge: RuntimeEdgeItemType,
edgeTypes: Map<string, EdgeType>
): EdgeType | undefined {
const edgeKey = `${edge.source}-${edge.target}-${edge.sourceHandle || 'default'}`;
return edgeTypes.get(edgeKey);
}
...@@ -10,9 +10,12 @@ const LogLevelSchema = z.enum(['trace', 'debug', 'info', 'warning', 'error', 'fa ...@@ -10,9 +10,12 @@ const LogLevelSchema = z.enum(['trace', 'debug', 'info', 'warning', 'error', 'fa
export const env = createEnv({ export const env = createEnv({
server: { server: {
AGENT_SANDBOX_PROVIDER: z.enum(['sealosdevbox']).optional(), AGENT_SANDBOX_PROVIDER: z.enum(['sealosdevbox', 'opensandbox', 'e2b']).optional(),
AGENT_SANDBOX_SEALOS_BASEURL: z.string().url().optional(), AGENT_SANDBOX_SEALOS_BASEURL: z.string().url().optional(),
AGENT_SANDBOX_SEALOS_TOKEN: z.string().optional(), AGENT_SANDBOX_SEALOS_TOKEN: z.string().optional(),
AGENT_SANDBOX_OPENSANDBOX_BASEURL: z.string().url().optional(),
AGENT_SANDBOX_OPENSANDBOX_TOKEN: z.string().optional(),
AGENT_SANDBOX_E2B_API_KEY: z.string().optional(),
LOG_ENABLE_CONSOLE: BoolSchema.default(true), LOG_ENABLE_CONSOLE: BoolSchema.default(true),
LOG_CONSOLE_LEVEL: LogLevelSchema.default('debug'), LOG_CONSOLE_LEVEL: LogLevelSchema.default('debug'),
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
}, },
"dependencies": { "dependencies": {
"@apidevtools/json-schema-ref-parser": "^11.7.2", "@apidevtools/json-schema-ref-parser": "^11.7.2",
"@fastgpt-sdk/sandbox-adapter": "^0.0.19", "@fastgpt-sdk/sandbox-adapter": "^0.0.21",
"@fastgpt-sdk/storage": "catalog:", "@fastgpt-sdk/storage": "catalog:",
"@fastgpt-sdk/logger": "catalog:", "@fastgpt-sdk/logger": "catalog:",
"@fastgpt/global": "workspace:*", "@fastgpt/global": "workspace:*",
......
...@@ -57,9 +57,7 @@ export const authCode = async ({ ...@@ -57,9 +57,7 @@ export const authCode = async ({
return Promise.reject(new UserError(i18nT('common:error.code_error'))); return Promise.reject(new UserError(i18nT('common:error.code_error')));
} }
setTimeout(async () => { await result.deleteOne();
await result.deleteOne({ session }).catch();
}, 60000);
return 'SUCCESS'; return 'SUCCESS';
}); });
......
...@@ -48,7 +48,7 @@ const nextConfig: NextConfig = { ...@@ -48,7 +48,7 @@ const nextConfig: NextConfig = {
]; ];
}, },
webpack(config, { isServer, dev }) { webpack(config, { isServer }) {
config.ignoreWarnings = [ config.ignoreWarnings = [
...(config.ignoreWarnings || []), ...(config.ignoreWarnings || []),
{ {
...@@ -97,6 +97,10 @@ const nextConfig: NextConfig = { ...@@ -97,6 +97,10 @@ const nextConfig: NextConfig = {
if (isServer) { if (isServer) {
(config.externals as string[]).push('@node-rs/jieba'); (config.externals as string[]).push('@node-rs/jieba');
config.externals.push({
'@e2b/code-interpreter': 'commonjs @e2b/code-interpreter',
e2b: 'commonjs e2b'
});
} }
config.experiments = { config.experiments = {
...@@ -127,14 +131,15 @@ const nextConfig: NextConfig = { ...@@ -127,14 +131,15 @@ const nextConfig: NextConfig = {
return config; return config;
}, },
transpilePackages: ['@modelcontextprotocol/sdk', 'ahooks'], transpilePackages: ['@modelcontextprotocol/sdk', 'ahooks', '@fastgpt-sdk/sandbox-adapter'],
serverExternalPackages: [ serverExternalPackages: [
'mongoose', 'mongoose',
'pg', 'pg',
'bullmq', 'bullmq',
'@zilliz/milvus2-sdk-node', '@zilliz/milvus2-sdk-node',
'tiktoken', 'tiktoken',
'@opentelemetry/api-logs' '@opentelemetry/api-logs',
'chalk'
], ],
// 优化大库的 barrel exports tree-shaking // 优化大库的 barrel exports tree-shaking
experimental: { experimental: {
......
import type { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
import type { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
// 辅助函数:创建测试节点
export const createNode = (
nodeId: string,
flowNodeType: FlowNodeTypeEnum
): RuntimeNodeItemType => ({
nodeId,
name: `Node ${nodeId}`,
avatar: '',
flowNodeType,
showStatus: true,
isEntry: false,
inputs: [],
outputs: []
});
// 辅助函数:创建测试边
export const createEdge = (
source: string,
target: string,
status: 'waiting' | 'active' | 'skipped' = 'waiting',
sourceHandle?: string,
targetHandle?: string
): RuntimeEdgeItemType => ({
source,
target,
status,
sourceHandle: sourceHandle || `${source}-source-right`,
targetHandle: targetHandle || `${target}-target-left`
});
...@@ -12,7 +12,8 @@ export default defineConfig({ ...@@ -12,7 +12,8 @@ export default defineConfig({
test: { test: {
coverage: { coverage: {
enabled: true, enabled: true,
reporter: ['text', 'text-summary', 'html', 'json-summary', 'json'], reporter: ['html', 'json-summary', 'json'],
// reporter: ['text', 'text-summary', 'html', 'json-summary', 'json'],
reportOnFailure: true, reportOnFailure: true,
all: false, // 只包含被测试实际覆盖的文件,不包含空目录 all: false, // 只包含被测试实际覆盖的文件,不包含空目录
include: ['projects/app/**/*.ts', 'packages/**/*.ts'], include: ['projects/app/**/*.ts', 'packages/**/*.ts'],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment