Skip to content
Merged

Dev #55

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions functions/functions_tools/BananaTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
resolveImageEndpoint,
callImageGenApi,
extractImageUrl,
normalizeImageSize,
} from '../../utils/api/imageGeneration.js';
import fs from "fs";
import YAML from "yaml";
Expand All @@ -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'],
Expand All @@ -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)不能为空。";

Expand All @@ -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 }],
Expand All @@ -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) {
Expand Down
15 changes: 12 additions & 3 deletions functions/functions_tools/GoogleImageEditTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
resolveImageEndpoint,
callImageGenApi,
extractImageUrl,
normalizeImageSize,
} from '../../utils/api/imageGeneration.js';
import fs from "fs";
import YAML from "yaml";
Expand All @@ -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'],
Expand All @@ -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";
Expand All @@ -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 }],
Expand All @@ -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) {
Expand Down
32 changes: 29 additions & 3 deletions utils/api/imageGeneration.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
*
Expand Down Expand Up @@ -210,11 +235,12 @@ export async function imagesToBase64(images) {
* @param {string} key API key
* @returns {Promise<string|null>} 图片 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' },
Expand All @@ -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)
Expand Down
Loading