feat: gate code agent runs server side
This commit is contained in:
@@ -20,7 +20,9 @@ H5_PUBLIC_BASE_URL=http://127.0.0.1:5173
|
|||||||
# MEMIND_RUNTIME_REDIS_NAMESPACE=memind:runtime
|
# MEMIND_RUNTIME_REDIS_NAMESPACE=memind:runtime
|
||||||
|
|
||||||
# Code-tool agent run 灰度开关(默认关闭)。
|
# Code-tool agent run 灰度开关(默认关闭)。
|
||||||
|
# 后端必须先开启 MEMIND_AGENT_CODE_RUNS_ENABLED,H5 构建开关才会生效。
|
||||||
# 开启后,页面编辑子聊天会传 tool_mode=code;普通聊天仍需同时开启 AUTODETECT 才会按文本自动识别代码任务。
|
# 开启后,页面编辑子聊天会传 tool_mode=code;普通聊天仍需同时开启 AUTODETECT 才会按文本自动识别代码任务。
|
||||||
|
# MEMIND_AGENT_CODE_RUNS_ENABLED=0
|
||||||
# VITE_AGENT_CODE_RUNS_ENABLED=0
|
# VITE_AGENT_CODE_RUNS_ENABLED=0
|
||||||
# VITE_AGENT_CODE_RUNS_AUTODETECT=0
|
# VITE_AGENT_CODE_RUNS_AUTODETECT=0
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,8 @@ const toolGrants = await queryToolGrants().catch((err) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const buildFlags = {
|
const buildFlags = {
|
||||||
|
serverCodeRunsEnabledEnv: process.env.MEMIND_AGENT_CODE_RUNS_ENABLED ?? null,
|
||||||
|
serverCodeRunsEnabledTruthy: truthy(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED),
|
||||||
enabledEnv: process.env.VITE_AGENT_CODE_RUNS_ENABLED ?? null,
|
enabledEnv: process.env.VITE_AGENT_CODE_RUNS_ENABLED ?? null,
|
||||||
autodetectEnv: process.env.VITE_AGENT_CODE_RUNS_AUTODETECT ?? null,
|
autodetectEnv: process.env.VITE_AGENT_CODE_RUNS_AUTODETECT ?? null,
|
||||||
enabledTruthy: truthy(process.env.VITE_AGENT_CODE_RUNS_ENABLED),
|
enabledTruthy: truthy(process.env.VITE_AGENT_CODE_RUNS_ENABLED),
|
||||||
@@ -157,6 +159,7 @@ const ok = Boolean(
|
|||||||
runtimePolicy.defaultMode === 'chat' &&
|
runtimePolicy.defaultMode === 'chat' &&
|
||||||
runtimePolicy.codeToolMode === 'code' &&
|
runtimePolicy.codeToolMode === 'code' &&
|
||||||
runtimePolicy.chatInjectsCodeTools === false &&
|
runtimePolicy.chatInjectsCodeTools === false &&
|
||||||
|
runtimePolicy.codeRunsEnabled === buildFlags.serverCodeRunsEnabledTruthy &&
|
||||||
roleDefaults.aider === false &&
|
roleDefaults.aider === false &&
|
||||||
roleDefaults.openhands === false &&
|
roleDefaults.openhands === false &&
|
||||||
(toolGrants.sampledUsers ?? []).every((user) => user.chatHasCodeTools === false) &&
|
(toolGrants.sampledUsers ?? []).every((user) => user.chatHasCodeTools === false) &&
|
||||||
|
|||||||
@@ -6744,7 +6744,14 @@ function createAgentRunGateway({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// agent-run-routes.mjs
|
// agent-run-routes.mjs
|
||||||
function createPostAgentRunsHandler({ userAuth: userAuth2, agentRunGateway: agentRunGateway2 }) {
|
function envFlag(value) {
|
||||||
|
return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
|
||||||
|
}
|
||||||
|
function createPostAgentRunsHandler({
|
||||||
|
userAuth: userAuth2,
|
||||||
|
agentRunGateway: agentRunGateway2,
|
||||||
|
codeRunsEnabled = envFlag(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED)
|
||||||
|
}) {
|
||||||
return async function postAgentRuns(request, response) {
|
return async function postAgentRuns(request, response) {
|
||||||
try {
|
try {
|
||||||
const sessionId = String(request.body?.session_id ?? "").trim() || null;
|
const sessionId = String(request.body?.session_id ?? "").trim() || null;
|
||||||
@@ -6769,6 +6776,10 @@ function createPostAgentRunsHandler({ userAuth: userAuth2, agentRunGateway: agen
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (toolMode === "code" && !codeRunsEnabled) {
|
||||||
|
response.status(403).json({ message: "\u4EE3\u7801\u4EFB\u52A1\u7070\u5EA6\u672A\u5F00\u542F" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
const owns = await userAuth2.ownsSession(request.currentUser.id, sessionId);
|
const owns = await userAuth2.ownsSession(request.currentUser.id, sessionId);
|
||||||
if (!owns) {
|
if (!owns) {
|
||||||
@@ -9338,6 +9349,9 @@ function createTkmindProxy({
|
|||||||
defaultMode: "chat",
|
defaultMode: "chat",
|
||||||
codeToolMode: "code",
|
codeToolMode: "code",
|
||||||
chatInjectsCodeTools: false,
|
chatInjectsCodeTools: false,
|
||||||
|
codeRunsEnabled: ["1", "true", "yes", "on"].includes(
|
||||||
|
String(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED ?? "").trim().toLowerCase()
|
||||||
|
),
|
||||||
aiderTimeoutMs: Number(process.env.MEMIND_AIDER_TIMEOUT_MS ?? 6e5),
|
aiderTimeoutMs: Number(process.env.MEMIND_AIDER_TIMEOUT_MS ?? 6e5),
|
||||||
openhandsTimeoutMs: Number(process.env.MEMIND_OPENHANDS_TIMEOUT_MS ?? 9e5)
|
openhandsTimeoutMs: Number(process.env.MEMIND_OPENHANDS_TIMEOUT_MS ?? 9e5)
|
||||||
},
|
},
|
||||||
|
|||||||
+13
-1
@@ -1,6 +1,14 @@
|
|||||||
import { normalizeAgentRunToolMode } from './agent-run-gateway.mjs';
|
import { normalizeAgentRunToolMode } from './agent-run-gateway.mjs';
|
||||||
|
|
||||||
export function createPostAgentRunsHandler({ userAuth, agentRunGateway }) {
|
function envFlag(value) {
|
||||||
|
return ['1', 'true', 'yes', 'on'].includes(String(value ?? '').trim().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPostAgentRunsHandler({
|
||||||
|
userAuth,
|
||||||
|
agentRunGateway,
|
||||||
|
codeRunsEnabled = envFlag(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED),
|
||||||
|
}) {
|
||||||
return async function postAgentRuns(request, response) {
|
return async function postAgentRuns(request, response) {
|
||||||
try {
|
try {
|
||||||
const sessionId = String(request.body?.session_id ?? '').trim() || null;
|
const sessionId = String(request.body?.session_id ?? '').trim() || null;
|
||||||
@@ -25,6 +33,10 @@ export function createPostAgentRunsHandler({ userAuth, agentRunGateway }) {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (toolMode === 'code' && !codeRunsEnabled) {
|
||||||
|
response.status(403).json({ message: '代码任务灰度未开启' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
const owns = await userAuth.ownsSession(request.currentUser.id, sessionId);
|
const owns = await userAuth.ownsSession(request.currentUser.id, sessionId);
|
||||||
if (!owns) {
|
if (!owns) {
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ test('POST /agent/runs accepts explicit code tool mode and task type', async ()
|
|||||||
return { id: 'run-code', status: 'queued' };
|
return { id: 'run-code', status: 'queued' };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
codeRunsEnabled: true,
|
||||||
});
|
});
|
||||||
const res = createResponseRecorder();
|
const res = createResponseRecorder();
|
||||||
|
|
||||||
@@ -141,6 +142,39 @@ test('POST /agent/runs accepts explicit code tool mode and task type', async ()
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('POST /agent/runs rejects code tool mode when server gate is disabled', async () => {
|
||||||
|
const handler = createPostAgentRunsHandler({
|
||||||
|
userAuth: {
|
||||||
|
async ownsSession() {
|
||||||
|
throw new Error('should not check ownership after disabled code mode');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
agentRunGateway: {
|
||||||
|
async createRun() {
|
||||||
|
throw new Error('should not be called');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
codeRunsEnabled: false,
|
||||||
|
});
|
||||||
|
const res = createResponseRecorder();
|
||||||
|
|
||||||
|
await handler(
|
||||||
|
{
|
||||||
|
currentUser: { id: 'user-1' },
|
||||||
|
body: {
|
||||||
|
session_id: 'session-1',
|
||||||
|
request_id: 'req-code',
|
||||||
|
user_message: { role: 'user', content: [] },
|
||||||
|
tool_mode: 'code',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(res.statusCode, 403);
|
||||||
|
assert.deepEqual(res.body, { message: '代码任务灰度未开启' });
|
||||||
|
});
|
||||||
|
|
||||||
test('POST /agent/runs rejects unsupported tool mode', async () => {
|
test('POST /agent/runs rejects unsupported tool mode', async () => {
|
||||||
const handler = createPostAgentRunsHandler({
|
const handler = createPostAgentRunsHandler({
|
||||||
userAuth: {
|
userAuth: {
|
||||||
|
|||||||
@@ -140,6 +140,8 @@ const toolGrants = await queryToolGrants().catch((err) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const buildFlags = {
|
const buildFlags = {
|
||||||
|
serverCodeRunsEnabledEnv: process.env.MEMIND_AGENT_CODE_RUNS_ENABLED ?? null,
|
||||||
|
serverCodeRunsEnabledTruthy: truthy(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED),
|
||||||
enabledEnv: process.env.VITE_AGENT_CODE_RUNS_ENABLED ?? null,
|
enabledEnv: process.env.VITE_AGENT_CODE_RUNS_ENABLED ?? null,
|
||||||
autodetectEnv: process.env.VITE_AGENT_CODE_RUNS_AUTODETECT ?? null,
|
autodetectEnv: process.env.VITE_AGENT_CODE_RUNS_AUTODETECT ?? null,
|
||||||
enabledTruthy: truthy(process.env.VITE_AGENT_CODE_RUNS_ENABLED),
|
enabledTruthy: truthy(process.env.VITE_AGENT_CODE_RUNS_ENABLED),
|
||||||
@@ -157,6 +159,7 @@ const ok = Boolean(
|
|||||||
runtimePolicy.defaultMode === 'chat' &&
|
runtimePolicy.defaultMode === 'chat' &&
|
||||||
runtimePolicy.codeToolMode === 'code' &&
|
runtimePolicy.codeToolMode === 'code' &&
|
||||||
runtimePolicy.chatInjectsCodeTools === false &&
|
runtimePolicy.chatInjectsCodeTools === false &&
|
||||||
|
runtimePolicy.codeRunsEnabled === buildFlags.serverCodeRunsEnabledTruthy &&
|
||||||
roleDefaults.aider === false &&
|
roleDefaults.aider === false &&
|
||||||
roleDefaults.openhands === false &&
|
roleDefaults.openhands === false &&
|
||||||
(toolGrants.sampledUsers ?? []).every((user) => user.chatHasCodeTools === false) &&
|
(toolGrants.sampledUsers ?? []).every((user) => user.chatHasCodeTools === false) &&
|
||||||
|
|||||||
@@ -912,6 +912,9 @@ export function createTkmindProxy({
|
|||||||
defaultMode: 'chat',
|
defaultMode: 'chat',
|
||||||
codeToolMode: 'code',
|
codeToolMode: 'code',
|
||||||
chatInjectsCodeTools: false,
|
chatInjectsCodeTools: false,
|
||||||
|
codeRunsEnabled: ['1', 'true', 'yes', 'on'].includes(
|
||||||
|
String(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED ?? '').trim().toLowerCase(),
|
||||||
|
),
|
||||||
aiderTimeoutMs: Number(process.env.MEMIND_AIDER_TIMEOUT_MS ?? 600_000),
|
aiderTimeoutMs: Number(process.env.MEMIND_AIDER_TIMEOUT_MS ?? 600_000),
|
||||||
openhandsTimeoutMs: Number(process.env.MEMIND_OPENHANDS_TIMEOUT_MS ?? 900_000),
|
openhandsTimeoutMs: Number(process.env.MEMIND_OPENHANDS_TIMEOUT_MS ?? 900_000),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user