From f1215fc32e9ebdf46831302bfe3c45b1a6499b06 Mon Sep 17 00:00:00 2001 From: Jerryplusy Date: Sat, 6 Dec 2025 23:32:53 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(config/ai.json):=20add=20image?= =?UTF-8?q?Mode=20for=20selecting=20image=20generation=20method=20?= =?UTF-8?q?=E2=9C=A8=20feat(lib/ai/imageProcessor.js):=20implement=20image?= =?UTF-8?q?=20generation=20via=20OpenAI=20and=20chat=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/ai.json | 2 + lib/ai/imageProcessor.js | 132 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 127 insertions(+), 7 deletions(-) diff --git a/config/ai.json b/config/ai.json index 7e45614..8e9de02 100644 --- a/config/ai.json +++ b/config/ai.json @@ -77,6 +77,8 @@ "imageConfig": { "?enabled": "是否启用图像生成功能", "enabled": true, + "?imageMode": "图像生成模式: 'openai'使用/v1/images/generations接口, 'chat'使用对话式生图模型(如gemini-3-pro-image-preview)", + "imageMode": "openai", "?model": "图像生成模型名称(支持gemini-3-pro-image-preview等)", "model": "gemini-3-pro-image-preview", "?baseApi": "图像生成API基础地址(不加v1后面的)", diff --git a/lib/ai/imageProcessor.js b/lib/ai/imageProcessor.js index 1700a71..077fb4c 100644 --- a/lib/ai/imageProcessor.js +++ b/lib/ai/imageProcessor.js @@ -50,18 +50,40 @@ class ImageProcessor { } /** - * 生成图像 - 使用OpenAI标准接口 + * 生成图像 - 根据imageMode选择不同的调用方式 * @param {string} prompt - 图像描述 * @param {Object} config - 配置对象 * @returns {Promise} 生成结果 */ async generateImage(prompt, config) { try { - logger.info(`[crystelf-ai] 开始生成图像: ${prompt}`); + if (config.imageMode === 'chat') { + return await this.generateImageByChat(prompt, config); + } else { + return await this.generateImageByOpenAI(prompt, config); + } + } catch (error) { + logger.error(`[crystelf-ai] 图像生成失败: ${error.message}`); + return { + success: false, + error: `图像生成失败: ${error.message}` + }; + } + } + + /** + * 使用OpenAI标准接口生成图像 + * @param {string} prompt - 图像描述 + * @param {Object} config - 配置对象 + * @returns {Promise} 生成结果 + */ + async generateImageByOpenAI(prompt, config) { + try { + logger.info(`[crystelf-ai] 使用OpenAI接口生成图像: ${prompt}`); const requestBody = { prompt: prompt, - model: config.model || 'gemini-3-pro-image-preview', + model: config.model || 'dall-e-3', n: config.n || 1, size: config.size || '1024x1024', quality: config.quality || 'standard', @@ -86,13 +108,13 @@ class ImageProcessor { const imageData = response.data.data[0]; const imageUrl = imageData.url || imageData.b64_json; - logger.info(`[crystelf-ai] 图像生成成功: ${imageUrl ? 'URL' : 'Base64数据'}`); + logger.info(`[crystelf-ai] OpenAI接口图像生成成功: ${imageUrl ? 'URL' : 'Base64数据'}`); return { success: true, imageUrl: imageUrl, revisedPrompt: imageData.revised_prompt, description: prompt, - model: config.model || 'gemini-3-pro-image-preview', + model: config.model || 'Qwen/Qwen-Image', rawResponse: response.data }; } else { @@ -103,10 +125,106 @@ class ImageProcessor { }; } } catch (error) { - logger.error(`[crystelf-ai] 图像生成失败: ${error.message}`); + logger.error(`[crystelf-ai] OpenAI接口图像生成失败: ${error.message}`); return { success: false, - error: `图像生成失败: ${error.message}` + error: `OpenAI接口图像生成失败: ${error.message}` + }; + } + } + + /** + * 使用对话式接口生成图像(如gemini-3-pro-image-preview) + * @param {string} prompt - 图像描述 + * @param {Object} config - 配置对象 + * @returns {Promise} 生成结果 + */ + async generateImageByChat(prompt, config) { + try { + logger.info(`[crystelf-ai] 使用对话接口生成图像: ${prompt}`); + const messages = [ + { + role: 'system', + content: '请你根据用户的描述生成高质量且准确的图像,条件允许的情况下,请先思考用户的意图再生成图像,请直接返回图像url,不要任何其他内容' + }, + { + role: 'user', + content: prompt + } + ]; + const requestBody = { + model: config.model || 'google/gemini-3-pro-image-preview', + messages: messages, + max_tokens: config.maxTokens || 4000, + temperature: config.temperature || 0.7, + modalities: config.modalities || ['text', 'image'], + size: config.size || '1024x1024', + response_format: config.responseFormat || 'url' + }; + + const response = await axios.post( + `${config.baseApi}/v1/chat/completions`, + requestBody, + { + headers: { + 'Authorization': `Bearer ${config.apiKey}`, + 'Content-Type': 'application/json' + }, + timeout: config.timeout || 60000 + } + ); + + if (response.data && response.data.choices && response.data.choices.length > 0) { + const choice = response.data.choices[0]; + if (choice.message && choice.message.images && choice.message.images.length > 0) { + const imageData = choice.message.images[0]; + const imageUrl = imageData.image_url ? imageData.image_url.url : null; + + if (imageUrl) { + logger.info(`[crystelf-ai] 对话接口图像生成成功: ${imageUrl.substring(0, 50)}...`); + return { + success: true, + imageUrl: imageUrl, + description: prompt, + model: config.model || 'google/gemini-3-pro-image-preview', + rawResponse: response.data + }; + } + } + if (choice.message && choice.message.content) { + const imageUrl = this.extractImageUrl(choice.message.content); + if (imageUrl) { + logger.info(`[crystelf-ai] 从响应内容中提取到图像URL: ${imageUrl}`); + return { + success: true, + imageUrl: imageUrl, + description: prompt, + model: config.model || 'google/gemini-3-pro-image-preview', + rawResponse: response.data + }; + } else { + logger.info(`[crystelf-ai] 收到文本响应: ${choice.message.content}`); + return { + success: true, + response: choice.message.content, + description: prompt, + model: config.model || 'google/gemini-3-pro-image-preview', + rawResponse: response.data + }; + } + } + } else { + logger.error(`[crystelf-ai] 无效的API响应格式: ${JSON.stringify(response.data)}`); + return { + success: false, + error: '无效的API响应格式' + }; + } + } catch (error) { + logger.error(`[crystelf-ai] 对话接口图像生成失败: ${error.message}`); + return { + success: false, + error: `对话接口图像生成失败: ${error.message}` }; } }