From 3b507730fb62044c4663890ee03b8fa2d51e8bb7 Mon Sep 17 00:00:00 2001 From: devilsity <245284070+l56407258-netizen@users.noreply.github.com> Date: Mon, 10 Aug 2026 11:39:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BE=E7=89=87=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=B0=BA=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/functions_tools/BananaTool.js | 15 +++++++-- .../functions_tools/GoogleImageEditTool.js | 15 +++++++-- utils/api/imageGeneration.js | 32 +++++++++++++++++-- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/functions/functions_tools/BananaTool.js b/functions/functions_tools/BananaTool.js index bdc7543..e2b4103 100644 --- a/functions/functions_tools/BananaTool.js +++ b/functions/functions_tools/BananaTool.js @@ -6,6 +6,7 @@ import { resolveImageEndpoint, callImageGenApi, extractImageUrl, + normalizeImageSize, } from '../../utils/api/imageGeneration.js'; import fs from "fs"; import YAML from "yaml"; @@ -31,6 +32,10 @@ export class BananaTool extends AbstractTool { type: 'array', description: '用户提供的图片链接数组,需保留原始URL完整性', items: { type: 'string' } + }, + size: { + type: 'string', + description: '生成图片的尺寸或比例,例如 1024x1024、1536x864、16:9、9:16、4:3、1:1、横图、竖图、方图', } }, required: ['prompt'], @@ -40,7 +45,7 @@ export class BananaTool extends AbstractTool { async func(opts, e) { const config = this.loadConfig(); - const { prompt, images: rawImages } = opts; + const { prompt, images: rawImages, size: rawSize } = opts; if (!prompt) return "错误:绘图提示词(prompt)不能为空。"; @@ -58,7 +63,11 @@ export class BananaTool extends AbstractTool { try { if (endpoint.type === 'chat') { // chat/completions 模式:多模态 messages 走 callAI(保持原行为) - const imgurls = await this.buildImageMessages(prompt, images); + const sizeHint = normalizeImageSize(rawSize) + const imgurls = await this.buildImageMessages( + sizeHint === normalizeImageSize() ? prompt : `${prompt}\n[图片尺寸: ${sizeHint}]`, + images + ); const result = await callAI( { url: finalUrl, model: finalModel, apikey: finalKey }, [{ role: "user", content: imgurls }], @@ -79,7 +88,7 @@ export class BananaTool extends AbstractTool { processedUrl = extractImageUrl(imageUrl); } else { // responses / images(edits|generations) 模式 - processedUrl = await callImageGenApi(endpoint, prompt, images, finalModel, finalKey); + processedUrl = await callImageGenApi(endpoint, prompt, images, finalModel, finalKey, rawSize); } if (processedUrl) { diff --git a/functions/functions_tools/GoogleImageEditTool.js b/functions/functions_tools/GoogleImageEditTool.js index 62f498d..fc61cf8 100644 --- a/functions/functions_tools/GoogleImageEditTool.js +++ b/functions/functions_tools/GoogleImageEditTool.js @@ -6,6 +6,7 @@ import { resolveImageEndpoint, callImageGenApi, extractImageUrl, + normalizeImageSize, } from '../../utils/api/imageGeneration.js'; import fs from "fs"; import YAML from "yaml"; @@ -29,6 +30,10 @@ export class GoogleImageEditTool extends AbstractTool { type: 'array', description: '用户提供的图片链接数组,需保留原始URL完整性。QQ头像格式:"https://q1.qlogo.cn/g?b=qq&nk=用户QQ号&s=640"', items: { type: 'string' } + }, + size: { + type: 'string', + description: '生成图片的尺寸或比例,例如 1024x1024、1536x864、16:9、9:16、4:3、1:1、横图、竖图、方图', } }, required: ['prompt', 'images'], @@ -39,7 +44,7 @@ export class GoogleImageEditTool extends AbstractTool { async func(opts, e) { try { const config = this.loadConfig(); - const { prompt } = opts; + const { prompt, size: rawSize } = opts; const { imageEditApiUrl, imageEditApiKey, imageEditApiModel } = config.imageEditAiConfig || {}; const apiUrl = imageEditApiUrl || 'https://api.openai.com/v1/chat/completions'; const apiModel = imageEditApiModel || "gemini-3-pro-image-preview"; @@ -57,7 +62,11 @@ export class GoogleImageEditTool extends AbstractTool { if (endpoint.type === 'chat') { // chat/completions 模式:多模态 messages 走 callAI(保持原行为) - const content = await this.buildImageMessages(prompt, images); + const sizeHint = normalizeImageSize(rawSize) + const content = await this.buildImageMessages( + sizeHint === normalizeImageSize() ? prompt : `${prompt}\n[图片尺寸: ${sizeHint}]`, + images + ); const result = await callAI( { url: apiUrl, model: apiModel, apikey: apiKey }, [{ role: "user", content }], @@ -79,7 +88,7 @@ export class GoogleImageEditTool extends AbstractTool { processedUrl = extractImageUrl(imageUrl); } else { // responses / images(edits) 模式 - processedUrl = await callImageGenApi(endpoint, prompt, images, apiModel, apiKey); + processedUrl = await callImageGenApi(endpoint, prompt, images, apiModel, apiKey, rawSize); } if (processedUrl) { diff --git a/utils/api/imageGeneration.js b/utils/api/imageGeneration.js index 4e924e5..57f4a8d 100644 --- a/utils/api/imageGeneration.js +++ b/utils/api/imageGeneration.js @@ -17,6 +17,31 @@ import { getBase64Image } from '../fileUtils.js' // 注:实际是否生效取决于服务商和模型;不支持的会被服务商忽略 export const DEFAULT_IMAGE_SIZE = '1024x1024' +/** + * 把用户/模型传入的尺寸或比例归一化成 API 可用尺寸 + * @param {string} size + * @returns {string} + */ +export function normalizeImageSize(size) { + if (!size) return DEFAULT_IMAGE_SIZE + const s = String(size).trim().toLowerCase().replace(/[x×*]/g, 'x') + if (s === 'auto') return s + const alias = { + '1:1': '1024x1024', + '16:9': '1536x864', + '9:16': '864x1536', + '4:3': '1152x864', + '3:4': '864x1152', + '横图': '1536x864', + '竖图': '864x1536', + '方图': '1024x1024', + } + if (alias[s]) return alias[s] + const wh = s.match(/^(\d{2,5})x(\d{2,5})$/) + if (wh) return `${wh[1]}x${wh[2]}` + return DEFAULT_IMAGE_SIZE +} + /** * 根据 URL 后缀识别 endpoint 类型(行为等价于手办化.js#resolveEndpoint) * @@ -210,11 +235,12 @@ export async function imagesToBase64(images) { * @param {string} key API key * @returns {Promise} 图片 URL(http(s) 或 base64://...),未找到返回 null */ -export async function callImageGenApi(endpoint, prompt, images, model, key) { +export async function callImageGenApi(endpoint, prompt, images, model, key, size = DEFAULT_IMAGE_SIZE) { const base64Images = await imagesToBase64(images) + const targetSize = normalizeImageSize(size) if (endpoint.type === 'responses') { - const payload = buildResponsesPayload(prompt, base64Images, model, DEFAULT_IMAGE_SIZE) + const payload = buildResponsesPayload(prompt, base64Images, model, targetSize) const data = await postImageApi( endpoint.url, { 'Content-Type': 'application/json' }, @@ -232,7 +258,7 @@ export async function callImageGenApi(endpoint, prompt, images, model, key) { prompt, base64Images, model, - DEFAULT_IMAGE_SIZE, + targetSize, ) const data = await postImageApi(endpoint.url, headers, body, key) const url = parseImagesApiResult(data)