mirror of
https://github.com/labring/FastGPT.git
synced 2026-05-07 01:02:55 +08:00
V4.14.9 dev (#6555)
* feat: encapsulate logger (#6535) * feat: encapsulate logger * update engines --------- Co-authored-by: archer <545436317@qq.com> * next config * dev shell * Agent sandbox (#6532) * docs: switch to docs layout and apply black theme (#6533) * feat: add Gemini 3.1 models - Add gemini-3.1-pro-preview (released February 19, 2026) - Add gemini-3.1-flash-lite-preview (released March 3, 2026) Both models support: - 1M context window - 64k max response - Vision - Tool choice * docs: switch to docs layout and apply black theme - Change layout from notebook to docs - Update logo to icon + text format - Apply fumadocs black theme - Simplify global.css (keep only navbar and TOC styles) - Fix icon components to properly accept className props - Add mobile text overflow handling - Update Node engine requirement to >=20.x * doc * doc * lock * fix: ts * doc * doc --------- Co-authored-by: archer <archer@archerdeMac-mini.local> Co-authored-by: archer <545436317@qq.com> * Doc (#6493) * cloud doc * doc refactor * doc move * seo * remove doc * yml * doc * fix: tsconfig * fix: tsconfig * sandbox version (#6497) * sandbox version * add sandbox log * update lock * fix * fix: sandbox * doc * add console * i18n * sandbxo in agent * feat: agent sandbox * lock * feat: sandbox ui * sandbox check exists * env tempalte * doc * lock * sandbox in chat window * sandbox entry * fix: test * rename var * sandbox config tip * update sandbox lifecircle * update prompt * rename provider test * sandbox logger * yml --------- Co-authored-by: Archer <archer@fastgpt.io> Co-authored-by: archer <archer@archerdeMac-mini.local> * perf: sandbox error tip * Add sandbox limit and fix some issue (#6550) * sandbox in plan * fix: some issue * fix: test * editor default path * fix: comment * perf: sandbox worksapce * doc * perf: del sandbox * sandbox build * fix: test * fix: pr comment --------- Co-authored-by: Ryo <whoeverimf5@gmail.com> Co-authored-by: Archer <archer@fastgpt.io> Co-authored-by: archer <archer@archerdeMac-mini.local>
This commit is contained in:
@@ -49,6 +49,9 @@ export type DispatchAgentModuleProps = ModuleDispatchProps<{
|
||||
|
||||
// Knowledge base search configuration
|
||||
[NodeInputKeyEnum.datasetParams]?: AppFormEditFormType['dataset'];
|
||||
|
||||
// Sandbox (Computer Use)
|
||||
[NodeInputKeyEnum.useAgentSandbox]?: boolean;
|
||||
}>;
|
||||
|
||||
type Response = DispatchNodeResultType<{
|
||||
@@ -85,7 +88,9 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
fileUrlList: fileLinks,
|
||||
agent_selectedTools: selectedTools = [],
|
||||
// Dataset search configuration
|
||||
agent_datasetParams: datasetParams
|
||||
agent_datasetParams: datasetParams,
|
||||
// Sandbox (Computer Use)
|
||||
useAgentSandbox = false
|
||||
}
|
||||
} = props;
|
||||
const chatHistories = getHistories(history, histories);
|
||||
@@ -162,7 +167,8 @@ export const dispatchRunAgent = async (props: DispatchAgentModuleProps): Promise
|
||||
lang,
|
||||
getPlanTool: true,
|
||||
hasDataset: datasetParams && datasetParams.datasets.length > 0,
|
||||
hasFiles: !!chatConfig?.fileSelectConfig?.canSelectFile
|
||||
hasFiles: !!chatConfig?.fileSelectConfig?.canSelectFile,
|
||||
useAgentSandbox
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { dispatchTool } from '../sub/tool';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { DatasetSearchToolSchema } from '../sub/dataset/utils';
|
||||
import { dispatchAgentDatasetSearch } from '../sub/dataset';
|
||||
import { dispatchSandboxShell } from '../sub/sandbox';
|
||||
import type { DispatchAgentModuleProps } from '..';
|
||||
import { getLLMModel } from '../../../../../ai/model';
|
||||
import { getStepCallQuery, getStepDependon } from './dependon';
|
||||
@@ -31,6 +32,10 @@ import { PlanAgentParamsSchema } from '../sub/plan/constants';
|
||||
import { filterMemoryMessages } from '../../utils';
|
||||
import { dispatchApp, dispatchPlugin } from '../sub/app';
|
||||
import { getLogger, LogCategories } from '../../../../../../common/logger';
|
||||
import {
|
||||
SandboxShellToolSchema,
|
||||
SANDBOX_TOOL_NAME
|
||||
} from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
|
||||
type Response = {
|
||||
stepResponse?: {
|
||||
@@ -85,7 +90,9 @@ export const masterCall = async ({
|
||||
params: {
|
||||
model,
|
||||
// Dataset search configuration
|
||||
agent_datasetParams: datasetParams
|
||||
agent_datasetParams: datasetParams,
|
||||
// Sandbox (Computer Use)
|
||||
useAgentSandbox = false
|
||||
}
|
||||
} = props;
|
||||
|
||||
@@ -177,7 +184,11 @@ export const masterCall = async ({
|
||||
const messages: ChatCompletionMessageParam[] = [
|
||||
{
|
||||
role: 'system' as const,
|
||||
content: getMasterSystemPrompt(systemPrompt, hasUserTools)
|
||||
content: getMasterSystemPrompt({
|
||||
systemPrompt,
|
||||
hasUserTools,
|
||||
useAgentSandbox
|
||||
})
|
||||
},
|
||||
...masterMessages
|
||||
];
|
||||
@@ -365,6 +376,33 @@ export const masterCall = async ({
|
||||
usages: result.usages
|
||||
};
|
||||
}
|
||||
if (toolId === SANDBOX_TOOL_NAME) {
|
||||
const toolParams = SandboxShellToolSchema.safeParse(
|
||||
parseJsonArgs(call.function.arguments)
|
||||
);
|
||||
if (!toolParams.success) {
|
||||
return {
|
||||
response: toolParams.error.message,
|
||||
usages: []
|
||||
};
|
||||
}
|
||||
|
||||
const result = await dispatchSandboxShell({
|
||||
command: toolParams.data.command,
|
||||
timeout: toolParams.data.timeout,
|
||||
appId: runningAppInfo.id,
|
||||
userId: props.uid,
|
||||
chatId,
|
||||
lang: props.lang
|
||||
});
|
||||
|
||||
childrenResponses.push(result.nodeResponse);
|
||||
|
||||
return {
|
||||
response: result.response,
|
||||
usages: result.usages
|
||||
};
|
||||
}
|
||||
if (toolId === SubAppIds.plan) {
|
||||
try {
|
||||
const toolArgs = await PlanAgentParamsSchema.safeParseAsync(
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { SubAppIds } from '@fastgpt/global/core/workflow/node/agent/constants';
|
||||
import { SANDBOX_SYSTEM_PROMPT } from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
|
||||
export const getMasterSystemPrompt = (systemPrompt?: string, hasUserTools: boolean = true) => {
|
||||
export const getMasterSystemPrompt = ({
|
||||
systemPrompt,
|
||||
hasUserTools,
|
||||
useAgentSandbox
|
||||
}: {
|
||||
systemPrompt?: string;
|
||||
hasUserTools: boolean;
|
||||
useAgentSandbox: boolean;
|
||||
}) => {
|
||||
return `<!-- Master Agent 决策系统 -->
|
||||
|
||||
<role>
|
||||
@@ -17,6 +26,15 @@ ${systemPrompt}
|
||||
: ''
|
||||
}
|
||||
|
||||
${
|
||||
useAgentSandbox
|
||||
? `
|
||||
<sandbox_environment>
|
||||
${SANDBOX_SYSTEM_PROMPT}
|
||||
</sandbox_environment>
|
||||
`
|
||||
: ''
|
||||
}
|
||||
|
||||
<decision_paths>
|
||||
三种执行路径:
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { SandboxClient } from '../../../../../../ai/sandbox/controller';
|
||||
import type { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
||||
import { getLogger, LogCategories } from '../../../../../../../common/logger';
|
||||
import {
|
||||
SANDBOX_ICON,
|
||||
SANDBOX_NAME,
|
||||
SANDBOX_TOOL_NAME
|
||||
} from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { parseI18nString } from '@fastgpt/global/common/i18n/utils';
|
||||
import type { localeType } from '@fastgpt/global/common/i18n/type';
|
||||
|
||||
type SandboxShellParams = {
|
||||
command: string;
|
||||
timeout?: number;
|
||||
appId: string;
|
||||
userId: string;
|
||||
chatId: string;
|
||||
lang?: localeType;
|
||||
};
|
||||
|
||||
export const dispatchSandboxShell = async ({
|
||||
command,
|
||||
timeout,
|
||||
appId,
|
||||
userId,
|
||||
chatId,
|
||||
lang
|
||||
}: SandboxShellParams): Promise<{
|
||||
response: string;
|
||||
usages: ChatNodeUsageType[];
|
||||
nodeResponse: ChatHistoryItemResType;
|
||||
}> => {
|
||||
const startTime = Date.now();
|
||||
const nodeId = getNanoid(6);
|
||||
const moduleName = parseI18nString(SANDBOX_NAME, lang);
|
||||
|
||||
try {
|
||||
const sandboxInstance = new SandboxClient({
|
||||
appId,
|
||||
userId,
|
||||
chatId
|
||||
});
|
||||
|
||||
const result = await sandboxInstance.exec(command, timeout);
|
||||
const response = JSON.stringify({
|
||||
stdout: result.stdout,
|
||||
stderr: result.stderr,
|
||||
exitCode: result.exitCode
|
||||
});
|
||||
|
||||
getLogger(LogCategories.MODULE.AI.AGENT).info('[Sandbox Shell] Command executed', {
|
||||
command,
|
||||
exitCode: result.exitCode,
|
||||
stdoutLength: result.stdout?.length || 0,
|
||||
stderrLength: result.stderr?.length || 0
|
||||
});
|
||||
|
||||
return {
|
||||
response,
|
||||
usages: [],
|
||||
nodeResponse: {
|
||||
nodeId,
|
||||
id: nodeId,
|
||||
moduleType: FlowNodeTypeEnum.tool,
|
||||
moduleName,
|
||||
moduleLogo: SANDBOX_ICON,
|
||||
toolId: SANDBOX_TOOL_NAME,
|
||||
toolInput: { command, timeout },
|
||||
toolRes: response,
|
||||
totalPoints: 0,
|
||||
runningTime: +((Date.now() - startTime) / 1000).toFixed(2)
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
getLogger(LogCategories.MODULE.AI.AGENT).error('[Sandbox Shell] Execution failed', { error });
|
||||
|
||||
const errorResponse = JSON.stringify({
|
||||
stdout: '',
|
||||
stderr: getErrText(error),
|
||||
exitCode: -1
|
||||
});
|
||||
|
||||
return {
|
||||
response: errorResponse,
|
||||
usages: [],
|
||||
nodeResponse: {
|
||||
nodeId,
|
||||
id: nodeId,
|
||||
moduleType: FlowNodeTypeEnum.tool,
|
||||
moduleName,
|
||||
moduleLogo: SANDBOX_ICON,
|
||||
toolInput: { command, timeout },
|
||||
toolRes: errorResponse,
|
||||
totalPoints: 0,
|
||||
runningTime: +((Date.now() - startTime) / 1000).toFixed(2)
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { localeType } from '@fastgpt/global/common/i18n/type';
|
||||
import type { SkillToolType } from '@fastgpt/global/core/ai/skill/type';
|
||||
import type { ChatCompletionTool } from '@fastgpt/global/core/ai/type';
|
||||
import type { SubAppRuntimeType } from './type';
|
||||
import { getAgentRuntimeTools } from './sub/tool/utils';
|
||||
import type { ChatCompletionTool } from '@fastgpt/global/core/ai/type';
|
||||
import { readFileTool } from './sub/file/utils';
|
||||
import { PlanAgentTool } from './sub/plan/constants';
|
||||
import { datasetSearchTool } from './sub/dataset/utils';
|
||||
import { SANDBOX_TOOLS } from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
|
||||
export const getSubapps = async ({
|
||||
tmbId,
|
||||
@@ -13,7 +14,8 @@ export const getSubapps = async ({
|
||||
lang,
|
||||
getPlanTool,
|
||||
hasDataset,
|
||||
hasFiles
|
||||
hasFiles,
|
||||
useAgentSandbox
|
||||
}: {
|
||||
tmbId: string;
|
||||
tools: SkillToolType[];
|
||||
@@ -21,6 +23,7 @@ export const getSubapps = async ({
|
||||
getPlanTool?: Boolean;
|
||||
hasDataset?: boolean;
|
||||
hasFiles: boolean;
|
||||
useAgentSandbox: boolean;
|
||||
}): Promise<{
|
||||
completionTools: ChatCompletionTool[];
|
||||
subAppsMap: Map<string, SubAppRuntimeType>;
|
||||
@@ -42,6 +45,11 @@ export const getSubapps = async ({
|
||||
completionTools.push(datasetSearchTool);
|
||||
}
|
||||
|
||||
/* Sandbox Shell */
|
||||
if (useAgentSandbox) {
|
||||
completionTools.push(...SANDBOX_TOOLS);
|
||||
}
|
||||
|
||||
/* System tool */
|
||||
const formatTools = await getAgentRuntimeTools({
|
||||
tools,
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import type { ChildResponseItemType } from './type';
|
||||
import { SANDBOX_TOOL_NAME } from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
|
||||
export const getMultiplePrompt = (obj: {
|
||||
fileCount: number;
|
||||
@@ -12,3 +16,36 @@ Image:{{imgCount}}
|
||||
{{question}}`;
|
||||
return replaceVariable(prompt, obj);
|
||||
};
|
||||
|
||||
export const getSandboxToolWorkflowResponse = ({
|
||||
name,
|
||||
logo,
|
||||
input,
|
||||
response,
|
||||
durationSeconds
|
||||
}: {
|
||||
name: string;
|
||||
logo: string;
|
||||
input: Record<string, any>;
|
||||
response: string;
|
||||
durationSeconds: number;
|
||||
}): ChildResponseItemType => {
|
||||
return {
|
||||
flowResponses: [
|
||||
{
|
||||
moduleName: name,
|
||||
moduleType: FlowNodeTypeEnum.tool,
|
||||
moduleLogo: logo,
|
||||
toolId: SANDBOX_TOOL_NAME,
|
||||
toolInput: input,
|
||||
toolRes: response,
|
||||
totalPoints: 0,
|
||||
id: getNanoid(),
|
||||
nodeId: getNanoid(),
|
||||
runningTime: durationSeconds
|
||||
}
|
||||
],
|
||||
flowUsages: [],
|
||||
runTimes: 0
|
||||
};
|
||||
};
|
||||
|
||||
@@ -62,7 +62,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
fileUrlList: fileLinks,
|
||||
aiChatVision,
|
||||
aiChatReasoning,
|
||||
isResponseAnswerText = true
|
||||
isResponseAnswerText = true,
|
||||
useAgentSandbox = false
|
||||
}
|
||||
} = props;
|
||||
|
||||
@@ -220,7 +221,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
toolModel,
|
||||
messages: adaptMessages,
|
||||
childrenInteractiveParams:
|
||||
lastInteractive?.type === 'toolChildrenInteractive' ? lastInteractive.params : undefined
|
||||
lastInteractive?.type === 'toolChildrenInteractive' ? lastInteractive.params : undefined,
|
||||
useAgentSandbox
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ import type {
|
||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { runWorkflow } from '../../index';
|
||||
import type { DispatchToolModuleProps, ToolNodeItemType } from './type';
|
||||
import type { DispatchFlowResponse } from '../../type';
|
||||
import type { ChildResponseItemType, DispatchToolModuleProps, ToolNodeItemType } from './type';
|
||||
import { chats2GPTMessages, GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import type { AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils';
|
||||
@@ -18,11 +17,22 @@ import { toolValueTypeList, valueTypeJsonSchemaMap } from '@fastgpt/global/core/
|
||||
import { runAgentCall } from '../../../../ai/llm/agentCall';
|
||||
import type { ToolCallChildrenInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import type { JsonSchemaPropertiesItemType } from '@fastgpt/global/core/app/jsonschema';
|
||||
import {
|
||||
SANDBOX_SHELL_TOOL,
|
||||
SandboxShellToolSchema,
|
||||
SANDBOX_SYSTEM_PROMPT,
|
||||
SANDBOX_ICON,
|
||||
SANDBOX_NAME,
|
||||
SANDBOX_TOOL_NAME
|
||||
} from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
import { SandboxClient } from '../../../../ai/sandbox/controller';
|
||||
import { getSandboxToolWorkflowResponse } from './constants';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
type ResponseType = {
|
||||
requestIds: string[];
|
||||
error?: string;
|
||||
toolDispatchFlowResponses: DispatchFlowResponse[];
|
||||
toolDispatchFlowResponses: ChildResponseItemType[];
|
||||
toolCallInputTokens: number;
|
||||
toolCallOutputTokens: number;
|
||||
completeMessages: ChatCompletionMessageParam[];
|
||||
@@ -31,8 +41,17 @@ type ResponseType = {
|
||||
toolWorkflowInteractiveResponse?: ToolCallChildrenInteractive;
|
||||
};
|
||||
|
||||
export const runToolCall = async (props: DispatchToolModuleProps): Promise<ResponseType> => {
|
||||
const { messages, toolNodes, toolModel, childrenInteractiveParams, ...workflowProps } = props;
|
||||
export const runToolCall = async (
|
||||
props: DispatchToolModuleProps & { useAgentSandbox?: boolean }
|
||||
): Promise<ResponseType> => {
|
||||
const {
|
||||
messages,
|
||||
toolNodes,
|
||||
toolModel,
|
||||
childrenInteractiveParams,
|
||||
useAgentSandbox,
|
||||
...workflowProps
|
||||
} = props;
|
||||
const {
|
||||
res,
|
||||
checkIsStopping,
|
||||
@@ -97,16 +116,40 @@ export const runToolCall = async (props: DispatchToolModuleProps): Promise<Respo
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 注入 sandbox_shell 工具和提示词
|
||||
let finalMessages = messages;
|
||||
if (useAgentSandbox) {
|
||||
tools.push(SANDBOX_SHELL_TOOL);
|
||||
|
||||
const systemMessage = messages.find((m) => m.role === 'system');
|
||||
if (systemMessage) {
|
||||
finalMessages = messages.map((m) =>
|
||||
m.role === 'system' ? { ...m, content: `${m.content}\n\n${SANDBOX_SYSTEM_PROMPT}` } : m
|
||||
);
|
||||
} else {
|
||||
finalMessages = [{ role: 'system', content: SANDBOX_SYSTEM_PROMPT }, ...messages];
|
||||
}
|
||||
}
|
||||
|
||||
const getToolInfo = (name: string) => {
|
||||
if (name === SANDBOX_TOOL_NAME) {
|
||||
return {
|
||||
name: SANDBOX_NAME[workflowProps.lang || 'zh-CN'] || SANDBOX_TOOL_NAME,
|
||||
avatar: SANDBOX_ICON
|
||||
};
|
||||
}
|
||||
|
||||
const toolNode = toolNodesMap.get(name);
|
||||
return {
|
||||
name: toolNode?.name || '',
|
||||
avatar: toolNode?.avatar || ''
|
||||
avatar: toolNode?.avatar || '',
|
||||
rawData: toolNode
|
||||
};
|
||||
};
|
||||
|
||||
// 工具响应原始值
|
||||
const toolRunResponses: DispatchFlowResponse[] = [];
|
||||
const toolRunResponses: ChildResponseItemType[] = [];
|
||||
|
||||
const {
|
||||
inputTokens,
|
||||
@@ -120,7 +163,7 @@ export const runToolCall = async (props: DispatchToolModuleProps): Promise<Respo
|
||||
} = await runAgentCall({
|
||||
maxRunAgentTimes: 50,
|
||||
body: {
|
||||
messages,
|
||||
messages: finalMessages,
|
||||
tools,
|
||||
model: toolModel.model,
|
||||
max_tokens: maxToken,
|
||||
@@ -158,7 +201,7 @@ export const runToolCall = async (props: DispatchToolModuleProps): Promise<Respo
|
||||
},
|
||||
onToolCall({ call }) {
|
||||
if (!isResponseAnswerText) return;
|
||||
const toolNode = toolNodesMap.get(call.function.name);
|
||||
const toolNode = getToolInfo(call.function.name);
|
||||
if (toolNode) {
|
||||
workflowStreamResponse?.({
|
||||
id: call.id,
|
||||
@@ -169,8 +212,7 @@ export const runToolCall = async (props: DispatchToolModuleProps): Promise<Respo
|
||||
toolName: toolNode.name,
|
||||
toolAvatar: toolNode.avatar,
|
||||
functionName: call.function.name,
|
||||
params: call.function.arguments ?? '',
|
||||
response: ''
|
||||
params: call.function.arguments ?? ''
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -186,38 +228,101 @@ export const runToolCall = async (props: DispatchToolModuleProps): Promise<Respo
|
||||
id: tool.id,
|
||||
toolName: '',
|
||||
toolAvatar: '',
|
||||
params,
|
||||
response: ''
|
||||
params
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
handleToolResponse: async ({ call, messages }) => {
|
||||
const toolNode = toolNodesMap.get(call.function?.name);
|
||||
const tool = getToolInfo(call.function?.name);
|
||||
const startTime = Date.now();
|
||||
|
||||
if (!toolNode) {
|
||||
return {
|
||||
response: 'Call tool not found',
|
||||
assistantMessages: [],
|
||||
usages: [],
|
||||
interactive: undefined
|
||||
};
|
||||
}
|
||||
const {
|
||||
response,
|
||||
flowResponse,
|
||||
assistantMessages = [],
|
||||
usages = [],
|
||||
interactive,
|
||||
stop
|
||||
} = await (async () => {
|
||||
// 拦截 sandbox_shell 调用
|
||||
if (call.function?.name === SANDBOX_TOOL_NAME) {
|
||||
try {
|
||||
const params = SandboxShellToolSchema.parse(parseJsonArgs(call.function.arguments));
|
||||
|
||||
// Init tool params and run
|
||||
const startParams = parseJsonArgs(call.function.arguments);
|
||||
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
||||
initToolCallEdges(runtimeEdges, [toolNode.nodeId]);
|
||||
const instance = new SandboxClient({
|
||||
appId: String(workflowProps.runningAppInfo.id),
|
||||
userId: String(workflowProps.uid),
|
||||
chatId: workflowProps.chatId
|
||||
});
|
||||
|
||||
const toolRunResponse = await runWorkflow({
|
||||
...workflowProps,
|
||||
runtimeNodes,
|
||||
usageId: undefined,
|
||||
isToolCall: true
|
||||
});
|
||||
const result = await instance.exec(params.command, params.timeout);
|
||||
|
||||
// Format tool response
|
||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||
const stringToolResponse = JSON.stringify({
|
||||
stdout: result.stdout,
|
||||
stderr: result.stderr,
|
||||
exitCode: result.exitCode
|
||||
});
|
||||
|
||||
const flowResponse = getSandboxToolWorkflowResponse({
|
||||
name: tool.name,
|
||||
logo: SANDBOX_ICON,
|
||||
input: params,
|
||||
response: stringToolResponse,
|
||||
durationSeconds: +((Date.now() - startTime) / 1000).toFixed(2)
|
||||
});
|
||||
|
||||
return {
|
||||
response: stringToolResponse,
|
||||
flowResponse
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
response: `Sandbox execution error: ${getErrText(error)}`
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const toolNode = tool?.rawData;
|
||||
|
||||
if (!toolNode) {
|
||||
return {
|
||||
response: 'Call tool not found'
|
||||
};
|
||||
}
|
||||
|
||||
// Init tool params and run
|
||||
const startParams = parseJsonArgs(call.function.arguments);
|
||||
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
||||
initToolCallEdges(runtimeEdges, [toolNode.nodeId]);
|
||||
|
||||
const toolRunResponse = await runWorkflow({
|
||||
...workflowProps,
|
||||
runtimeNodes,
|
||||
usageId: undefined,
|
||||
isToolCall: true
|
||||
});
|
||||
|
||||
// Format tool response
|
||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||
|
||||
return {
|
||||
response: stringToolResponse,
|
||||
flowResponse: toolRunResponse,
|
||||
assistantMessages: chats2GPTMessages({
|
||||
messages: [
|
||||
{
|
||||
obj: ChatRoleEnum.AI,
|
||||
value: toolRunResponse.assistantResponses
|
||||
}
|
||||
],
|
||||
reserveId: false
|
||||
}),
|
||||
usages: toolRunResponse.flowUsages,
|
||||
interactive: toolRunResponse.workflowInteractiveResponse,
|
||||
stop: toolRunResponse.flowResponses?.some((item) => item.toolStop)
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
if (isResponseAnswerText) {
|
||||
workflowStreamResponse?.({
|
||||
@@ -229,30 +334,22 @@ export const runToolCall = async (props: DispatchToolModuleProps): Promise<Respo
|
||||
toolName: '',
|
||||
toolAvatar: '',
|
||||
params: '',
|
||||
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
||||
response: sliceStrStartEnd(response, 5000, 5000)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toolRunResponses.push(toolRunResponse);
|
||||
|
||||
const assistantMessages = chats2GPTMessages({
|
||||
messages: [
|
||||
{
|
||||
obj: ChatRoleEnum.AI,
|
||||
value: toolRunResponse.assistantResponses
|
||||
}
|
||||
],
|
||||
reserveId: false
|
||||
});
|
||||
if (flowResponse) {
|
||||
toolRunResponses.push(flowResponse);
|
||||
}
|
||||
|
||||
return {
|
||||
response: stringToolResponse,
|
||||
response,
|
||||
assistantMessages,
|
||||
usages: toolRunResponse.flowUsages,
|
||||
interactive: toolRunResponse.workflowInteractiveResponse,
|
||||
stop: toolRunResponse.flowResponses?.some((item) => item.toolStop)
|
||||
usages,
|
||||
interactive,
|
||||
stop
|
||||
};
|
||||
},
|
||||
childrenInteractiveParams,
|
||||
|
||||
@@ -27,6 +27,7 @@ export type DispatchToolModuleProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.aiChatStopSign]?: string;
|
||||
[NodeInputKeyEnum.aiChatResponseFormat]?: string;
|
||||
[NodeInputKeyEnum.aiChatJsonSchema]?: string;
|
||||
[NodeInputKeyEnum.useAgentSandbox]?: boolean;
|
||||
}> & {
|
||||
messages: ChatCompletionMessageParam[];
|
||||
toolNodes: ToolNodeItemType[];
|
||||
@@ -38,3 +39,9 @@ export type ToolNodeItemType = RuntimeNodeItemType & {
|
||||
toolParams: RuntimeNodeItemType['inputs'];
|
||||
jsonSchema?: JSONSchemaInputType;
|
||||
};
|
||||
|
||||
export type ChildResponseItemType = {
|
||||
flowResponses: DispatchFlowResponse['flowResponses'];
|
||||
runTimes: DispatchFlowResponse['runTimes'];
|
||||
flowUsages: DispatchFlowResponse['flowUsages'];
|
||||
};
|
||||
|
||||
@@ -78,6 +78,6 @@ export const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||
[FlowNodeTypeEnum.comment]: () => Promise.resolve(),
|
||||
[FlowNodeTypeEnum.toolSet]: () => Promise.resolve(),
|
||||
|
||||
// @deprecated
|
||||
/** @deprecated */
|
||||
[FlowNodeTypeEnum.runApp]: dispatchAppRequest
|
||||
};
|
||||
|
||||
@@ -29,13 +29,13 @@ export const dispatchCodeSandbox = async (props: RunCodeType): Promise<RunCodeRe
|
||||
params: { codeType, code, [NodeInputKeyEnum.addInputParam]: customVariables }
|
||||
} = props;
|
||||
|
||||
if (!process.env.SANDBOX_URL) {
|
||||
if (!process.env.CODE_SANDBOX_URL) {
|
||||
return {
|
||||
error: {
|
||||
[NodeOutputKeyEnum.error]: 'Can not find SANDBOX_URL in env'
|
||||
[NodeOutputKeyEnum.error]: 'Can not find CODE_SANDBOX_URL in env'
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
errorText: 'Can not find SANDBOX_URL in env',
|
||||
errorText: 'Can not find CODE_SANDBOX_URL in env',
|
||||
customInputs: customVariables
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user