feat: integrate image_make with MindSpace assets
This commit is contained in:
+88
-9
@@ -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,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user