diff --git a/lib/ai/providers/openai-image.ts b/lib/ai/providers/openai-image.ts index 7c13ffc05..ca0a350b5 100644 --- a/lib/ai/providers/openai-image.ts +++ b/lib/ai/providers/openai-image.ts @@ -28,6 +28,7 @@ const DEFAULT_TIMEOUT_MS = 300000; const OPENAI_IMAGE_MIN_TOTAL_PIXELS = 3_686_400; const OPENAI_IMAGE_DIMENSION_MULTIPLE = 16; const BLTCY_HOST_REGEX = /(^|\.)bltcy\.ai$/i; +const COMFLY_HOST_REGEX = /(^|\.)ai\.comfly\.org$/i; const SUPPORTED_ASPECT_RATIOS = [ "1:1", "1:4", @@ -70,6 +71,15 @@ function shouldUseBltcyImageCompatParams(baseURL: string) { return isBltcyBaseURL(baseURL); } +function isComflyBaseURL(baseURL: string) { + try { + const parsed = new URL(normalizeBaseURL(baseURL)); + return COMFLY_HOST_REGEX.test(parsed.hostname); + } catch { + return false; + } +} + function normalizeImageSizeLabel(imageSize?: string): "1K" | "2K" | "4K" { const normalized = String(imageSize || "").trim().toUpperCase(); if (normalized === "1K" || normalized === "2K" || normalized === "4K") return normalized; @@ -180,6 +190,32 @@ function resolveOpenAISize(aspectRatio: string, imageSize: "1K" | "2K" | "4K") { return `${width}x${height}`; } +function resolveLongestEdgeSize(aspectRatio: string, imageSize: "1K" | "2K" | "4K") { + const edgeMap: Record = { + "1K": 1024, + "2K": 2048, + "4K": 4096, + }; + const ratioPair = parseAspectRatioPair(aspectRatio) || { width: 1, height: 1 }; + const longestEdge = edgeMap[imageSize] || edgeMap["2K"]; + const scale = longestEdge / Math.max(ratioPair.width, ratioPair.height); + const width = ceilToMultiple(ratioPair.width * scale, OPENAI_IMAGE_DIMENSION_MULTIPLE); + const height = ceilToMultiple(ratioPair.height * scale, OPENAI_IMAGE_DIMENSION_MULTIPLE); + + return `${width}x${height}`; +} + +function resolveProviderImageSize( + baseURL: string, + aspectRatio: string, + imageSize: "1K" | "2K" | "4K" +) { + if (isComflyBaseURL(baseURL)) { + return resolveLongestEdgeSize(aspectRatio, imageSize); + } + return resolveOpenAISize(aspectRatio, imageSize); +} + function normalizeQuality(value?: string) { const normalized = String(value || "").trim().toLowerCase(); if (normalized === "low" || normalized === "medium" || normalized === "high") { @@ -540,7 +576,7 @@ export function createOpenAIImage({ apiKey, baseURL }: OpenAIImageConfig) { const imageSize = normalizeImageSizeLabel(options.imageParams?.imageSize); const size = options.imageParams?.size || - (aspectRatio ? resolveOpenAISize(aspectRatio, imageSize) : undefined); + (aspectRatio ? resolveProviderImageSize(baseURL, aspectRatio, imageSize) : undefined); const quality = normalizeQuality(options.imageParams?.quality); const background = normalizeBackground(options.imageParams?.background); const outputFormat = normalizeOutputFormat(options.imageParams?.outputFormat); @@ -551,6 +587,7 @@ export function createOpenAIImage({ apiKey, baseURL }: OpenAIImageConfig) { aspectRatio: aspectRatio || "auto", promptAspectRatio: promptAspectRatio || undefined, imageSize, + resolutionMode: isComflyBaseURL(baseURL) ? "longest-edge" : "pixel-area", useBltcyImageCompatParams: shouldUseBltcyImageCompatParams(baseURL), imageCount: imageUrls.length, baseURL: normalizeBaseURL(baseURL),