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:
@@ -275,6 +275,7 @@ export const runAgentCall = async ({
|
||||
throwError: false,
|
||||
body: {
|
||||
...body,
|
||||
max_tokens,
|
||||
model,
|
||||
messages: requestMessages,
|
||||
tool_choice: consecutiveRequestToolTimes > 5 ? 'none' : 'auto',
|
||||
|
||||
@@ -442,16 +442,6 @@ export const compressToolResponse = async ({
|
||||
// 取静态阈值和动态可用空间的较小值
|
||||
const maxTokens = Math.min(staticMaxTokens, availableSpace);
|
||||
|
||||
logger.info('Tool Response Compression', {
|
||||
responseTokens: await countGptMessagesTokens([{ role: 'user', content: response }]),
|
||||
currentMessagesTokens,
|
||||
maxContext: model.maxContext,
|
||||
reservedTokens,
|
||||
availableSpace,
|
||||
staticMaxTokens,
|
||||
finalMaxTokens: maxTokens
|
||||
});
|
||||
|
||||
// 调用通用压缩函数
|
||||
return compressLargeContent({
|
||||
content: response,
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
parseLLMStreamResponse,
|
||||
parseReasoningContent
|
||||
} from '../utils';
|
||||
import { removeDatasetCiteText } from '@fastgpt/global/core/ai/llm/utils';
|
||||
import { getLLMSupportParams, removeDatasetCiteText } from '@fastgpt/global/core/ai/llm/utils';
|
||||
import { getAIApi } from '../config';
|
||||
import type { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
|
||||
import { customNanoid, getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
@@ -772,6 +772,21 @@ const llmCompletionsBodyFormat = async <T extends CompletionsBodyType>({
|
||||
Object.entries(requestBody).filter(([_, value]) => value !== null && value !== undefined)
|
||||
) as T;
|
||||
|
||||
const supportParams = getLLMSupportParams(modelData);
|
||||
|
||||
if (!supportParams.temperature) {
|
||||
delete requestBody.temperature;
|
||||
}
|
||||
if (!supportParams.topP) {
|
||||
delete requestBody.top_p;
|
||||
}
|
||||
if (!supportParams.stop) {
|
||||
delete requestBody.stop;
|
||||
}
|
||||
if (!supportParams.responseFormat) {
|
||||
delete requestBody.response_format;
|
||||
}
|
||||
|
||||
// field map
|
||||
if (modelData.fieldMap) {
|
||||
Object.entries(modelData.fieldMap).forEach(([sourceKey, targetKey]) => {
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
import {
|
||||
generateSandboxId,
|
||||
SandboxStatusEnum,
|
||||
SANDBOX_SUSPEND_MINUTES
|
||||
} from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
import { env } from '../../../env';
|
||||
import { MongoSandboxInstance } from './schema';
|
||||
import {
|
||||
createSandbox,
|
||||
type ExecuteResult,
|
||||
type ISandbox,
|
||||
type ResourceLimits
|
||||
} from '@fastgpt-sdk/sandbox-adapter';
|
||||
import { getLogger, LogCategories } from '../../../common/logger';
|
||||
import { setCron } from '../../../common/system/cron';
|
||||
import { subMinutes } from 'date-fns';
|
||||
import { batchRun } from '@fastgpt/global/common/system/utils';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
const logger = getLogger(LogCategories.MODULE.AI.SANDBOX);
|
||||
|
||||
type UnionIdType = {
|
||||
appId: string;
|
||||
userId: string;
|
||||
chatId: string;
|
||||
};
|
||||
|
||||
export class SandboxClient {
|
||||
private appId?: string;
|
||||
private userId?: string;
|
||||
private chatId?: string;
|
||||
private sandboxId: string;
|
||||
readonly provider: ISandbox;
|
||||
|
||||
constructor(
|
||||
props:
|
||||
| {
|
||||
sandboxId: string;
|
||||
}
|
||||
| UnionIdType,
|
||||
opts: {
|
||||
resourceLimits?: ResourceLimits;
|
||||
} = {}
|
||||
) {
|
||||
if ('sandboxId' in props) {
|
||||
this.sandboxId = props.sandboxId;
|
||||
} else {
|
||||
this.appId = props.appId;
|
||||
this.userId = props.userId;
|
||||
this.chatId = props.chatId;
|
||||
this.sandboxId = generateSandboxId(this.appId, this.userId, this.chatId);
|
||||
}
|
||||
|
||||
const providerName = env.AGENT_SANDBOX_PROVIDER;
|
||||
|
||||
const params = (() => {
|
||||
if (providerName === 'sealosdevbox') {
|
||||
if (!env.AGENT_SANDBOX_SEALOS_BASEURL || !env.AGENT_SANDBOX_SEALOS_TOKEN) {
|
||||
throw new Error('AGENT_SANDBOX_SEALOS_BASEURL / AGENT_SANDBOX_SEALOS_TOKEN required');
|
||||
}
|
||||
return {
|
||||
provider: 'sealosdevbox' as const,
|
||||
config: {
|
||||
baseUrl: env.AGENT_SANDBOX_SEALOS_BASEURL,
|
||||
token: env.AGENT_SANDBOX_SEALOS_TOKEN,
|
||||
sandboxId: this.sandboxId
|
||||
},
|
||||
createConfig: undefined
|
||||
};
|
||||
} else if (!providerName) {
|
||||
throw new Error(
|
||||
'AGENT_SANDBOX_PROVIDER is not configured. Please set it in your environment variables.'
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Unsupported sandbox provider: ${env.AGENT_SANDBOX_PROVIDER}`);
|
||||
}
|
||||
})();
|
||||
this.provider = createSandbox(params.provider, params.config, params.createConfig);
|
||||
}
|
||||
|
||||
async ensureAvailable() {
|
||||
await MongoSandboxInstance.findOneAndUpdate(
|
||||
{ provider: this.provider.provider, sandboxId: this.sandboxId },
|
||||
{
|
||||
$set: {
|
||||
status: SandboxStatusEnum.running,
|
||||
lastActiveAt: new Date()
|
||||
},
|
||||
$setOnInsert: {
|
||||
...(this.appId ? { appId: this.appId } : {}),
|
||||
...(this.userId ? { userId: this.userId } : {}),
|
||||
...(this.chatId ? { chatId: this.chatId } : {}),
|
||||
createdAt: new Date()
|
||||
}
|
||||
},
|
||||
{ upsert: true }
|
||||
);
|
||||
await this.provider.ensureRunning();
|
||||
}
|
||||
|
||||
async exec(command: string, timeout?: number): Promise<ExecuteResult> {
|
||||
try {
|
||||
await this.ensureAvailable();
|
||||
} catch (err) {
|
||||
logger.error('Failed to ensure sandbox available', { sandboxId: this.sandboxId, error: err });
|
||||
return {
|
||||
stdout: '',
|
||||
stderr: `Sandbox service is not available: ${getErrText(err)}`,
|
||||
exitCode: -1
|
||||
};
|
||||
}
|
||||
|
||||
return await this.provider
|
||||
.execute(command, {
|
||||
timeoutMs: timeout ? timeout * 1000 : undefined
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error('Failed to execute sandbox', { sandboxId: this.sandboxId, error: err });
|
||||
return {
|
||||
stdout: '',
|
||||
stderr: `Failed to execute sandbox: ${getErrText(err)}`,
|
||||
exitCode: -1
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async delete() {
|
||||
await this.provider.delete();
|
||||
await MongoSandboxInstance.deleteOne({ sandboxId: this.sandboxId });
|
||||
}
|
||||
|
||||
async stop() {
|
||||
await this.provider.stop();
|
||||
await MongoSandboxInstance.updateOne(
|
||||
{ sandboxId: this.sandboxId },
|
||||
{ $set: { status: SandboxStatusEnum.stoped } }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ==== Delete Sandboxes ====
|
||||
export const deleteSandboxesByChatIds = async ({
|
||||
appId,
|
||||
chatIds
|
||||
}: {
|
||||
appId: string;
|
||||
chatIds: string[];
|
||||
}) => {
|
||||
const instances = await MongoSandboxInstance.find({ appId, chatId: { $in: chatIds } }).lean();
|
||||
if (!instances.length) return;
|
||||
|
||||
await Promise.allSettled(
|
||||
instances.map((doc) =>
|
||||
new SandboxClient({
|
||||
sandboxId: doc.sandboxId
|
||||
})
|
||||
.delete()
|
||||
.catch((err) => {
|
||||
logger.error('Failed to delete sandbox', { sandboxId: doc.sandboxId, error: err });
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
export const deleteSandboxesByAppId = async (appId: string) => {
|
||||
const instances = await MongoSandboxInstance.find({ appId }).lean();
|
||||
if (!instances.length) return;
|
||||
|
||||
await Promise.allSettled(
|
||||
instances.map((doc) =>
|
||||
new SandboxClient({
|
||||
sandboxId: doc.sandboxId
|
||||
}).delete()
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// 5 分钟检查一遍,暂停
|
||||
export const cronJob = async () => {
|
||||
setCron('*/5 * * * *', async () => {
|
||||
const instances = await MongoSandboxInstance.find({
|
||||
status: SandboxStatusEnum.running,
|
||||
lastActiveAt: { $lt: subMinutes(new Date(), SANDBOX_SUSPEND_MINUTES) }
|
||||
}).lean();
|
||||
if (!instances.length) return;
|
||||
|
||||
logger.info('Found running sandboxes inactive > 5 min', { count: instances.length });
|
||||
|
||||
await batchRun(instances, (doc) =>
|
||||
new SandboxClient({
|
||||
sandboxId: doc.sandboxId
|
||||
})
|
||||
.stop()
|
||||
.catch((error) => {
|
||||
logger.error('Failed to stop sandbox', { sandboxId: doc.sandboxId, error });
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export type { ISandbox } from '@fastgpt-sdk/sandbox-adapter';
|
||||
@@ -0,0 +1,67 @@
|
||||
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||
const { Schema } = connectionMongo;
|
||||
import type { SandboxInstanceSchemaType } from './type';
|
||||
import { SandboxStatusEnum } from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
import { AppCollectionName } from '../../app/schema';
|
||||
import { SandboxLimitSchema, SandboxProviderSchema } from './type';
|
||||
|
||||
export const collectionName = 'agent_sandbox_instances';
|
||||
|
||||
const SandboxInstanceSchema = new Schema({
|
||||
provider: {
|
||||
type: String,
|
||||
enum: SandboxProviderSchema.options,
|
||||
required: true
|
||||
},
|
||||
// 唯一 id,chat 模式下,由 3 个 id hash 获取。
|
||||
sandboxId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// Chat 模式下会关联会话。Skill editor 不需要 appId,userId,chatId
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: AppCollectionName
|
||||
},
|
||||
userId: String,
|
||||
chatId: String,
|
||||
|
||||
status: {
|
||||
type: String,
|
||||
enum: Object.values(SandboxStatusEnum),
|
||||
default: SandboxStatusEnum.running,
|
||||
required: true
|
||||
},
|
||||
lastActiveAt: {
|
||||
type: Date,
|
||||
default: () => new Date(),
|
||||
required: true
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
default: () => new Date(),
|
||||
required: true
|
||||
},
|
||||
limit: {
|
||||
type: SandboxLimitSchema.shape
|
||||
}
|
||||
});
|
||||
|
||||
SandboxInstanceSchema.index(
|
||||
{ appId: 1, userId: 1, chatId: 1 },
|
||||
{
|
||||
unique: true,
|
||||
partialFilterExpression: {
|
||||
appId: { $exists: true, $ne: null },
|
||||
userId: { $exists: true, $ne: null },
|
||||
chatId: { $exists: true, $ne: null }
|
||||
}
|
||||
}
|
||||
);
|
||||
SandboxInstanceSchema.index({ status: 1, lastActiveAt: 1 });
|
||||
SandboxInstanceSchema.index({ provider: 1, sandboxId: 1 }, { unique: true });
|
||||
|
||||
export const MongoSandboxInstance = getMongoModel<SandboxInstanceSchemaType>(
|
||||
collectionName,
|
||||
SandboxInstanceSchema
|
||||
);
|
||||
@@ -0,0 +1,26 @@
|
||||
import z from 'zod';
|
||||
import { SandboxStatusEnum } from '@fastgpt/global/core/ai/sandbox/constants';
|
||||
|
||||
// ---- 沙盒实例 DB 类型 ----
|
||||
export const SandboxProviderSchema = z.enum(['sealosdevbox']);
|
||||
export type SandboxProviderType = z.infer<typeof SandboxProviderSchema>;
|
||||
|
||||
export const SandboxLimitSchema = z.object({
|
||||
cpuCount: z.number(),
|
||||
memoryMiB: z.number(),
|
||||
diskGiB: z.number()
|
||||
});
|
||||
export const SandboxInstanceZodSchema = z.object({
|
||||
_id: z.string(),
|
||||
sandboxId: z.string(),
|
||||
appId: z.string().nullish(),
|
||||
userId: z.string().nullish(),
|
||||
chatId: z.string().nullish(),
|
||||
status: z.enum(SandboxStatusEnum),
|
||||
lastActiveAt: z.date(),
|
||||
createdAt: z.date(),
|
||||
limit: SandboxLimitSchema.nullish(),
|
||||
provider: SandboxProviderSchema
|
||||
});
|
||||
|
||||
export type SandboxInstanceSchemaType = z.infer<typeof SandboxInstanceZodSchema>;
|
||||
@@ -30,6 +30,7 @@ import { MongoMcpKey } from '../../support/mcp/schema';
|
||||
import { MongoAppRecord } from './record/schema';
|
||||
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||
import { getLogger, LogCategories } from '../../common/logger';
|
||||
import { deleteSandboxesByAppId } from '../ai/sandbox/controller';
|
||||
|
||||
const logger = getLogger(LogCategories.MODULE.APP.FOLDER);
|
||||
|
||||
@@ -152,7 +153,10 @@ export const deleteAppDataProcessor = async ({
|
||||
|
||||
// 1. 删除应用头像
|
||||
await removeImageByPath(app.avatar);
|
||||
|
||||
// 2. 删除聊天记录和S3文件
|
||||
// 删除沙盒实例
|
||||
await deleteSandboxesByAppId(appId);
|
||||
await getS3ChatSource().deleteChatFilesByPrefix({ appId });
|
||||
await MongoAppChatLog.deleteMany({ teamId, appId });
|
||||
await MongoChatItemResponse.deleteMany({ appId });
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { getWorker, QueueNames, getQueue, type Job } from '../../../common/bullmq';
|
||||
import { MongoDatasetCollection } from './schema';
|
||||
import { getLogger, LogCategories } from '../../../common/logger';
|
||||
|
||||
const logger = getLogger(LogCategories.MODULE.DATASET.COLLECTION);
|
||||
|
||||
export type CollectionUpdateJobData = {
|
||||
teamId: string;
|
||||
datasetId: string;
|
||||
collectionId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize Collection Update Worker
|
||||
* This worker handles collection updates (updateTime, etc.) with debounce mechanism
|
||||
*/
|
||||
export const initCollectionUpdateWorker = () => {
|
||||
const worker = getWorker<CollectionUpdateJobData>(
|
||||
QueueNames.collectionUpdate,
|
||||
async (job: Job<CollectionUpdateJobData>) => {
|
||||
const { collectionId } = job.data;
|
||||
|
||||
try {
|
||||
// Update collection updateTime and other operations
|
||||
await MongoDatasetCollection.updateOne(
|
||||
{
|
||||
_id: collectionId
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
updateTime: new Date()
|
||||
// TODO: 更新统计数据
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
logger.debug('Collection updated', {
|
||||
collectionId
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to update collection', {
|
||||
collectionId,
|
||||
error
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
{
|
||||
concurrency: 3, // Process 3 jobs concurrently
|
||||
removeOnComplete: {
|
||||
count: 0 // Remove completed jobs immediately
|
||||
},
|
||||
removeOnFail: {
|
||||
count: 1000, // Keep last 1000 failed jobs
|
||||
age: 30 * 24 * 60 * 60 // Keep failed jobs for 30 days (in seconds)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
logger.info('Collection Update worker initialized');
|
||||
return worker;
|
||||
};
|
||||
|
||||
/**
|
||||
* Push collection update job to queue with debounce
|
||||
* @param collectionId - Collection ID
|
||||
* @param datasetId - Dataset ID
|
||||
* @param teamId - Team ID
|
||||
* @param delay - Delay in milliseconds (default: 5000ms = 5s)
|
||||
*/
|
||||
export const pushCollectionUpdateJob = async (data: CollectionUpdateJobData) => {
|
||||
const queue = getQueue<CollectionUpdateJobData>(QueueNames.collectionUpdate);
|
||||
|
||||
// Use jobId to ensure only one job per collection (debounce mechanism)
|
||||
// If a job with the same jobId already exists, it will be replaced
|
||||
const jobId = `collection-update-${data.collectionId}`;
|
||||
|
||||
try {
|
||||
await queue.add('updateCollection', data, {
|
||||
jobId, // Unique job ID for debounce
|
||||
delay: 5000 // Delay execution by 5 seconds
|
||||
});
|
||||
|
||||
logger.debug('Collection update job pushed', {
|
||||
collectionId: data.collectionId
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to push collection update job', {
|
||||
collectionId: data.collectionId,
|
||||
error
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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