feat: expose image generation to page agents
This commit is contained in:
@@ -40,6 +40,8 @@ const PRIVATE_DATA_MAX_BYTES = Number(process.env.PRIVATE_DATA_MAX_BYTES ?? 20 *
|
||||
const PRIVATE_DATA_QUERY_TIMEOUT_MS = Number(process.env.PRIVATE_DATA_QUERY_TIMEOUT_MS ?? 5000);
|
||||
const PRIVATE_DATA_MAX_ROWS = Number(process.env.PRIVATE_DATA_MAX_ROWS ?? 200);
|
||||
const PRIVATE_DATA_USER_ID = process.env.PRIVATE_DATA_USER_ID?.trim();
|
||||
const AGENT_API_BASE_URL = process.env.MINDSPACE_AGENT_API_BASE_URL?.trim();
|
||||
const INTERNAL_AGENT_SECRET = process.env.MINDSPACE_INTERNAL_AGENT_SECRET?.trim();
|
||||
|
||||
const allowedToolsEnv = process.env.ALLOWED_TOOLS?.trim();
|
||||
const ALLOWED_TOOLS = allowedToolsEnv ? new Set(allowedToolsEnv.split(',').map((s) => s.trim())) : null;
|
||||
@@ -121,6 +123,39 @@ function runGenerateDocxScript({ outputPath, title, sections }) {
|
||||
return { bytes: size, stdout: String(stdout ?? '').trim() };
|
||||
}
|
||||
|
||||
async function generateMindSpaceImage({ purpose, prompt, negativePrompt, idempotencyKey }) {
|
||||
if (!AGENT_API_BASE_URL || !INTERNAL_AGENT_SECRET || !PRIVATE_DATA_USER_ID) {
|
||||
throw new Error('generate_image: Memind 内部生图入口未配置');
|
||||
}
|
||||
const endpoint = new URL('agent/mindspace_image_generate', `${AGENT_API_BASE_URL.replace(/\/+$/, '')}/`);
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
authorization: `Bearer ${INTERNAL_AGENT_SECRET}`,
|
||||
'content-type': 'application/json',
|
||||
...(idempotencyKey ? { 'idempotency-key': idempotencyKey } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: PRIVATE_DATA_USER_ID,
|
||||
purpose,
|
||||
prompt,
|
||||
negative_prompt: negativePrompt,
|
||||
}),
|
||||
signal: AbortSignal.timeout(Number(process.env.IMAGE_MAKE_GENERATION_TIMEOUT_MS ?? 600_000)),
|
||||
});
|
||||
let body = null;
|
||||
try {
|
||||
body = await response.json();
|
||||
} catch {
|
||||
// Safe error below intentionally excludes the response body.
|
||||
}
|
||||
if (!response.ok && !body?.fallback) {
|
||||
throw new Error(`generate_image: Memind 返回 ${response.status}`);
|
||||
}
|
||||
return body?.data ?? body ?? { ok: false, fallback: true, code: 'empty_response' };
|
||||
}
|
||||
|
||||
const ALL_TOOLS = [
|
||||
{
|
||||
name: 'read_file',
|
||||
@@ -195,6 +230,25 @@ const ALL_TOOLS = [
|
||||
required: ['html_path'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'generate_image',
|
||||
description:
|
||||
'通过平台 image_make 服务生成页面图片并直接写入当前用户 MindSpace。仅在后台开关开启时生效;失败时继续原页面流程。返回 canonical publicUrl 与 workspaceRelativePath。',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
purpose: {
|
||||
type: 'string',
|
||||
enum: ['inline_image', 'hero', 'card_cover', 'feed_cover'],
|
||||
description: '图片用途',
|
||||
},
|
||||
prompt: { type: 'string', description: '图片描述,画面内不要包含文字、水印或二维码' },
|
||||
negative_prompt: { type: 'string', description: '可选负面描述' },
|
||||
idempotency_key: { type: 'string', description: '可选幂等键;同一页面同一用途应复用' },
|
||||
},
|
||||
required: ['purpose', 'prompt'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'generate_docx',
|
||||
description:
|
||||
@@ -534,6 +588,18 @@ async function callTool(name, args) {
|
||||
fs.mkdirSync(abs, { recursive: true });
|
||||
return [{ type: 'text', text: `已创建目录 ${args.path}` }];
|
||||
}
|
||||
case 'generate_image': {
|
||||
const purpose = String(args.purpose ?? '').trim();
|
||||
const prompt = String(args.prompt ?? '').trim();
|
||||
if (!prompt) throw new Error('generate_image: prompt 不能为空');
|
||||
const result = await generateMindSpaceImage({
|
||||
purpose,
|
||||
prompt,
|
||||
negativePrompt: String(args.negative_prompt ?? '').trim(),
|
||||
idempotencyKey: String(args.idempotency_key ?? '').trim(),
|
||||
});
|
||||
return [{ type: 'text', text: JSON.stringify(result, null, 2) }];
|
||||
}
|
||||
case 'generate_long_image': {
|
||||
const htmlPath = String(args.html_path ?? args.path ?? '').trim();
|
||||
if (!htmlPath.toLowerCase().endsWith('.html')) {
|
||||
|
||||
Reference in New Issue
Block a user