feat: integrate image_make with MindSpace assets

This commit is contained in:
john
2026-07-19 18:34:50 +08:00
parent 2f4366e882
commit 5b88f8ece5
14 changed files with 768 additions and 14 deletions
+88 -9
View File
@@ -12,7 +12,7 @@ export const ASSET_PLUGIN_CATALOG = [
id: 'asset-generate',
label: '图片生成',
description: '异步生成页面所需图片;关闭或失败时必须降级为无图页面。',
providers: ['flux-schnell', 'comfyui'],
providers: ['image_make', 'flux-schnell', 'comfyui'],
supportsLlm: true,
},
{
@@ -31,6 +31,18 @@ export const ASSET_PLUGIN_CATALOG = [
},
];
export const IMAGE_MAKE_PURPOSE_CATALOG = [
{ id: 'inline_image', label: '页面正文图片', presetId: 'memind_square_illustration' },
{ id: 'hero', label: '页面头图', presetId: 'memind_dark_hero' },
{ id: 'card_cover', label: '卡片封面', presetId: 'memind_card_cover' },
{ id: 'feed_cover', label: '信息流与缩略图源图', presetId: 'memind_feed_cover_source' },
];
const imageMakePurposeIds = new Set(IMAGE_MAKE_PURPOSE_CATALOG.map((purpose) => purpose.id));
const disabledImageMakePurposes = Object.freeze(
Object.fromEntries(IMAGE_MAKE_PURPOSE_CATALOG.map((purpose) => [purpose.id, false])),
);
const pluginById = new Map(ASSET_PLUGIN_CATALOG.map((plugin) => [plugin.id, plugin]));
function normalizePluginId(value) {
@@ -48,9 +60,34 @@ function toBoolean(value, defaultValue = false) {
return Boolean(value);
}
function parseJsonObject(value) {
if (!value) return {};
if (typeof value === 'object' && !Array.isArray(value)) return value;
try {
const parsed = JSON.parse(String(value));
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
} catch {
return {};
}
}
function normalizeImageMakePurposes(value, fallback = disabledImageMakePurposes) {
const raw = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
return Object.fromEntries(
IMAGE_MAKE_PURPOSE_CATALOG.map((purpose) => [
purpose.id,
toBoolean(raw[purpose.id], Boolean(fallback?.[purpose.id])),
]),
);
}
function rowImageMakePurposes(row) {
return normalizeImageMakePurposes(parseJsonObject(row?.config_json)?.purposes);
}
function rowToPluginConfig(row, providerKey = null) {
const plugin = pluginById.get(row.plugin_id);
return {
const config = {
pluginId: row.plugin_id,
label: plugin?.label ?? row.plugin_id,
description: plugin?.description ?? '',
@@ -64,6 +101,11 @@ function rowToPluginConfig(row, providerKey = null) {
llmModel: row.llm_model ?? null,
updatedAt: Number(row.updated_at ?? 0) || null,
};
if (row.plugin_id === 'asset-generate') {
config.purposes = rowImageMakePurposes(row);
config.purposeCatalog = IMAGE_MAKE_PURPOSE_CATALOG;
}
return config;
}
/**
@@ -140,6 +182,12 @@ export function createAssetGatewayConfigService(pool, { llmProviderService = nul
llmProviderId: null,
llmModel: null,
updatedAt: null,
...(plugin.id === 'asset-generate'
? {
purposes: { ...disabledImageMakePurposes },
purposeCatalog: IMAGE_MAKE_PURPOSE_CATALOG,
}
: {}),
};
}),
};
@@ -171,26 +219,36 @@ export function createAssetGatewayConfigService(pool, { llmProviderService = nul
const provider = normalizeProvider(plugin, payload.provider);
if (enabled && !provider) return { ok: false, message: '请选择该插件支持的 Provider' };
const keyId = String(payload.llmProviderKeyId ?? payload.keyId ?? '').trim() || null;
const model = String(payload.llmModel ?? payload.model ?? '').trim() || null;
const usesModelCenter = !(pluginId === 'asset-generate' && provider === 'image_make');
const keyId = usesModelCenter
? String(payload.llmProviderKeyId ?? payload.keyId ?? '').trim() || null
: null;
const model = usesModelCenter
? String(payload.llmModel ?? payload.model ?? '').trim() || null
: null;
const binding = await ensureLlmBinding({ plugin, keyId, model, enabled });
if (!binding.ok) return binding;
const existing = await getRow(pluginId);
const now = Date.now();
const purposeFallback = rowImageMakePurposes(existing);
const purposes = pluginId === 'asset-generate'
? normalizeImageMakePurposes(payload.purposes, purposeFallback)
: null;
const configJson = purposes ? JSON.stringify({ purposes }) : null;
if (existing) {
await pool.query(
`UPDATE h5_asset_plugin_configs
SET enabled = ?, provider = ?, llm_provider_key_id = ?, llm_model = ?, updated_by = ?, updated_at = ?
SET enabled = ?, provider = ?, llm_provider_key_id = ?, llm_model = ?, config_json = ?, updated_by = ?, updated_at = ?
WHERE plugin_id = ?`,
[enabled ? 1 : 0, provider, keyId, model, updatedBy, now, pluginId],
[enabled ? 1 : 0, provider, keyId, model, configJson, updatedBy, now, pluginId],
);
} else {
await pool.query(
`INSERT INTO h5_asset_plugin_configs
(id, plugin_id, enabled, provider, llm_provider_key_id, llm_model, updated_by, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[crypto.randomUUID(), pluginId, enabled ? 1 : 0, provider, keyId, model, updatedBy, now, now],
(id, plugin_id, enabled, provider, llm_provider_key_id, llm_model, config_json, updated_by, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[crypto.randomUUID(), pluginId, enabled ? 1 : 0, provider, keyId, model, configJson, updatedBy, now, now],
);
}
return { ok: true, config: await this.getConfig() };
@@ -209,5 +267,26 @@ export function createAssetGatewayConfigService(pool, { llmProviderService = nul
}
return { ok: true, plugin: rowToPluginConfig(row) };
},
async resolveImageGenerationPurpose(rawPurpose) {
const purpose = String(rawPurpose ?? '').trim().toLowerCase();
if (!imageMakePurposeIds.has(purpose)) {
return { ok: false, code: 'unsupported_purpose', message: '不支持的图片生成用途' };
}
const resolved = await this.resolvePlugin('asset-generate');
if (!resolved.ok) return resolved;
if (resolved.plugin.provider !== 'image_make') {
return { ok: false, code: 'provider_not_image_make', message: 'image_make Provider 未启用' };
}
if (!resolved.plugin.purposes?.[purpose]) {
return { ok: false, code: 'purpose_disabled', message: '该图片用途未启用,可安全降级到原有流程' };
}
return {
ok: true,
purpose,
presetId: IMAGE_MAKE_PURPOSE_CATALOG.find((item) => item.id === purpose)?.presetId,
plugin: resolved.plugin,
};
},
};
}