feat: add aspect ratio
This commit is contained in:
parent
620a2a9caa
commit
585b446ca5
|
|
@ -16,6 +16,20 @@ const VALIDATION_RULES = {
|
|||
},
|
||||
};
|
||||
|
||||
// Valid aspect ratios supported by Gemini SDK
|
||||
const VALID_ASPECT_RATIOS = [
|
||||
"1:1", // Square (1024x1024)
|
||||
"2:3", // Portrait (832x1248)
|
||||
"3:2", // Landscape (1248x832)
|
||||
"3:4", // Portrait (864x1184)
|
||||
"4:3", // Landscape (1184x864)
|
||||
"4:5", // Portrait (896x1152)
|
||||
"5:4", // Landscape (1152x896)
|
||||
"9:16", // Vertical (768x1344)
|
||||
"16:9", // Widescreen (1344x768)
|
||||
"21:9", // Ultrawide (1536x672)
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Validate the text-to-image JSON request
|
||||
*/
|
||||
|
|
@ -25,7 +39,7 @@ export const validateTextToImageRequest = (
|
|||
next: NextFunction,
|
||||
): void | Response => {
|
||||
const timestamp = new Date().toISOString();
|
||||
const { prompt, filename, autoEnhance, enhancementOptions } = req.body;
|
||||
const { prompt, filename, aspectRatio, autoEnhance, enhancementOptions } = req.body;
|
||||
const errors: string[] = [];
|
||||
|
||||
console.log(
|
||||
|
|
@ -73,6 +87,17 @@ export const validateTextToImageRequest = (
|
|||
);
|
||||
}
|
||||
|
||||
// Validate aspectRatio (optional, defaults to "1:1")
|
||||
if (aspectRatio !== undefined) {
|
||||
if (typeof aspectRatio !== "string") {
|
||||
errors.push("aspectRatio must be a string");
|
||||
} else if (!VALID_ASPECT_RATIOS.includes(aspectRatio as any)) {
|
||||
errors.push(
|
||||
`Invalid aspectRatio. Must be one of: ${VALID_ASPECT_RATIOS.join(", ")}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate autoEnhance (optional boolean)
|
||||
if (autoEnhance !== undefined && typeof autoEnhance !== "boolean") {
|
||||
errors.push("autoEnhance must be a boolean");
|
||||
|
|
@ -111,11 +136,11 @@ export const validateTextToImageRequest = (
|
|||
|
||||
if (
|
||||
aspectRatio !== undefined &&
|
||||
!["square", "portrait", "landscape", "wide", "ultrawide"].includes(
|
||||
aspectRatio,
|
||||
)
|
||||
!VALID_ASPECT_RATIOS.includes(aspectRatio as any)
|
||||
) {
|
||||
errors.push("Invalid aspectRatio in enhancementOptions");
|
||||
errors.push(
|
||||
`Invalid aspectRatio. Must be one of: ${VALID_ASPECT_RATIOS.join(", ")}`
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ generateRouter.post(
|
|||
|
||||
const timestamp = new Date().toISOString();
|
||||
const requestId = req.requestId;
|
||||
const { prompt, filename } = req.body;
|
||||
const { prompt, filename, aspectRatio } = req.body;
|
||||
const files = (req.files as Express.Multer.File[]) || [];
|
||||
|
||||
// Extract org/project slugs from validated API key
|
||||
|
|
@ -108,6 +108,7 @@ generateRouter.post(
|
|||
const result = await imageGenService.generateImage({
|
||||
prompt,
|
||||
filename,
|
||||
...(aspectRatio && { aspectRatio }),
|
||||
orgId,
|
||||
projectId,
|
||||
...(referenceImages && { referenceImages }),
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ textToImageRouter.post(
|
|||
|
||||
const timestamp = new Date().toISOString();
|
||||
const requestId = req.requestId;
|
||||
const { prompt, filename } = req.body;
|
||||
const { prompt, filename, aspectRatio } = req.body;
|
||||
|
||||
// Extract org/project slugs from validated API key
|
||||
const orgId = req.apiKey?.organizationSlug || undefined;
|
||||
|
|
@ -73,6 +73,7 @@ textToImageRouter.post(
|
|||
const result = await imageGenService.generateImage({
|
||||
prompt,
|
||||
filename,
|
||||
...(aspectRatio && { aspectRatio }),
|
||||
orgId,
|
||||
projectId,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,12 +29,13 @@ export class ImageGenService {
|
|||
async generateImage(
|
||||
options: ImageGenerationOptions,
|
||||
): Promise<ImageGenerationResult> {
|
||||
const { prompt, filename, referenceImages, orgId, projectId } = options;
|
||||
const { prompt, filename, referenceImages, aspectRatio, orgId, projectId } = options;
|
||||
|
||||
// Use default values if not provided
|
||||
const finalOrgId = orgId || process.env["DEFAULT_ORG_ID"] || "default";
|
||||
const finalProjectId =
|
||||
projectId || process.env["DEFAULT_PROJECT_ID"] || "main";
|
||||
const finalAspectRatio = aspectRatio || "1:1"; // Default to square
|
||||
|
||||
// Step 1: Generate image from Gemini AI
|
||||
let generatedData: GeneratedImageData;
|
||||
|
|
@ -43,6 +44,7 @@ export class ImageGenService {
|
|||
const aiResult = await this.generateImageWithAI(
|
||||
prompt,
|
||||
referenceImages,
|
||||
finalAspectRatio,
|
||||
finalOrgId,
|
||||
finalProjectId,
|
||||
);
|
||||
|
|
@ -121,6 +123,7 @@ export class ImageGenService {
|
|||
private async generateImageWithAI(
|
||||
prompt: string,
|
||||
referenceImages: ReferenceImage[] | undefined,
|
||||
aspectRatio: string,
|
||||
orgId: string,
|
||||
projectId: string,
|
||||
): Promise<{
|
||||
|
|
@ -155,7 +158,12 @@ export class ImageGenService {
|
|||
},
|
||||
];
|
||||
|
||||
const config = { responseModalities: ["IMAGE", "TEXT"] };
|
||||
const config = {
|
||||
responseModalities: ["IMAGE", "TEXT"],
|
||||
imageConfig: {
|
||||
aspectRatio,
|
||||
},
|
||||
};
|
||||
|
||||
// Capture Gemini SDK parameters for debugging
|
||||
const geminiParams: GeminiParams = {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ export interface ImageGenerationOptions {
|
|||
prompt: string;
|
||||
filename: string;
|
||||
referenceImages?: ReferenceImage[];
|
||||
aspectRatio?: string;
|
||||
orgId?: string;
|
||||
projectId?: string;
|
||||
userId?: string;
|
||||
|
|
@ -74,6 +75,9 @@ export interface GeminiParams {
|
|||
model: string;
|
||||
config: {
|
||||
responseModalities: string[];
|
||||
imageConfig?: {
|
||||
aspectRatio?: string;
|
||||
};
|
||||
};
|
||||
contentsStructure: {
|
||||
role: string;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export default function DemoTTIPage() {
|
|||
const [generationError, setGenerationError] = useState('');
|
||||
|
||||
// Enhancement Options State
|
||||
const [aspectRatio, setAspectRatio] = useState('');
|
||||
const [aspectRatio, setAspectRatio] = useState('1:1');
|
||||
const [imageStyle, setImageStyle] = useState('');
|
||||
const [advancedOptions, setAdvancedOptions] = useState<AdvancedOptionsData>({});
|
||||
const [showAdvancedModal, setShowAdvancedModal] = useState(false);
|
||||
|
|
@ -241,6 +241,7 @@ export default function DemoTTIPage() {
|
|||
body: JSON.stringify({
|
||||
prompt: prompt.trim(),
|
||||
filename: `demo_${resultId}_left`,
|
||||
aspectRatio,
|
||||
}),
|
||||
}),
|
||||
fetch(`${API_BASE_URL}/api/text-to-image`, {
|
||||
|
|
@ -252,6 +253,7 @@ export default function DemoTTIPage() {
|
|||
body: JSON.stringify({
|
||||
prompt: prompt.trim(),
|
||||
filename: `demo_${resultId}_right`,
|
||||
aspectRatio,
|
||||
autoEnhance: true,
|
||||
...(hasEnhancementOptions && {
|
||||
enhancementOptions: rightEnhancementOptions
|
||||
|
|
@ -488,12 +490,12 @@ export default function DemoTTIPage() {
|
|||
disabled={!apiKeyValidated || generating}
|
||||
className="w-full px-3 py-2 text-sm bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<option value="">Auto</option>
|
||||
<option value="square">Square (1:1)</option>
|
||||
<option value="portrait">Portrait (3:4)</option>
|
||||
<option value="landscape">Landscape (4:3)</option>
|
||||
<option value="wide">Wide (16:9)</option>
|
||||
<option value="ultrawide">Ultrawide (21:9)</option>
|
||||
<option value="1:1">Square (1:1)</option>
|
||||
<option value="3:4">Portrait (3:4)</option>
|
||||
<option value="4:3">Landscape (4:3)</option>
|
||||
<option value="9:16">Vertical (9:16)</option>
|
||||
<option value="16:9">Widescreen (16:9)</option>
|
||||
<option value="21:9">Ultrawide (21:9)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue