File size: 6,437 Bytes
f703ba6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | import type {
GenerateImageInput,
ImageBackground,
ImageInputFidelity,
ImageModeration,
ImageOutputFormat,
ImageQuality,
ImageRatio
} from './types'
export const DEFAULT_MODEL = 'gpt-image-2'
export const DEFAULT_IMAGE_OUTPUT_FORMAT: ImageOutputFormat = 'png'
export const IMAGE_RATIOS: ImageRatio[] = ['1:1', '3:2', '2:3', '4:3', '3:4', '16:9', '9:16', '21:9', '9:21']
export const IMAGE_QUALITIES: GenerateImageInput['quality'][] = ['auto', 'low', 'medium', 'high']
export const IMAGE_OUTPUT_FORMATS: ImageOutputFormat[] = ['jpeg', 'png', 'webp']
export const IMAGE_BACKGROUNDS: ImageBackground[] = ['auto', 'opaque']
export const IMAGE_MODERATIONS: ImageModeration[] = ['auto', 'low']
export const IMAGE_INPUT_FIDELITIES: ImageInputFidelity[] = ['low', 'high']
export const IMAGE_QUALITY_LABELS: Record<ImageQuality, string> = {
auto: 'θͺε¨',
low: 'δ½',
medium: 'δΈ',
high: 'ι«'
}
export const IMAGE_OUTPUT_FORMAT_LABELS: Record<ImageOutputFormat, string> = {
png: 'PNG',
jpeg: 'JPEG',
webp: 'WebP'
}
export const IMAGE_BACKGROUND_LABELS: Record<ImageBackground, string> = {
auto: 'θͺε¨',
opaque: 'δΈιζ'
}
export const IMAGE_MODERATION_LABELS: Record<ImageModeration, string> = {
auto: 'θͺε¨',
low: 'δ½'
}
export const IMAGE_INPUT_FIDELITY_LABELS: Record<ImageInputFidelity, string> = {
low: 'δ½',
high: 'ι«'
}
export type ImageSizeOption = {
value: string
label: string
}
const imageSizeOptionsByRatio: Record<ImageRatio, ImageSizeOption[]> = {
'1:1': [
{ value: '1024x1024', label: 'ζ ε 1024Γ1024' },
{ value: '1536x1536', label: 'ι«ζΈ
1536Γ1536' },
{ value: '2048x2048', label: '2K 2048Γ2048' },
{ value: '2560x2560', label: 'ι«ε 2560Γ2560' }
],
'3:2': [
{ value: '1536x1024', label: 'ζ ε 1536Γ1024' },
{ value: '3072x2048', label: 'ι«ε 3072Γ2048' }
],
'2:3': [
{ value: '1024x1536', label: 'ζ ε 1024Γ1536' },
{ value: '2048x3072', label: 'ι«ε 2048Γ3072' }
],
'4:3': [
{ value: '1024x768', label: 'ζ ε 1024Γ768' },
{ value: '1536x1152', label: 'ι«ζΈ
1536Γ1152' },
{ value: '2048x1536', label: 'ι«ε 2048Γ1536' },
{ value: '3072x2304', label: 'ι«ε 3072Γ2304' }
],
'3:4': [
{ value: '768x1024', label: 'ζ ε 768Γ1024' },
{ value: '1152x1536', label: 'ι«ζΈ
1152Γ1536' },
{ value: '1536x2048', label: 'ι«ε 1536Γ2048' },
{ value: '2304x3072', label: 'ι«ε 2304Γ3072' }
],
'16:9': [
{ value: '1280x720', label: '720P 1280Γ720' },
{ value: '1536x864', label: '1536Γ864' },
{ value: '1792x1008', label: 'ζ ε 1792Γ1008' },
{ value: '2048x1152', label: '2K 2048Γ1152' },
{ value: '2560x1440', label: '2K 2560Γ1440' },
{ value: '3072x1728', label: 'ι«ε 3072Γ1728' },
{ value: '3840x2160', label: '4K 3840Γ2160' }
],
'9:16': [
{ value: '720x1280', label: '720P 720Γ1280' },
{ value: '864x1536', label: '864Γ1536' },
{ value: '1008x1792', label: 'ζ ε 1008Γ1792' },
{ value: '1152x2048', label: '2K 1152Γ2048' },
{ value: '1440x2560', label: '2K 1440Γ2560' },
{ value: '1728x3072', label: 'ι«ε 1728Γ3072' },
{ value: '2160x3840', label: '4K 2160Γ3840' }
],
'21:9': [
{ value: '1344x576', label: 'ζ ε 1344Γ576' },
{ value: '1792x768', label: 'ι«ε 1792Γ768' },
{ value: '2240x960', label: 'ι«ε 2240Γ960' },
{ value: '2688x1152', label: 'ι«ε 2688Γ1152' },
{ value: '3136x1344', label: 'ι«ε 3136Γ1344' },
{ value: '3584x1536', label: 'ι«ε 3584Γ1536' }
],
'9:21': [
{ value: '576x1344', label: 'ζ ε 576Γ1344' },
{ value: '768x1792', label: 'ι«ε 768Γ1792' },
{ value: '960x2240', label: 'ι«ε 960Γ2240' },
{ value: '1152x2688', label: 'ι«ε 1152Γ2688' },
{ value: '1344x3136', label: 'ι«ε 1344Γ3136' },
{ value: '1536x3584', label: 'ι«ε 1536Γ3584' }
]
}
export function getImageSizeOptions(ratio: ImageRatio): ImageSizeOption[] {
return imageSizeOptionsByRatio[ratio]
}
export function getDefaultImageSize(ratio: ImageRatio): string {
return getImageSizeOptions(ratio).find((option) => option.label.startsWith('ζ ε'))?.value
|| getImageSizeOptions(ratio)[0]?.value
|| ratioToSize(ratio)
}
export function isImageSizeCompatible(ratio: ImageRatio, size: string): boolean {
return getImageSizeOptions(ratio).some((option) => option.value === size)
}
const ratioSizeMap: Record<ImageRatio, string> = {
'1:1': '1024x1024',
'3:2': '1536x1024',
'2:3': '1024x1536',
'4:3': '1024x768',
'3:4': '768x1024',
'16:9': '1792x1008',
'9:16': '1008x1792',
'21:9': '1344x576',
'9:21': '576x1344'
}
export function ratioToSize(ratio: ImageRatio): string {
return ratioSizeMap[ratio]
}
export function formatImageQuality(quality: ImageQuality): string {
return IMAGE_QUALITY_LABELS[quality] || quality
}
export function formatImageSize(size: string): string {
return size.replace('x', 'Γ')
}
function toOptionalNumber(value: number | null | undefined): number | undefined {
return typeof value === 'number' && Number.isFinite(value) ? value : undefined
}
export function buildImageEndpoint(baseURL: string): string {
const normalized = baseURL.trim().replace(/\/+$/, '')
return `${normalized}/v1/images/generations`
}
export function buildImageEditEndpoint(baseURL: string): string {
const normalized = baseURL.trim().replace(/\/+$/, '')
return `${normalized}/v1/images/edits`
}
export function supportsImageInputFidelity(model: string): boolean {
return !model.trim().toLowerCase().startsWith('gpt-image-2')
}
export function buildImageRequestBody(input: GenerateImageInput): Record<string, unknown> {
const body: Record<string, unknown> = {
prompt: input.prompt.trim(),
model: input.model.trim() || DEFAULT_MODEL,
size: input.size.trim() || getDefaultImageSize(input.ratio),
quality: input.quality,
n: Math.min(10, Math.max(1, input.n || 1))
}
if (input.outputFormat) body.output_format = input.outputFormat
if (toOptionalNumber(input.outputCompression) != null) body.output_compression = input.outputCompression
if (input.background) body.background = input.background
if (input.moderation) body.moderation = input.moderation
if (input.stream) body.stream = true
if (toOptionalNumber(input.partialImages) != null) body.partial_images = input.partialImages
return body
}
|