feat: update API

This commit is contained in:
Oleg Proskurin 2025-10-07 22:28:27 +07:00
parent 63aa812f5e
commit 847145c385
5 changed files with 93 additions and 157 deletions

View File

@ -55,6 +55,20 @@ export const validateTextToImageRequest = (
}); });
} }
// Set defaults before validation
// Default autoEnhance to true if not explicitly set
if (req.body.autoEnhance === undefined) {
req.body.autoEnhance = true;
}
// Default template to "photorealistic" in enhancementOptions
if (req.body.enhancementOptions && !req.body.enhancementOptions.template) {
req.body.enhancementOptions.template = "photorealistic";
} else if (!req.body.enhancementOptions && req.body.autoEnhance !== false) {
// If autoEnhance is true (default) and no enhancementOptions, create it with default template
req.body.enhancementOptions = { template: "photorealistic" };
}
// Validate prompt // Validate prompt
if (!prompt) { if (!prompt) {
errors.push("Prompt is required"); errors.push("Prompt is required");
@ -111,17 +125,11 @@ export const validateTextToImageRequest = (
) { ) {
errors.push("enhancementOptions must be an object"); errors.push("enhancementOptions must be an object");
} else { } else {
const { const { template } = enhancementOptions;
imageStyle,
aspectRatio,
mood,
lighting,
cameraAngle,
negativePrompts,
} = enhancementOptions;
// Validate template parameter
if ( if (
imageStyle !== undefined && template !== undefined &&
![ ![
"photorealistic", "photorealistic",
"illustration", "illustration",
@ -129,55 +137,13 @@ export const validateTextToImageRequest = (
"sticker", "sticker",
"product", "product",
"comic", "comic",
].includes(imageStyle) "general",
) { ].includes(template)
errors.push("Invalid imageStyle in enhancementOptions");
}
if (
aspectRatio !== undefined &&
!VALID_ASPECT_RATIOS.includes(aspectRatio as any)
) { ) {
errors.push( errors.push(
`Invalid aspectRatio. Must be one of: ${VALID_ASPECT_RATIOS.join(", ")}` "Invalid template in enhancementOptions. Must be one of: photorealistic, illustration, minimalist, sticker, product, comic, general",
); );
} }
if (
mood !== undefined &&
(typeof mood !== "string" || mood.length > 100)
) {
errors.push("mood must be a string with max 100 characters");
}
if (
lighting !== undefined &&
(typeof lighting !== "string" || lighting.length > 100)
) {
errors.push("lighting must be a string with max 100 characters");
}
if (
cameraAngle !== undefined &&
(typeof cameraAngle !== "string" || cameraAngle.length > 100)
) {
errors.push("cameraAngle must be a string with max 100 characters");
}
if (negativePrompts !== undefined) {
if (!Array.isArray(negativePrompts) || negativePrompts.length > 10) {
errors.push("negativePrompts must be an array with max 10 items");
} else {
for (const item of negativePrompts) {
if (typeof item !== "string" || item.length > 100) {
errors.push(
"Each negative prompt must be a string with max 100 characters",
);
break;
}
}
}
}
} }
} }

View File

@ -24,9 +24,12 @@ export const autoEnhancePrompt = async (
const requestId = req.requestId; const requestId = req.requestId;
const { prompt, autoEnhance, enhancementOptions } = req.body; const { prompt, autoEnhance, enhancementOptions } = req.body;
if (!autoEnhance) { // Default autoEnhance to true if not explicitly set to false
const shouldEnhance = autoEnhance !== false;
if (!shouldEnhance) {
console.log( console.log(
`[${timestamp}] [${requestId}] Auto-enhancement disabled, skipping`, `[${timestamp}] [${requestId}] Auto-enhancement explicitly disabled, skipping`,
); );
return next(); return next();
} }

View File

@ -1,19 +1,14 @@
import { GoogleGenAI } from "@google/genai"; import { GoogleGenAI } from "@google/genai";
export interface PromptEnhancementOptions { export interface PromptEnhancementOptions {
imageStyle?: template?:
| "photorealistic" | "photorealistic"
| "illustration" | "illustration"
| "minimalist" | "minimalist"
| "sticker" | "sticker"
| "product" | "product"
| "comic"; | "comic"
aspectRatio?: "square" | "portrait" | "landscape" | "wide" | "ultrawide"; | "general";
mood?: string;
lighting?: string;
cameraAngle?: string;
outputFormat?: "text" | "markdown" | "detailed";
negativePrompts?: string[];
} }
export interface PromptEnhancementResult { export interface PromptEnhancementResult {
@ -47,13 +42,20 @@ export class PromptEnhancementService {
): Promise<PromptEnhancementResult> { ): Promise<PromptEnhancementResult> {
const timestamp = new Date().toISOString(); const timestamp = new Date().toISOString();
// Default template to "photorealistic" if not specified
const finalOptions = {
...options,
template: options.template || "photorealistic",
};
console.log( console.log(
`[${timestamp}] Starting prompt enhancement for: "${rawPrompt.substring(0, 50)}..."`, `[${timestamp}] Starting prompt enhancement for: "${rawPrompt.substring(0, 50)}..."`,
); );
console.log(`[${timestamp}] Using template: ${finalOptions.template}`);
try { try {
const systemPrompt = this.buildSystemPrompt(options); const systemPrompt = this.buildSystemPrompt(finalOptions);
const userPrompt = this.buildUserPrompt(rawPrompt, options); const userPrompt = this.buildUserPrompt(rawPrompt, finalOptions);
console.log( console.log(
`[${timestamp}] Making API request to Gemini 2.5 Flash for prompt enhancement...`, `[${timestamp}] Making API request to Gemini 2.5 Flash for prompt enhancement...`,
@ -83,7 +85,7 @@ export class PromptEnhancementService {
const result = this.parseEnhancedResponse( const result = this.parseEnhancedResponse(
enhancedText, enhancedText,
rawPrompt, rawPrompt,
options, finalOptions,
); );
const enhancementResult: PromptEnhancementResult = { const enhancementResult: PromptEnhancementResult = {
@ -93,14 +95,9 @@ export class PromptEnhancementService {
...(result.detectedLanguage && { ...(result.detectedLanguage && {
detectedLanguage: result.detectedLanguage, detectedLanguage: result.detectedLanguage,
}), }),
...(result.appliedTemplate && { appliedTemplate: finalOptions.template,
appliedTemplate: result.appliedTemplate,
}),
metadata: { metadata: {
...(options.imageStyle && { style: options.imageStyle }), style: finalOptions.template,
...(!options.imageStyle &&
result.detectedStyle && { style: result.detectedStyle }),
...(options.aspectRatio && { aspectRatio: options.aspectRatio }),
enhancements: result.enhancements, enhancements: result.enhancements,
}, },
}; };
@ -123,14 +120,10 @@ export class PromptEnhancementService {
} }
private buildSystemPrompt(options: PromptEnhancementOptions): string { private buildSystemPrompt(options: PromptEnhancementOptions): string {
const { const { template } = options;
imageStyle,
aspectRatio, // Default to photorealistic
mood, const selectedTemplate = template || "photorealistic";
lighting,
cameraAngle,
negativePrompts,
} = options;
return `You are an expert AI prompt engineer specializing in transforming rough, unstructured prompts into professional, detailed prompts for the Gemini Flash Image Generation model. Your goal is to follow these principles: return `You are an expert AI prompt engineer specializing in transforming rough, unstructured prompts into professional, detailed prompts for the Gemini Flash Image Generation model. Your goal is to follow these principles:
@ -150,14 +143,10 @@ STYLE TEMPLATES:
- Sticker: Emphasize style (kawaii, bold outlines, clean design), transparent background - Sticker: Emphasize style (kawaii, bold outlines, clean design), transparent background
- Product: Studio lighting setup, commercial photography terms, surfaces, angles - Product: Studio lighting setup, commercial photography terms, surfaces, angles
- Comic: Panel style, art technique, mood, dialogue/caption integration - Comic: Panel style, art technique, mood, dialogue/caption integration
- General: Balanced approach with clear descriptions and artistic detail
TECHNICAL REQUIREMENTS: TECHNICAL REQUIREMENTS:
${imageStyle ? `- Target Style: ${imageStyle}` : "- Auto-detect and apply appropriate style"} - Target Template: ${selectedTemplate}
${aspectRatio ? `- Aspect Ratio: ${aspectRatio}` : "- Use square format unless context suggests otherwise"}
${mood ? `- Mood: ${mood}` : ""}
${lighting ? `- Lighting: ${lighting}` : ""}
${cameraAngle ? `- Camera Angle: ${cameraAngle}` : ""}
${negativePrompts && negativePrompts.length > 0 ? `- Avoid: ${negativePrompts.join(", ")}` : ""}
RESPONSE FORMAT: RESPONSE FORMAT:
Provide only the enhanced prompt as a single, cohesive paragraph. Do not include explanations, metadata, or multiple options. The response should be ready to use directly for image generation. Provide only the enhanced prompt as a single, cohesive paragraph. Do not include explanations, metadata, or multiple options. The response should be ready to use directly for image generation.
@ -171,13 +160,8 @@ Remember: More detail equals more control. Transform vague concepts into vivid,
): string { ): string {
let prompt = `Transform this prompt into a professional image generation prompt: "${rawPrompt}"`; let prompt = `Transform this prompt into a professional image generation prompt: "${rawPrompt}"`;
if (options.imageStyle) { const selectedTemplate = options.template || "photorealistic";
prompt += `\n\nTarget style: ${options.imageStyle}`; prompt += `\n\nTarget template/style: ${selectedTemplate}`;
}
if (options.aspectRatio) {
prompt += `\n\nAspect ratio: ${options.aspectRatio}`;
}
return prompt; return prompt;
} }
@ -228,26 +212,8 @@ Remember: More detail equals more control. Transform vague concepts into vivid,
// Try to detect the language of original prompt (simple heuristic) // Try to detect the language of original prompt (simple heuristic)
const detectedLanguage = this.detectLanguage(originalPrompt); const detectedLanguage = this.detectLanguage(originalPrompt);
// Detect applied template based on content // Use the explicit template if provided
let appliedTemplate = "general"; const appliedTemplate = options.template || "photorealistic";
if (options.imageStyle) {
appliedTemplate = options.imageStyle;
} else if (
enhancedPrompt.includes("photorealistic") ||
enhancedPrompt.includes("camera")
) {
appliedTemplate = "photorealistic";
} else if (
enhancedPrompt.includes("sticker") ||
enhancedPrompt.includes("kawaii")
) {
appliedTemplate = "sticker";
} else if (
enhancedPrompt.includes("minimalist") ||
enhancedPrompt.includes("negative space")
) {
appliedTemplate = "minimalist";
}
return { return {
enhancedPrompt, enhancedPrompt,

View File

@ -9,20 +9,17 @@ export interface GenerateImageRequest {
export interface TextToImageRequest { export interface TextToImageRequest {
prompt: string; prompt: string;
filename: string; filename: string;
autoEnhance?: boolean; aspectRatio?: string; // Gemini aspect ratio format (e.g., "1:1", "16:9", "3:2")
autoEnhance?: boolean; // Defaults to true
enhancementOptions?: { enhancementOptions?: {
imageStyle?: template?:
| "photorealistic" | "photorealistic"
| "illustration" | "illustration"
| "minimalist" | "minimalist"
| "sticker" | "sticker"
| "product" | "product"
| "comic"; | "comic"
aspectRatio?: "square" | "portrait" | "landscape" | "wide" | "ultrawide"; | "general"; // Defaults to "photorealistic"
mood?: string;
lighting?: string;
cameraAngle?: string;
negativePrompts?: string[];
}; };
} }
@ -119,19 +116,14 @@ export interface LogContext {
export interface PromptEnhancementRequest { export interface PromptEnhancementRequest {
prompt: string; prompt: string;
options?: { options?: {
imageStyle?: template?:
| "photorealistic" | "photorealistic"
| "illustration" | "illustration"
| "minimalist" | "minimalist"
| "sticker" | "sticker"
| "product" | "product"
| "comic"; | "comic"
aspectRatio?: "square" | "portrait" | "landscape" | "wide" | "ultrawide"; | "general"; // Defaults to "photorealistic"
mood?: string;
lighting?: string;
cameraAngle?: string;
outputFormat?: "text" | "markdown" | "detailed";
negativePrompts?: string[];
}; };
} }
@ -151,20 +143,17 @@ export interface PromptEnhancementResponse {
// Enhanced Generate Request (with auto-enhancement option) // Enhanced Generate Request (with auto-enhancement option)
export interface EnhancedGenerateImageRequest extends GenerateImageRequest { export interface EnhancedGenerateImageRequest extends GenerateImageRequest {
autoEnhance?: boolean; aspectRatio?: string; // Gemini aspect ratio format (e.g., "1:1", "16:9", "3:2")
autoEnhance?: boolean; // Defaults to true
enhancementOptions?: { enhancementOptions?: {
imageStyle?: template?:
| "photorealistic" | "photorealistic"
| "illustration" | "illustration"
| "minimalist" | "minimalist"
| "sticker" | "sticker"
| "product" | "product"
| "comic"; | "comic"
aspectRatio?: "square" | "portrait" | "landscape" | "wide" | "ultrawide"; | "general"; // Defaults to "photorealistic"
mood?: string;
lighting?: string;
cameraAngle?: string;
negativePrompts?: string[];
}; };
} }

View File

@ -273,14 +273,9 @@ Generate images from text prompts with optional reference images.
**Enhancement Options:** **Enhancement Options:**
| Field | Type | Options | Description | | Field | Type | Options | Default | Description |
|-------|------|---------|-------------| |-------|------|---------|---------|-------------|
| `imageStyle` | string | `photorealistic`, `illustration`, `minimalist`, `sticker`, `product`, `comic` | Visual style | | `template` | string | `photorealistic`, `illustration`, `minimalist`, `sticker`, `product`, `comic`, `general` | `photorealistic` | Prompt engineering template to apply |
| `aspectRatio` | string | `square`, `portrait`, `landscape`, `wide`, `ultrawide` | Image proportions |
| `mood` | string | - | Mood description (max 100 chars) |
| `lighting` | string | - | Lighting description (max 100 chars) |
| `cameraAngle` | string | - | Camera angle description (max 100 chars) |
| `negativePrompts` | string[] | - | What to avoid (max 10 items, 100 chars each) |
**Example Request:** **Example Request:**
```bash ```bash
@ -342,10 +337,10 @@ Generate images from text prompts only using JSON payload. Simplified endpoint f
{ {
"prompt": "A beautiful sunset over mountains", "prompt": "A beautiful sunset over mountains",
"filename": "sunset_image", "filename": "sunset_image",
"aspectRatio": "16:9",
"autoEnhance": true, "autoEnhance": true,
"enhancementOptions": { "enhancementOptions": {
"imageStyle": "photorealistic", "template": "photorealistic",
"aspectRatio": "landscape",
"mood": "peaceful", "mood": "peaceful",
"lighting": "golden hour" "lighting": "golden hour"
} }
@ -354,12 +349,19 @@ Generate images from text prompts only using JSON payload. Simplified endpoint f
**Parameters:** **Parameters:**
| Field | Type | Required | Description | | Field | Type | Required | Default | Description |
|-------|------|----------|-------------| |-------|------|----------|---------|-------------|
| `prompt` | string | Yes | Text description of the image to generate (3-2000 chars) | | `prompt` | string | Yes | - | Text description of the image to generate (3-2000 chars) |
| `filename` | string | Yes | Desired filename for the generated image (alphanumeric, underscore, hyphen only) | | `filename` | string | Yes | - | Desired filename for the generated image (alphanumeric, underscore, hyphen only) |
| `autoEnhance` | boolean | No | Enable automatic prompt enhancement | | `aspectRatio` | string | No | `"1:1"` | Image aspect ratio (`"1:1"`, `"2:3"`, `"3:2"`, `"3:4"`, `"4:3"`, `"4:5"`, `"5:4"`, `"9:16"`, `"16:9"`, `"21:9"`) |
| `enhancementOptions` | object | No | Enhancement configuration options (same as /api/generate) | | `autoEnhance` | boolean | No | `true` | Enable automatic prompt enhancement (set to `false` to use prompt as-is) |
| `enhancementOptions` | object | No | - | Enhancement configuration options |
**Enhancement Options:**
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `template` | string | No | `"photorealistic"` | Prompt engineering template: `"photorealistic"`, `"illustration"`, `"minimalist"`, `"sticker"`, `"product"`, `"comic"`, `"general"` |
**Example Request:** **Example Request:**
```bash ```bash
@ -369,10 +371,10 @@ curl -X POST http://localhost:3000/api/text-to-image \
-d '{ -d '{
"prompt": "A beautiful sunset over mountains with golden clouds", "prompt": "A beautiful sunset over mountains with golden clouds",
"filename": "test_sunset", "filename": "test_sunset",
"aspectRatio": "16:9",
"autoEnhance": true, "autoEnhance": true,
"enhancementOptions": { "enhancementOptions": {
"imageStyle": "photorealistic", "template": "photorealistic"
"aspectRatio": "landscape"
} }
}' }'
``` ```
@ -413,6 +415,16 @@ curl -X POST http://localhost:3000/api/text-to-image \
- **Faster**: No multipart parsing overhead - **Faster**: No multipart parsing overhead
- **Simpler testing**: Easy to use with curl or API clients - **Simpler testing**: Easy to use with curl or API clients
- **Same features**: Supports all enhancement options - **Same features**: Supports all enhancement options
- **Auto-enhance by default**: `autoEnhance` defaults to `true`, set explicitly to `false` to use prompt as-is
**Template Descriptions:**
- `photorealistic`: Photography-focused with camera angles, lens types, lighting, and fine details
- `illustration`: Art style specifications with line work, color palette, and shading techniques
- `minimalist`: Emphasis on negative space, simple composition, and subtle elements
- `sticker`: Bold outlines, kawaii style, clean design, transparent background style
- `product`: Studio lighting setups, commercial photography terms, surfaces, and angles
- `comic`: Panel style, art technique, mood, and dialogue/caption integration
- `general`: Balanced approach with clear descriptions and artistic detail
--- ---